From 003be1952262ccc8eed917cdc760ee860fdaf31f Mon Sep 17 00:00:00 2001 From: bing Date: Fri, 3 Apr 2026 11:32:07 +0800 Subject: [PATCH] first commit --- .gitignore | 122 + CMakeLists.txt | 676 + README.md | 92 + XdlOrm.cmake | 384 + doc/doxygen/html/.gitignore | 7 + doc/doxygen/index.html | 7 + doc/doxygen/qxorm.doxygen | 1761 +++ doc/index.html | 18 + doc/php/add_download.php | 31 + doc/php/add_download.php5 | 39 + .../resource/qxee_all_views.jpg | Bin 0 -> 179658 bytes .../resource/qxee_cpp_preview.png | Bin 0 -> 66864 bytes doc/qxentityeditor/resource/qxee_database.png | Bin 0 -> 42253 bytes .../resource/qxee_entity_clone.png | Bin 0 -> 139676 bytes .../resource/qxee_entity_colors.png | Bin 0 -> 5946 bytes .../resource/qxee_entity_delete.png | Bin 0 -> 76082 bytes .../resource/qxee_entity_modify.png | Bin 0 -> 166172 bytes .../resource/qxee_entity_new.png | Bin 0 -> 206328 bytes .../resource/qxee_entity_settings.png | Bin 0 -> 56604 bytes .../resource/qxee_enum_colors.png | Bin 0 -> 5821 bytes .../resource/qxee_enum_settings.png | Bin 0 -> 27455 bytes .../resource/qxee_export_cpp.png | Bin 0 -> 53430 bytes .../resource/qxee_export_cpp_model_view.png | Bin 0 -> 45145 bytes .../qxee_export_cpp_model_view_output.png | Bin 0 -> 12815 bytes .../resource/qxee_export_cpp_output.png | Bin 0 -> 12524 bytes .../resource/qxee_export_cpp_services.png | Bin 0 -> 45595 bytes .../qxee_export_cpp_services_output.png | Bin 0 -> 19135 bytes .../qxee_export_cpp_services_server_app.png | Bin 0 -> 15835 bytes ..._export_cpp_services_server_app_output.png | Bin 0 -> 11019 bytes .../resource/qxee_export_ddl.png | Bin 0 -> 39303 bytes .../resource/qxee_export_plugins.png | Bin 0 -> 26023 bytes .../resource/qxee_export_print.png | Bin 0 -> 8671 bytes .../qxee_export_source_control_settings.png | Bin 0 -> 17269 bytes .../resource/qxee_export_xml_json.png | Bin 0 -> 7235 bytes .../resource/qxee_global_settings.png | Bin 0 -> 11899 bytes .../resource/qxee_import_json.png | Bin 0 -> 15598 bytes .../resource/qxee_import_mysql.png | Bin 0 -> 31954 bytes .../resource/qxee_import_odbc.png | Bin 0 -> 30601 bytes .../resource/qxee_import_plugins.png | Bin 0 -> 21269 bytes .../resource/qxee_import_postgresql.png | Bin 0 -> 31816 bytes .../resource/qxee_import_source_control.png | Bin 0 -> 18791 bytes .../resource/qxee_import_sqlite.png | Bin 0 -> 24839 bytes doc/qxentityeditor/resource/qxee_js_debug.png | Bin 0 -> 102779 bytes doc/qxentityeditor/resource/qxee_license.png | Bin 0 -> 26697 bytes doc/qxentityeditor/resource/qxee_linux.png | Bin 0 -> 426940 bytes .../resource/qxee_linux_small.png | Bin 0 -> 30860 bytes doc/qxentityeditor/resource/qxee_logo.png | Bin 0 -> 122133 bytes .../resource/qxee_logo_small.png | Bin 0 -> 11630 bytes doc/qxentityeditor/resource/qxee_macosx.png | Bin 0 -> 308698 bytes .../resource/qxee_macosx_small.png | Bin 0 -> 55887 bytes .../resource/qxee_namespace_colors.png | Bin 0 -> 20213 bytes .../resource/qxee_namespace_menu.png | Bin 0 -> 24141 bytes .../resource/qxee_namespace_rename.png | Bin 0 -> 4861 bytes .../resource/qxee_naming_convention.png | Bin 0 -> 7736 bytes .../resource/qxee_organize_diagram_layout.png | Bin 0 -> 36547 bytes .../resource/qxee_plugin_scripts.png | Bin 0 -> 42142 bytes .../resource/qxee_plugins_about.png | Bin 0 -> 54615 bytes doc/qxentityeditor/resource/qxee_post_it.png | Bin 0 -> 46341 bytes .../resource/qxee_post_it_colors.png | Bin 0 -> 5925 bytes .../resource/qxee_project_list.png | Bin 0 -> 12092 bytes .../resource/qxee_project_new.png | Bin 0 -> 5628 bytes .../resource/qxee_project_settings_01.png | Bin 0 -> 11156 bytes .../resource/qxee_project_settings_02.png | Bin 0 -> 29294 bytes .../resource/qxee_property_settings.png | Bin 0 -> 31487 bytes .../qxee_property_settings_access.png | Bin 0 -> 8036 bytes .../resource/qxee_relation_settings.png | Bin 0 -> 31229 bytes .../qxee_relation_settings_access.png | Bin 0 -> 18150 bytes doc/qxentityeditor/resource/qxee_sample.png | Bin 0 -> 261415 bytes .../resource/qxee_sample_old.png | Bin 0 -> 271629 bytes .../resource/qxee_sample_small.png | Bin 0 -> 176076 bytes .../resource/qxee_sample_small_old.png | Bin 0 -> 125556 bytes .../qxee_source_control_output_directory.png | Bin 0 -> 14529 bytes doc/qxentityeditor/resource/qxee_splitter.png | Bin 0 -> 179182 bytes .../resource/qxee_tag_project_state_01.png | Bin 0 -> 7776 bytes .../resource/qxee_tag_project_state_02.png | Bin 0 -> 13755 bytes doc/qxentityeditor/resource/qxee_windows.png | Bin 0 -> 238751 bytes .../resource/qxee_windows_small.png | Bin 0 -> 34542 bytes doc/qxentityeditor/resource/qxee_zoom.png | Bin 0 -> 152074 bytes doc/qxorm_en/customer.html | 492 + doc/qxorm_en/download.html | 1254 ++ doc/qxorm_en/download_details.php | 161 + doc/qxorm_en/faq.html | 3606 +++++ doc/qxorm_en/home.html | 411 + doc/qxorm_en/link.html | 264 + doc/qxorm_en/manual.html | 12788 +++++++++++++++ doc/qxorm_en/manual_qxee.html | 5630 +++++++ doc/qxorm_en/model.html | 177 + doc/qxorm_en/quick_sample.html | 449 + doc/qxorm_en/resource/ES.png | Bin 0 -> 605 bytes doc/qxorm_en/resource/FR.png | Bin 0 -> 442 bytes doc/qxorm_en/resource/GB.png | Bin 0 -> 942 bytes doc/qxorm_en/resource/background.jpg | Bin 0 -> 124834 bytes doc/qxorm_en/resource/download.jpg | Bin 0 -> 1407 bytes doc/qxorm_en/resource/gui_qxClientServer.jpg | Bin 0 -> 192022 bytes doc/qxorm_en/resource/gui_qxClient_01.jpg | Bin 0 -> 67652 bytes doc/qxorm_en/resource/gui_qxClient_02.jpg | Bin 0 -> 51014 bytes doc/qxorm_en/resource/gui_qxServer.jpg | Bin 0 -> 60288 bytes doc/qxorm_en/resource/jquery.min.js | 5 + doc/qxorm_en/resource/license.gpl3.txt | 630 + doc/qxorm_en/resource/link_download.jpg | Bin 0 -> 2602 bytes doc/qxorm_en/resource/link_faq.jpg | Bin 0 -> 2265 bytes doc/qxorm_en/resource/link_home.jpg | Bin 0 -> 2196 bytes doc/qxorm_en/resource/link_link.jpg | Bin 0 -> 2199 bytes doc/qxorm_en/resource/link_model.jpg | Bin 0 -> 1488 bytes doc/qxorm_en/resource/link_quick_sample.jpg | Bin 0 -> 3068 bytes doc/qxorm_en/resource/link_tutorial.jpg | Bin 0 -> 2489 bytes doc/qxorm_en/resource/logo_boost.jpg | Bin 0 -> 4563 bytes doc/qxorm_en/resource/logo_cpp.gif | Bin 0 -> 1306 bytes doc/qxorm_en/resource/logo_linux.gif | Bin 0 -> 2374 bytes doc/qxorm_en/resource/logo_mac.gif | Bin 0 -> 2352 bytes doc/qxorm_en/resource/logo_qt.jpg | Bin 0 -> 2445 bytes doc/qxorm_en/resource/logo_qxorm.jpg | Bin 0 -> 8199 bytes doc/qxorm_en/resource/logo_qxorm.png | Bin 0 -> 28681 bytes doc/qxorm_en/resource/logo_qxorm_and_qxee.png | Bin 0 -> 53095 bytes .../resource/logo_qxorm_and_qxee_old.png | Bin 0 -> 47030 bytes doc/qxorm_en/resource/logo_qxorm_small.jpg | Bin 0 -> 4570 bytes doc/qxorm_en/resource/logo_qxorm_small.png | Bin 0 -> 15180 bytes doc/qxorm_en/resource/logo_windows.gif | Bin 0 -> 2318 bytes .../resource/paypal_support_qxorm_library.gif | Bin 0 -> 4095 bytes doc/qxorm_en/resource/qt_ambassador_logo.png | Bin 0 -> 11561 bytes .../resource/qt_ambassador_logo_150x150.png | Bin 0 -> 5431 bytes .../quick_sample.export_drugs.xml.jpg | Bin 0 -> 40190 bytes .../quick_sample.export_drugs.xml.txt | 25 + .../resource/qxBlog.main.exec.result.txt | 162 + doc/qxorm_en/resource/qxBlog.pro.jpg | Bin 0 -> 49949 bytes doc/qxorm_en/resource/qxBlog.qxBlog.pro | 32 + doc/qxorm_en/resource/qxBlog.table.author.jpg | Bin 0 -> 8708 bytes .../resource/qxBlog.table.category.jpg | Bin 0 -> 11122 bytes .../resource/qxBlog.table.comment.jpg | Bin 0 -> 9477 bytes doc/qxorm_en/resource/qxBlog.table.person.jpg | Bin 0 -> 8424 bytes doc/qxorm_en/resource/qxService.pri | 33 + doc/qxorm_en/resource/qxServiceClient.pro | 11 + doc/qxorm_en/resource/qxServiceServer.pro | 9 + .../resource/qx_blog_rest_api_http_01.png | Bin 0 -> 83759 bytes .../resource/qx_blog_rest_api_qml_01.png | Bin 0 -> 88812 bytes .../qx_http_server_apache_bench_test_01.png | Bin 0 -> 44117 bytes .../qx_http_server_hello_world_01.png | Bin 0 -> 5708 bytes doc/qxorm_en/resource/qx_model_view_01.png | Bin 0 -> 34598 bytes doc/qxorm_en/resource/qx_model_view_02.png | Bin 0 -> 14745 bytes doc/qxorm_en/resource/qx_persistable_cpp.html | 101 + doc/qxorm_en/resource/qx_persistable_hpp.html | 165 + .../resource/qxclassx_dump_sql_schema.html | 121 + .../resource/qxorm.namespace.qx.dao.jpg | Bin 0 -> 25144 bytes doc/qxorm_en/resource/qxorm.namespace.qx.jpg | Bin 0 -> 20491 bytes .../qxorm.namespace.qx.serialization.jpg | Bin 0 -> 15347 bytes .../resource/qxorm.namespace.qx.trait.jpg | Bin 0 -> 27120 bytes doc/qxorm_en/resource/qxorm_cmake.png | Bin 0 -> 29428 bytes doc/qxorm_en/resource/qxorm_script.js | 92 + doc/qxorm_en/resource/qxorm_style.css | 427 + doc/qxorm_en/resource/tuto_01_qxBlog.jpg | Bin 0 -> 3220 bytes .../resource/tuto_02_qxClientServer.jpg | Bin 0 -> 4034 bytes .../resource/tuto_03_install_QxOrm.jpg | Bin 0 -> 3456 bytes .../resource/tuto_install_qxorm_1.png | Bin 0 -> 4341 bytes .../resource/tuto_install_qxorm_10.png | Bin 0 -> 28652 bytes .../resource/tuto_install_qxorm_11.png | Bin 0 -> 26323 bytes .../resource/tuto_install_qxorm_12.png | Bin 0 -> 67411 bytes .../resource/tuto_install_qxorm_13.png | Bin 0 -> 81128 bytes .../resource/tuto_install_qxorm_2.png | Bin 0 -> 31488 bytes .../resource/tuto_install_qxorm_3.png | Bin 0 -> 26927 bytes .../resource/tuto_install_qxorm_4.png | Bin 0 -> 28577 bytes .../resource/tuto_install_qxorm_5.png | Bin 0 -> 23983 bytes .../resource/tuto_install_qxorm_6.png | Bin 0 -> 11884 bytes .../resource/tuto_install_qxorm_7.png | Bin 0 -> 27093 bytes .../resource/tuto_install_qxorm_8.png | Bin 0 -> 28327 bytes .../resource/tuto_install_qxorm_9.png | Bin 0 -> 28185 bytes doc/qxorm_en/resource/work_in_progress.png | Bin 0 -> 34072 bytes doc/qxorm_en/tutorial.html | 1119 ++ doc/qxorm_en/tutorial_2.html | 1062 ++ doc/qxorm_en/tutorial_3.html | 520 + doc/qxorm_en/tutorial_4.html | 232 + doc/qxorm_fr/customer.html | 493 + doc/qxorm_fr/download.html | 1271 ++ doc/qxorm_fr/download_details.php | 165 + doc/qxorm_fr/faq.html | 3768 +++++ doc/qxorm_fr/home.html | 491 + doc/qxorm_fr/link.html | 265 + doc/qxorm_fr/manual.html | 13103 ++++++++++++++++ doc/qxorm_fr/manual_qxee.html | 5824 +++++++ doc/qxorm_fr/model.html | 177 + doc/qxorm_fr/quick_sample.html | 460 + doc/qxorm_fr/resource/ES.png | Bin 0 -> 605 bytes doc/qxorm_fr/resource/FR.png | Bin 0 -> 442 bytes doc/qxorm_fr/resource/GB.png | Bin 0 -> 942 bytes doc/qxorm_fr/resource/background.jpg | Bin 0 -> 124834 bytes doc/qxorm_fr/resource/download.jpg | Bin 0 -> 1407 bytes doc/qxorm_fr/resource/gui_qxClientServer.jpg | Bin 0 -> 192022 bytes doc/qxorm_fr/resource/gui_qxClient_01.jpg | Bin 0 -> 67652 bytes doc/qxorm_fr/resource/gui_qxClient_02.jpg | Bin 0 -> 51014 bytes doc/qxorm_fr/resource/gui_qxServer.jpg | Bin 0 -> 60288 bytes doc/qxorm_fr/resource/jquery.min.js | 5 + doc/qxorm_fr/resource/license.gpl3.txt | 630 + doc/qxorm_fr/resource/link_download.jpg | Bin 0 -> 3064 bytes doc/qxorm_fr/resource/link_faq.jpg | Bin 0 -> 2192 bytes doc/qxorm_fr/resource/link_home.jpg | Bin 0 -> 2212 bytes doc/qxorm_fr/resource/link_link.jpg | Bin 0 -> 2184 bytes doc/qxorm_fr/resource/link_model.jpg | Bin 0 -> 1488 bytes doc/qxorm_fr/resource/link_quick_sample.jpg | Bin 0 -> 3071 bytes doc/qxorm_fr/resource/link_tutorial.jpg | Bin 0 -> 2291 bytes doc/qxorm_fr/resource/logo_boost.jpg | Bin 0 -> 4563 bytes doc/qxorm_fr/resource/logo_cpp.gif | Bin 0 -> 1306 bytes doc/qxorm_fr/resource/logo_linux.gif | Bin 0 -> 2374 bytes doc/qxorm_fr/resource/logo_mac.gif | Bin 0 -> 2352 bytes doc/qxorm_fr/resource/logo_qt.jpg | Bin 0 -> 2445 bytes doc/qxorm_fr/resource/logo_qxorm.jpg | Bin 0 -> 8199 bytes doc/qxorm_fr/resource/logo_qxorm.png | Bin 0 -> 28681 bytes doc/qxorm_fr/resource/logo_qxorm_and_qxee.png | Bin 0 -> 53095 bytes .../resource/logo_qxorm_and_qxee_old.png | Bin 0 -> 47030 bytes doc/qxorm_fr/resource/logo_qxorm_small.jpg | Bin 0 -> 4570 bytes doc/qxorm_fr/resource/logo_qxorm_small.png | Bin 0 -> 15180 bytes doc/qxorm_fr/resource/logo_windows.gif | Bin 0 -> 2318 bytes .../resource/paypal_support_qxorm_library.gif | Bin 0 -> 4095 bytes doc/qxorm_fr/resource/qt_ambassador_logo.png | Bin 0 -> 11561 bytes .../resource/qt_ambassador_logo_150x150.png | Bin 0 -> 5431 bytes .../quick_sample.export_drugs.xml.jpg | Bin 0 -> 40190 bytes .../quick_sample.export_drugs.xml.txt | 25 + .../resource/qxBlog.main.exec.result.txt | 162 + doc/qxorm_fr/resource/qxBlog.pro.jpg | Bin 0 -> 49949 bytes doc/qxorm_fr/resource/qxBlog.qxBlog.pro | 32 + doc/qxorm_fr/resource/qxBlog.table.author.jpg | Bin 0 -> 8708 bytes .../resource/qxBlog.table.category.jpg | Bin 0 -> 11122 bytes .../resource/qxBlog.table.comment.jpg | Bin 0 -> 9477 bytes doc/qxorm_fr/resource/qxBlog.table.person.jpg | Bin 0 -> 8424 bytes doc/qxorm_fr/resource/qxService.pri | 33 + doc/qxorm_fr/resource/qxServiceClient.pro | 11 + doc/qxorm_fr/resource/qxServiceServer.pro | 9 + .../resource/qx_blog_rest_api_http_01.png | Bin 0 -> 83759 bytes .../resource/qx_blog_rest_api_qml_01.png | Bin 0 -> 88812 bytes .../qx_http_server_apache_bench_test_01.png | Bin 0 -> 44117 bytes .../qx_http_server_hello_world_01.png | Bin 0 -> 5708 bytes doc/qxorm_fr/resource/qx_model_view_01.png | Bin 0 -> 34598 bytes doc/qxorm_fr/resource/qx_model_view_02.png | Bin 0 -> 14745 bytes doc/qxorm_fr/resource/qx_persistable_cpp.html | 101 + doc/qxorm_fr/resource/qx_persistable_hpp.html | 165 + .../resource/qxclassx_dump_sql_schema.html | 121 + .../resource/qxorm.namespace.qx.dao.jpg | Bin 0 -> 25144 bytes doc/qxorm_fr/resource/qxorm.namespace.qx.jpg | Bin 0 -> 20491 bytes .../qxorm.namespace.qx.serialization.jpg | Bin 0 -> 15347 bytes .../resource/qxorm.namespace.qx.trait.jpg | Bin 0 -> 27120 bytes doc/qxorm_fr/resource/qxorm_cmake.png | Bin 0 -> 29428 bytes doc/qxorm_fr/resource/qxorm_script.js | 92 + doc/qxorm_fr/resource/qxorm_style.css | 427 + doc/qxorm_fr/resource/tuto_01_qxBlog.jpg | Bin 0 -> 3220 bytes .../resource/tuto_02_qxClientServer.jpg | Bin 0 -> 4034 bytes .../resource/tuto_03_install_QxOrm.jpg | Bin 0 -> 3456 bytes .../resource/tuto_install_qxorm_1.png | Bin 0 -> 4341 bytes .../resource/tuto_install_qxorm_10.png | Bin 0 -> 44081 bytes .../resource/tuto_install_qxorm_11.png | Bin 0 -> 26323 bytes .../resource/tuto_install_qxorm_12.png | Bin 0 -> 73570 bytes .../resource/tuto_install_qxorm_13.png | Bin 0 -> 74894 bytes .../resource/tuto_install_qxorm_2.png | Bin 0 -> 28704 bytes .../resource/tuto_install_qxorm_3.png | Bin 0 -> 47749 bytes .../resource/tuto_install_qxorm_4.png | Bin 0 -> 41618 bytes .../resource/tuto_install_qxorm_5.png | Bin 0 -> 23983 bytes .../resource/tuto_install_qxorm_6.png | Bin 0 -> 11884 bytes .../resource/tuto_install_qxorm_7.png | Bin 0 -> 54799 bytes .../resource/tuto_install_qxorm_8.png | Bin 0 -> 55654 bytes .../resource/tuto_install_qxorm_9.png | Bin 0 -> 40526 bytes doc/qxorm_fr/resource/work_in_progress.png | Bin 0 -> 34072 bytes doc/qxorm_fr/tutorial.html | 1209 ++ doc/qxorm_fr/tutorial_2.html | 1119 ++ doc/qxorm_fr/tutorial_3.html | 538 + doc/qxorm_fr/tutorial_4.html | 234 + include/QxCollection/IxCollection.h | 82 + include/QxCollection/QxCollection.h | 285 + include/QxCollection/QxCollectionIterator.h | 100 + include/QxCollection/QxForeach.h | 206 + include/QxCommon/QxAny.h | 199 + include/QxCommon/QxAnyCastDynamic.h | 162 + include/QxCommon/QxBool.h | 190 + include/QxCommon/QxCache.h | 264 + include/QxCommon/QxConfig.h | 184 + include/QxCommon/QxException.h | 83 + include/QxCommon/QxExceptionCode.h | 64 + include/QxCommon/QxHashValue.h | 493 + include/QxCommon/QxMacro.h | 298 + include/QxCommon/QxMainPage.h | 367 + include/QxCommon/QxPropertyBag.h | 105 + include/QxCommon/QxSimpleCrypt.h | 250 + include/QxConvert/QxConvert.h | 117 + include/QxConvert/QxConvert_Export.h | 224 + include/QxConvert/QxConvert_Impl.h | 906 ++ include/QxDao/IxDao_Helper.h | 237 + include/QxDao/IxPersistable.h | 713 + include/QxDao/IxPersistableCollection.h | 364 + include/QxDao/IxPersistableList.h | 311 + include/QxDao/IxSqlQueryBuilder.h | 159 + include/QxDao/IxSqlRelation.h | 203 + include/QxDao/QxDao.h | 938 ++ include/QxDao/QxDaoAsync.h | 238 + include/QxDao/QxDaoPointer.h | 333 + include/QxDao/QxDaoStrategy.h | 78 + include/QxDao/QxDaoThrowable.h | 955 ++ include/QxDao/QxDao_Impl.h | 88 + include/QxDao/QxDao_IsDirty.h | 153 + include/QxDao/QxDateNeutral.h | 153 + include/QxDao/QxDateTimeNeutral.h | 153 + include/QxDao/QxMongoDB/QxMongoDB_Helper.h | 155 + include/QxDao/QxRepository/IxRepository.h | 128 + include/QxDao/QxRepository/QxRepository.h | 606 + include/QxDao/QxRepository/QxRepositoryX.h | 89 + include/QxDao/QxSession.h | 457 + include/QxDao/QxSoftDelete.h | 148 + include/QxDao/QxSqlDatabase.h | 189 + include/QxDao/QxSqlElement/IxSqlElement.h | 208 + include/QxDao/QxSqlElement/QxSqlCompare.h | 107 + include/QxDao/QxSqlElement/QxSqlElement.h | 52 + include/QxDao/QxSqlElement/QxSqlElementTemp.h | 83 + include/QxDao/QxSqlElement/QxSqlEmbedQuery.h | 105 + include/QxDao/QxSqlElement/QxSqlExpression.h | 97 + include/QxDao/QxSqlElement/QxSqlFreeText.h | 89 + include/QxDao/QxSqlElement/QxSqlIn.h | 96 + include/QxDao/QxSqlElement/QxSqlIsBetween.h | 94 + include/QxDao/QxSqlElement/QxSqlIsNull.h | 94 + include/QxDao/QxSqlElement/QxSqlLimit.h | 93 + include/QxDao/QxSqlElement/QxSqlSort.h | 95 + include/QxDao/QxSqlError.h | 108 + include/QxDao/QxSqlGenerator/IxSqlGenerator.h | 105 + include/QxDao/QxSqlGenerator/QxSqlGenerator.h | 48 + .../QxSqlGenerator_MSSQLServer.h | 81 + .../QxSqlGenerator/QxSqlGenerator_MySQL.h | 78 + .../QxSqlGenerator/QxSqlGenerator_Oracle.h | 95 + .../QxSqlGenerator_PostgreSQL.h | 79 + .../QxSqlGenerator/QxSqlGenerator_SQLite.h | 76 + .../QxSqlGenerator/QxSqlGenerator_Standard.h | 92 + include/QxDao/QxSqlJoin.h | 72 + include/QxDao/QxSqlQuery.h | 729 + include/QxDao/QxSqlQueryBuilder.h | 567 + include/QxDao/QxSqlQueryHelper.h | 87 + include/QxDao/QxSqlRelation.h | 296 + include/QxDao/QxSqlRelationLinked.h | 149 + include/QxDao/QxSqlRelationParams.h | 195 + include/QxDao/QxSqlRelation_ManyToMany.h | 341 + include/QxDao/QxSqlRelation_ManyToOne.h | 300 + include/QxDao/QxSqlRelation_OneToMany.h | 322 + include/QxDao/QxSqlRelation_OneToOne.h | 228 + include/QxDao/QxSqlRelation_RawData.h | 30 + include/QxDao/QxSqlSaveMode.h | 70 + include/QxDao/QxTimeNeutral.h | 153 + include/QxDaoRepository.h | 52 + include/QxDataMember/IxDataMember.h | 430 + include/QxDataMember/IxDataMemberX.h | 107 + include/QxDataMember/QxDataMember.h | 202 + include/QxDataMember/QxDataMemberX.h | 229 + include/QxDataMember/QxDataMember_PImpl.h | 376 + include/QxDataMember/QxDataMember_QObject.h | 132 + include/QxExtras/QxBoostOptionalOnly.h | 178 + include/QxExtras/QxStdOptional.h | 202 + include/QxFactory/IxFactory.h | 87 + include/QxFactory/QxFactory.h | 231 + include/QxFactory/QxFactoryX.h | 144 + include/QxFunction/IxFunction.h | 161 + include/QxFunction/QxFunctionError.h | 57 + include/QxFunction/QxFunctionInclude.h | 51 + include/QxFunction/QxFunctionMacro.h | 210 + include/QxFunction/QxFunction_0.h | 188 + include/QxFunction/QxFunction_1.h | 194 + include/QxFunction/QxFunction_2.h | 200 + include/QxFunction/QxFunction_3.h | 206 + include/QxFunction/QxFunction_4.h | 212 + include/QxFunction/QxFunction_5.h | 218 + include/QxFunction/QxFunction_6.h | 224 + include/QxFunction/QxFunction_7.h | 230 + include/QxFunction/QxFunction_8.h | 236 + include/QxFunction/QxFunction_9.h | 242 + include/QxFunction/QxParameters.h | 140 + include/QxHttpServer/QxHttpCookie.h | 76 + include/QxHttpServer/QxHttpRequest.h | 94 + include/QxHttpServer/QxHttpResponse.h | 91 + include/QxHttpServer/QxHttpServer.h | 133 + include/QxHttpServer/QxHttpSession.h | 91 + include/QxHttpServer/QxHttpSessionManager.h | 103 + include/QxHttpServer/QxHttpTransaction.h | 99 + include/QxMemLeak.h | 64 + include/QxMemLeak/bool_array.h | 244 + include/QxMemLeak/class_level_lock.h | 139 + include/QxMemLeak/cont_ptr_utils.h | 151 + include/QxMemLeak/debug_new.h | 220 + include/QxMemLeak/fast_mutex.h | 367 + include/QxMemLeak/fixed_mem_pool.h | 330 + include/QxMemLeak/mem_leak.h | 82 + include/QxMemLeak/mem_pool_base.h | 77 + include/QxMemLeak/object_level_lock.h | 156 + include/QxMemLeak/pctimer.h | 112 + include/QxMemLeak/set_assign.h | 162 + include/QxMemLeak/static_assert.h | 63 + include/QxMemLeak/static_mem_pool.h | 395 + include/QxModelView.h | 54 + include/QxModelView/IxModel.h | 374 + include/QxModelView/QxModel.h | 1136 ++ include/QxModelView/QxModelRowCompare.h | 83 + include/QxModelView/QxModelService.h | 577 + include/QxModelView/QxNestedModel.h | 483 + include/QxOrm.h | 192 + include/QxOrm_Impl.h | 47 + include/QxPrecompiled.h | 135 + include/QxRegister/IxClass.h | 133 + include/QxRegister/IxTypeInfo.h | 54 + include/QxRegister/QxClass.h | 433 + include/QxRegister/QxClassName.h | 84 + include/QxRegister/QxClassX.h | 185 + include/QxRegister/QxRegister.h | 517 + include/QxRegister/QxRegisterInternalHelper.h | 92 + include/QxRegister/QxRegisterQtProperty.h | 95 + include/QxRegister/QxVersion.h | 82 + include/QxRestApi/QxRestApi.h | 113 + .../QxSerializeQDataStream_QFlags.h | 69 + .../QxSerializeQDataStream_QObject.h | 52 + .../QxSerializeQDataStream_QScopedPointer.h | 80 + .../QxSerializeQDataStream_QSharedPointer.h | 78 + .../QxSerializeQDataStream_QSqlError.h | 53 + .../QxSerializeQDataStream_QWeakPointer.h | 68 + .../QxSerializeQDataStream_all_include.h | 72 + .../QxSerializeQDataStream_boost_optional.h | 79 + .../QxSerializeQDataStream_boost_scoped_ptr.h | 79 + .../QxSerializeQDataStream_boost_shared_ptr.h | 79 + .../QxSerializeQDataStream_boost_tuple.h | 266 + ...SerializeQDataStream_boost_unordered_map.h | 122 + ...SerializeQDataStream_boost_unordered_set.h | 108 + .../QxSerializeQDataStream_primitive_type.h | 51 + ...SerializeQDataStream_qx_registered_class.h | 91 + .../QxSerializeQDataStream_std_list.h | 78 + .../QxSerializeQDataStream_std_map.h | 85 + .../QxSerializeQDataStream_std_pair.h | 66 + .../QxSerializeQDataStream_std_set.h | 78 + .../QxSerializeQDataStream_std_shared_ptr.h | 77 + .../QxSerializeQDataStream_std_string.h | 56 + .../QxSerializeQDataStream_std_tuple.h | 264 + .../QxSerializeQDataStream_std_unique_ptr.h | 77 + ...QxSerializeQDataStream_std_unordered_map.h | 122 + ...QxSerializeQDataStream_std_unordered_set.h | 108 + .../QxSerializeQDataStream_std_vector.h | 79 + .../QJson/QxSerializeQJson_IxParameter.h | 93 + .../QJson/QxSerializeQJson_IxPersistable.h | 95 + .../QJson/QxSerializeQJson_IxService.h | 88 + .../QJson/QxSerializeQJson_IxSqlElement.h | 86 + .../QJson/QxSerializeQJson_QBrush.h | 91 + .../QJson/QxSerializeQJson_QColor.h | 91 + .../QJson/QxSerializeQJson_QFlags.h | 88 + .../QJson/QxSerializeQJson_QFont.h | 91 + .../QJson/QxSerializeQJson_QHash.h | 282 + .../QJson/QxSerializeQJson_QImage.h | 93 + .../QJson/QxSerializeQJson_QLinkedList.h | 105 + .../QJson/QxSerializeQJson_QList.h | 107 + .../QxSerialize/QJson/QxSerializeQJson_QMap.h | 278 + .../QJson/QxSerializeQJson_QMatrix.h | 93 + .../QJson/QxSerializeQJson_QMultiHash.h | 125 + .../QJson/QxSerializeQJson_QMultiMap.h | 124 + .../QJson/QxSerializeQJson_QObject.h | 88 + .../QJson/QxSerializeQJson_QPair.h | 105 + .../QJson/QxSerializeQJson_QPicture.h | 93 + .../QJson/QxSerializeQJson_QPixmap.h | 93 + .../QJson/QxSerializeQJson_QPoint.h | 88 + .../QJson/QxSerializeQJson_QRect.h | 88 + .../QJson/QxSerializeQJson_QRegExp.h | 90 + .../QJson/QxSerializeQJson_QRegion.h | 91 + .../QJson/QxSerializeQJson_QScopedPointer.h | 97 + .../QJson/QxSerializeQJson_QSharedPointer.h | 95 + .../QJson/QxSerializeQJson_QSize.h | 88 + .../QJson/QxSerializeQJson_QSqlError.h | 89 + .../QJson/QxSerializeQJson_QStringList.h | 88 + .../QxSerialize/QJson/QxSerializeQJson_QUrl.h | 88 + .../QJson/QxSerializeQJson_QVariantHash.h | 90 + .../QJson/QxSerializeQJson_QVariantMap.h | 88 + .../QJson/QxSerializeQJson_QVector.h | 105 + .../QJson/QxSerializeQJson_QWeakPointer.h | 91 + .../QJson/QxSerializeQJson_QxCollection.h | 274 + .../QJson/QxSerializeQJson_QxDaoPointer.h | 104 + .../QJson/QxSerializeQJson_QxInvalidValue.h | 86 + .../QJson/QxSerializeQJson_QxInvalidValueX.h | 86 + .../QJson/QxSerializeQJson_QxSqlQuery.h | 86 + .../QJson/QxSerializeQJson_QxTransaction.h | 88 + .../QJson/QxSerializeQJson_all_include.h | 108 + .../QJson/QxSerializeQJson_boost_scoped_ptr.h | 96 + .../QJson/QxSerializeQJson_boost_shared_ptr.h | 96 + .../QJson/QxSerializeQJson_boost_tuple.h | 455 + .../QxSerializeQJson_boost_unordered_map.h | 334 + .../QxSerializeQJson_boost_unordered_set.h | 142 + .../QxSerializeQJson_qx_registered_class.h | 99 + .../QJson/QxSerializeQJson_std_list.h | 104 + .../QJson/QxSerializeQJson_std_map.h | 274 + .../QJson/QxSerializeQJson_std_pair.h | 102 + .../QJson/QxSerializeQJson_std_set.h | 104 + .../QJson/QxSerializeQJson_std_shared_ptr.h | 94 + .../QJson/QxSerializeQJson_std_tuple.h | 453 + .../QJson/QxSerializeQJson_std_unique_ptr.h | 94 + .../QxSerializeQJson_std_unordered_map.h | 334 + .../QxSerializeQJson_std_unordered_set.h | 142 + .../QJson/QxSerializeQJson_std_vector.h | 105 + include/QxSerialize/Qt/QxSerialize_QBrush.h | 59 + .../QxSerialize/Qt/QxSerialize_QByteArray.h | 56 + include/QxSerialize/Qt/QxSerialize_QColor.h | 57 + include/QxSerialize/Qt/QxSerialize_QDate.h | 58 + .../QxSerialize/Qt/QxSerialize_QDateTime.h | 58 + include/QxSerialize/Qt/QxSerialize_QFlags.h | 78 + include/QxSerialize/Qt/QxSerialize_QFont.h | 58 + include/QxSerialize/Qt/QxSerialize_QHash.h | 96 + include/QxSerialize/Qt/QxSerialize_QImage.h | 62 + .../QxSerialize/Qt/QxSerialize_QLinkedList.h | 92 + include/QxSerialize/Qt/QxSerialize_QList.h | 89 + include/QxSerialize/Qt/QxSerialize_QMap.h | 95 + include/QxSerialize/Qt/QxSerialize_QMatrix.h | 59 + .../QxSerialize/Qt/QxSerialize_QMultiHash.h | 96 + .../QxSerialize/Qt/QxSerialize_QMultiMap.h | 95 + include/QxSerialize/Qt/QxSerialize_QObject.h | 59 + include/QxSerialize/Qt/QxSerialize_QPair.h | 63 + include/QxSerialize/Qt/QxSerialize_QPicture.h | 62 + include/QxSerialize/Qt/QxSerialize_QPixmap.h | 62 + include/QxSerialize/Qt/QxSerialize_QPoint.h | 55 + include/QxSerialize/Qt/QxSerialize_QRect.h | 55 + include/QxSerialize/Qt/QxSerialize_QRegExp.h | 58 + include/QxSerialize/Qt/QxSerialize_QRegion.h | 59 + .../Qt/QxSerialize_QScopedPointer.h | 80 + .../Qt/QxSerialize_QSharedPointer.h | 113 + include/QxSerialize/Qt/QxSerialize_QSize.h | 55 + .../QxSerialize/Qt/QxSerialize_QSqlError.h | 56 + include/QxSerialize/Qt/QxSerialize_QString.h | 58 + .../QxSerialize/Qt/QxSerialize_QStringList.h | 59 + include/QxSerialize/Qt/QxSerialize_QTime.h | 58 + include/QxSerialize/Qt/QxSerialize_QUrl.h | 56 + include/QxSerialize/Qt/QxSerialize_QUuid.h | 55 + include/QxSerialize/Qt/QxSerialize_QVariant.h | 72 + include/QxSerialize/Qt/QxSerialize_QVector.h | 92 + .../QxSerialize/Qt/QxSerialize_QWeakPointer.h | 80 + .../QxSerialize/Qx/QxSerialize_QxCollection.h | 94 + .../QxSerialize/Qx/QxSerialize_QxDaoPointer.h | 82 + .../QxSerialize/Qx/QxSerialize_QxXmlReader.h | 34 + .../QxSerialize/Qx/QxSerialize_QxXmlWriter.h | 34 + include/QxSerialize/QxArchive.h | 307 + .../IxBoostSerializeRegisterHelper.h | 108 + .../QxBoostSerializeHelper/QxBoostInitGuid.h | 83 + .../QxBoostSerializeHelper.h | 94 + .../QxBoostSerializeRegisterHelper.h | 131 + .../QxBoostSerializeRegisterHelperX.h | 134 + include/QxSerialize/QxClone.h | 208 + include/QxSerialize/QxDump.h | 125 + include/QxSerialize/QxSerialize.h | 159 + .../QxSerialize/QxSerializeCheckInstance.h | 100 + include/QxSerialize/QxSerializeFastCompil.h | 318 + include/QxSerialize/QxSerializeInvoker.h | 147 + include/QxSerialize/QxSerializeMacro.h | 67 + include/QxSerialize/QxSerializeQDataStream.h | 204 + include/QxSerialize/QxSerializeQJson.h | 166 + .../boost/QxExportDllBoostArchive.h | 237 + .../QxSerialize/boost/QxExportDllMacroCpp.h | 252 + .../QxSerialize/boost/QxExportDllMacroHpp.h | 264 + .../boost/QxImportDllBoostArchive.h | 84 + .../QxSerialize/boost/QxSerializeInclude.h | 195 + .../boost/QxSerialize_shared_ptr.h | 63 + include/QxSerialize/boost/QxSerialize_tuple.h | 161 + .../boost/QxSerialize_unordered_map.h | 176 + .../boost/QxSerialize_unordered_set.h | 174 + .../class_export/qx_boost_class_export.h | 208 + .../portable_archive_exception.hpp | 104 + .../portable_binary/portable_iarchive.hpp | 505 + .../portable_binary/portable_oarchive.hpp | 488 + .../std/QxSerialize_std_shared_ptr.h | 83 + .../QxSerialize/std/QxSerialize_std_tuple.h | 161 + .../std/QxSerialize_std_unique_ptr.h | 83 + .../std/QxSerialize_std_unordered_map.h | 111 + .../std/QxSerialize_std_unordered_set.h | 111 + include/QxService/IxParameter.h | 146 + include/QxService/IxService.h | 171 + include/QxService/QxClientAsync.h | 97 + include/QxService/QxConnect.h | 156 + include/QxService/QxServer.h | 88 + include/QxService/QxService.h | 109 + include/QxService/QxThread.h | 166 + include/QxService/QxThreadPool.h | 129 + include/QxService/QxTools.h | 72 + include/QxService/QxTransaction.h | 242 + include/QxServices.h | 63 + include/QxSingleton/IxSingleton.h | 82 + include/QxSingleton/QxSingleton.h | 84 + include/QxSingleton/QxSingletonInit.h | 79 + include/QxSingleton/QxSingletonX.h | 83 + include/QxTraits/archive_printable.h | 168 + include/QxTraits/archive_wide_traits.h | 249 + include/QxTraits/construct_null_qvariant.h | 727 + include/QxTraits/construct_ptr.h | 242 + include/QxTraits/generic_container.h | 662 + include/QxTraits/get_base_class.h | 124 + include/QxTraits/get_class_name.h | 378 + include/QxTraits/get_class_name_primitive.h | 237 + include/QxTraits/get_primary_key.h | 79 + include/QxTraits/get_sql_type.h | 416 + include/QxTraits/is_boost_intrusive_ptr.h | 90 + include/QxTraits/is_boost_scoped_ptr.h | 90 + include/QxTraits/is_boost_shared_ptr.h | 90 + include/QxTraits/is_boost_unordered_map.h | 114 + include/QxTraits/is_boost_unordered_set.h | 114 + include/QxTraits/is_boost_weak_ptr.h | 90 + include/QxTraits/is_container.h | 596 + include/QxTraits/is_container_base_of.h | 166 + include/QxTraits/is_container_key_value.h | 310 + include/QxTraits/is_container_to_pod.h | 108 + include/QxTraits/is_equal.h | 181 + include/QxTraits/is_ptr_base_of.h | 79 + include/QxTraits/is_ptr_to_pod.h | 80 + include/QxTraits/is_qt_hash.h | 90 + include/QxTraits/is_qt_linked_list.h | 92 + include/QxTraits/is_qt_list.h | 90 + include/QxTraits/is_qt_map.h | 90 + include/QxTraits/is_qt_multi_hash.h | 90 + include/QxTraits/is_qt_multi_map.h | 90 + include/QxTraits/is_qt_scoped_ptr.h | 114 + include/QxTraits/is_qt_set.h | 90 + include/QxTraits/is_qt_shared_data_ptr.h | 90 + include/QxTraits/is_qt_shared_ptr.h | 90 + include/QxTraits/is_qt_variant_compatible.h | 415 + include/QxTraits/is_qt_vector.h | 90 + include/QxTraits/is_qt_weak_ptr.h | 90 + include/QxTraits/is_qx_collection.h | 75 + include/QxTraits/is_qx_dao_ptr.h | 90 + include/QxTraits/is_qx_pod.h | 70 + include/QxTraits/is_qx_registered.h | 83 + include/QxTraits/is_smart_ptr.h | 373 + include/QxTraits/is_smart_ptr_base_of.h | 170 + include/QxTraits/is_smart_ptr_to_pod.h | 115 + include/QxTraits/is_std_list.h | 90 + include/QxTraits/is_std_map.h | 90 + include/QxTraits/is_std_set.h | 90 + include/QxTraits/is_std_shared_ptr.h | 88 + include/QxTraits/is_std_unique_ptr.h | 88 + include/QxTraits/is_std_unordered_map.h | 114 + include/QxTraits/is_std_unordered_set.h | 114 + include/QxTraits/is_std_vector.h | 90 + include/QxTraits/is_std_weak_ptr.h | 88 + include/QxTraits/is_valid_primary_key.h | 144 + include/QxTraits/qt_meta_object.h | 90 + include/QxTraits/qx_traits.h | 94 + include/QxTraits/remove_attr.h | 72 + include/QxTraits/remove_smart_ptr.h | 126 + include/QxValidator/IxValidator.h | 130 + include/QxValidator/IxValidatorX.h | 113 + include/QxValidator/QxInvalidValue.h | 160 + include/QxValidator/QxInvalidValueX.h | 159 + include/QxValidator/QxValidator.h | 186 + include/QxValidator/QxValidatorError.h | 84 + include/QxValidator/QxValidatorFct.h | 232 + include/QxValidator/QxValidatorX.h | 116 + include/QxXml/QxXml.h | 67 + include/QxXml/QxXmlReader.h | 83 + include/QxXml/QxXmlWriter.h | 92 + inl/QxCollection/QxCollection.inl | 615 + inl/QxCollection/QxCollectionIterator.inl | 87 + inl/QxConvert/QxConvert_FromJson.inl | 413 + inl/QxConvert/QxConvert_FromString.inl | 979 ++ inl/QxConvert/QxConvert_FromVariant.inl | 991 ++ inl/QxConvert/QxConvert_Qt.inl | 71 + inl/QxConvert/QxConvert_ToJson.inl | 387 + inl/QxConvert/QxConvert_ToString.inl | 923 ++ inl/QxConvert/QxConvert_ToVariant.inl | 946 ++ inl/QxConvert/QxConvert_WithIndex.inl | 1015 ++ inl/QxDao/QxDao_Count.inl | 196 + inl/QxDao/QxDao_CreateTable.inl | 96 + inl/QxDao/QxDao_DeleteAll.inl | 103 + inl/QxDao/QxDao_DeleteById.inl | 381 + inl/QxDao/QxDao_ExecuteQuery.inl | 295 + inl/QxDao/QxDao_Exist.inl | 261 + inl/QxDao/QxDao_FetchAll.inl | 291 + inl/QxDao/QxDao_FetchAll_WithRelation.inl | 451 + inl/QxDao/QxDao_FetchById.inl | 363 + inl/QxDao/QxDao_FetchById_WithRelation.inl | 459 + inl/QxDao/QxDao_Helper.inl | 76 + inl/QxDao/QxDao_Helper_Container.inl | 127 + inl/QxDao/QxDao_Insert.inl | 394 + inl/QxDao/QxDao_Insert_WithRelation.inl | 276 + inl/QxDao/QxDao_Save.inl | 222 + inl/QxDao/QxDao_Save_WithRelation.inl | 240 + .../QxDao_Save_WithRelation_Recursive.inl | 516 + inl/QxDao/QxDao_Trigger.inl | 122 + inl/QxDao/QxDao_Update.inl | 398 + inl/QxDao/QxDao_Update_Optimized.inl | 149 + inl/QxDao/QxDao_Update_WithRelation.inl | 280 + inl/QxDao/QxSqlQueryHelper_CreateTable.inl | 66 + inl/QxDao/QxSqlQueryHelper_DeleteById.inl | 65 + inl/QxDao/QxSqlQueryHelper_Exist.inl | 67 + inl/QxDao/QxSqlQueryHelper_FetchAll.inl | 96 + ...QxSqlQueryHelper_FetchAll_WithRelation.inl | 78 + inl/QxDao/QxSqlQueryHelper_FetchById.inl | 94 + ...xSqlQueryHelper_FetchById_WithRelation.inl | 77 + inl/QxDao/QxSqlQueryHelper_Insert.inl | 65 + inl/QxDao/QxSqlQueryHelper_Update.inl | 96 + inl/QxDataMember/QxDataMember.inl | 35 + inl/QxDataMember/QxDataMemberX.inl | 463 + inl/QxFactory/QxFactory.inl | 56 + inl/QxRegister/QxClass.inl | 361 + inl/QxSerialize/QxArchive.inl | 416 + inl/QxSerialize/QxSerializeInvoker.inl | 50 + inl/QxSingleton/QxSingleton.inl | 69 + inl/QxXml/QxXml.inl | 85 + install-deps-debug.bat | 13 + install-deps-release.bat | 13 + qt/moc/.gitignore | 7 + qt/rcc/src/.gitignore | 7 + qt/ui/include/.gitignore | 7 + qt/ui/src/.gitignore | 7 + src/QxCollection/QxCollection.cpp | 93 + src/QxCommon/QxBool.cpp | 54 + src/QxCommon/QxCache.cpp | 168 + src/QxCommon/QxSimpleCrypt.cpp | 254 + src/QxConvert/QxConvert_Export.cpp | 222 + src/QxDao/IxDao_Helper.cpp | 907 ++ src/QxDao/IxPersistable.cpp | 125 + src/QxDao/IxPersistableCollection.cpp | 45 + src/QxDao/IxPersistableList.cpp | 41 + src/QxDao/IxSqlQueryBuilder.cpp | 953 ++ src/QxDao/IxSqlRelation.cpp | 1486 ++ src/QxDao/QxDaoAsync.cpp | 543 + src/QxDao/QxDateNeutral.cpp | 50 + src/QxDao/QxDateTimeNeutral.cpp | 50 + src/QxDao/QxMongoDB/QxMongoDB_Helper.cpp | 1851 +++ src/QxDao/QxRepository/IxRepository.cpp | 130 + src/QxDao/QxRepository/QxRepositoryX.cpp | 93 + src/QxDao/QxSession.cpp | 353 + src/QxDao/QxSoftDelete.cpp | 158 + src/QxDao/QxSqlDatabase.cpp | 1298 ++ src/QxDao/QxSqlElement/IxSqlElement.cpp | 182 + src/QxDao/QxSqlElement/QxSqlCompare.cpp | 174 + src/QxDao/QxSqlElement/QxSqlElementTemp.cpp | 67 + src/QxDao/QxSqlElement/QxSqlEmbedQuery.cpp | 131 + src/QxDao/QxSqlElement/QxSqlExpression.cpp | 95 + src/QxDao/QxSqlElement/QxSqlFreeText.cpp | 125 + src/QxDao/QxSqlElement/QxSqlIn.cpp | 142 + src/QxDao/QxSqlElement/QxSqlIsBetween.cpp | 125 + src/QxDao/QxSqlElement/QxSqlIsNull.cpp | 88 + src/QxDao/QxSqlElement/QxSqlLimit.cpp | 172 + src/QxDao/QxSqlElement/QxSqlSort.cpp | 110 + src/QxDao/QxSqlGenerator/IxSqlGenerator.cpp | 51 + .../QxSqlGenerator_MSSQLServer.cpp | 150 + .../QxSqlGenerator/QxSqlGenerator_MySQL.cpp | 91 + .../QxSqlGenerator/QxSqlGenerator_Oracle.cpp | 314 + .../QxSqlGenerator_PostgreSQL.cpp | 152 + .../QxSqlGenerator/QxSqlGenerator_SQLite.cpp | 89 + .../QxSqlGenerator_Standard.cpp | 212 + src/QxDao/QxSqlQuery.cpp | 1604 ++ src/QxDao/QxSqlRelationLinked.cpp | 864 + src/QxDao/QxSqlRelationParams.cpp | 60 + src/QxDao/QxTimeNeutral.cpp | 50 + src/QxDataMember/IxDataMember.cpp | 963 ++ src/QxDataMember/IxDataMemberX.cpp | 107 + src/QxDataMember/QxDataMember_QObject.cpp | 234 + src/QxFactory/IxFactory.cpp | 63 + src/QxFactory/QxFactoryX.cpp | 95 + src/QxHttpServer/QxHttpCookie.cpp | 109 + src/QxHttpServer/QxHttpRequest.cpp | 116 + src/QxHttpServer/QxHttpResponse.cpp | 478 + src/QxHttpServer/QxHttpServer.cpp | 769 + src/QxHttpServer/QxHttpSession.cpp | 115 + src/QxHttpServer/QxHttpSessionManager.cpp | 183 + src/QxHttpServer/QxHttpTransaction.cpp | 510 + src/QxMemLeak/bool_array.cpp | 228 + src/QxMemLeak/debug_new.cpp | 618 + src/QxMemLeak/mem_pool_base.cpp | 84 + src/QxMemLeak/static_mem_pool.cpp | 111 + src/QxModelView/IxModel.cpp | 840 + src/QxModelView/QxModelRowCompare.cpp | 182 + src/QxModelView/QxNestedModel.cpp | 47 + src/QxRegister/IxClass.cpp | 367 + src/QxRegister/QxClassX.cpp | 523 + src/QxRestApi/QxRestApi.cpp | 1045 ++ .../QxSerializeQDataStream_QObject.cpp | 68 + .../QxSerializeQDataStream_QSqlError.cpp | 84 + .../QxSerializeQDataStream_primitive_type.cpp | 51 + ...rializeQDataStream_qx_registered_class.cpp | 175 + .../QxSerializeQDataStream_std_string.cpp | 69 + .../QJson/QxSerializeQJson_IxService.cpp | 134 + .../QJson/QxSerializeQJson_IxSqlElement.cpp | 79 + .../QJson/QxSerializeQJson_QBrush.cpp | 86 + .../QJson/QxSerializeQJson_QColor.cpp | 85 + .../QJson/QxSerializeQJson_QFont.cpp | 66 + .../QJson/QxSerializeQJson_QImage.cpp | 79 + .../QJson/QxSerializeQJson_QMatrix.cpp | 88 + .../QJson/QxSerializeQJson_QObject.cpp | 85 + .../QJson/QxSerializeQJson_QPicture.cpp | 90 + .../QJson/QxSerializeQJson_QPixmap.cpp | 79 + .../QJson/QxSerializeQJson_QPoint.cpp | 74 + .../QJson/QxSerializeQJson_QRect.cpp | 78 + .../QJson/QxSerializeQJson_QRegExp.cpp | 80 + .../QJson/QxSerializeQJson_QRegion.cpp | 73 + .../QJson/QxSerializeQJson_QSize.cpp | 74 + .../QJson/QxSerializeQJson_QSqlError.cpp | 86 + .../QJson/QxSerializeQJson_QStringList.cpp | 83 + .../QJson/QxSerializeQJson_QxInvalidValue.cpp | 95 + .../QxSerializeQJson_QxInvalidValueX.cpp | 79 + .../QJson/QxSerializeQJson_QxSqlQuery.cpp | 249 + .../QJson/QxSerializeQJson_QxTransaction.cpp | 160 + .../QxSerializeQJson_qx_registered_class.cpp | 570 + src/QxSerialize/Qt/QxSerialize_QBrush.cpp | 82 + src/QxSerialize/Qt/QxSerialize_QByteArray.cpp | 75 + src/QxSerialize/Qt/QxSerialize_QColor.cpp | 83 + src/QxSerialize/Qt/QxSerialize_QDate.cpp | 67 + src/QxSerialize/Qt/QxSerialize_QDateTime.cpp | 67 + src/QxSerialize/Qt/QxSerialize_QFont.cpp | 70 + src/QxSerialize/Qt/QxSerialize_QImage.cpp | 90 + src/QxSerialize/Qt/QxSerialize_QMatrix.cpp | 85 + src/QxSerialize/Qt/QxSerialize_QObject.cpp | 85 + src/QxSerialize/Qt/QxSerialize_QPicture.cpp | 93 + src/QxSerialize/Qt/QxSerialize_QPixmap.cpp | 90 + src/QxSerialize/Qt/QxSerialize_QPoint.cpp | 58 + src/QxSerialize/Qt/QxSerialize_QRect.cpp | 79 + src/QxSerialize/Qt/QxSerialize_QRegExp.cpp | 87 + src/QxSerialize/Qt/QxSerialize_QRegion.cpp | 74 + src/QxSerialize/Qt/QxSerialize_QSize.cpp | 58 + src/QxSerialize/Qt/QxSerialize_QSqlError.cpp | 98 + src/QxSerialize/Qt/QxSerialize_QString.cpp | 129 + .../Qt/QxSerialize_QStringList.cpp | 78 + src/QxSerialize/Qt/QxSerialize_QTime.cpp | 67 + src/QxSerialize/Qt/QxSerialize_QUrl.cpp | 67 + src/QxSerialize/Qt/QxSerialize_QUuid.cpp | 68 + src/QxSerialize/Qt/QxSerialize_QVariant.cpp | 463 + .../IxBoostSerializeRegisterHelper.cpp | 57 + .../QxBoostSerializeRegisterHelperX.cpp | 64 + src/QxSerialize/QxSerializeCheckInstance.cpp | 110 + .../boost/QxExportDllBoostArchive.cpp | 211 + src/QxService/IxParameter.cpp | 133 + src/QxService/IxService.cpp | 194 + src/QxService/QxConnect.cpp | 442 + src/QxService/QxServer.cpp | 109 + src/QxService/QxThread.cpp | 405 + src/QxService/QxThreadPool.cpp | 173 + src/QxService/QxTools.cpp | 318 + src/QxService/QxTransaction.cpp | 612 + src/QxSingleton/IxSingleton.cpp | 72 + src/QxSingleton/QxSingletonInit.cpp | 55 + src/QxSingleton/QxSingletonX.cpp | 173 + src/QxTraits/unit_test_is_container.cpp | 85 + src/QxTraits/unit_test_is_smart_ptr.cpp | 145 + src/QxValidator/IxValidator.cpp | 323 + src/QxValidator/IxValidatorX.cpp | 292 + src/QxValidator/QxInvalidValue.cpp | 82 + src/QxValidator/QxInvalidValueX.cpp | 167 + src/QxXml/QxXmlReader.cpp | 139 + src/QxXml/QxXmlWriter.cpp | 84 + src/all.cpp | 209 + src/main.cpp | 66 + test/CMakeLists.txt | 15 + test/_bin/.gitignore | 7 + test/qxBlog/CMakeLists.txt | 56 + test/qxBlog/debug/.gitignore | 7 + test/qxBlog/include/author.h | 33 + test/qxBlog/include/blog.h | 28 + test/qxBlog/include/category.h | 27 + test/qxBlog/include/comment.h | 26 + test/qxBlog/include/export.h | 18 + test/qxBlog/include/precompiled.h | 8 + test/qxBlog/qt/moc/.gitignore | 7 + test/qxBlog/qxBlog.pro | 34 + test/qxBlog/release/.gitignore | 7 + test/qxBlog/src/author.cpp | 28 + test/qxBlog/src/blog.cpp | 20 + test/qxBlog/src/category.cpp | 19 + test/qxBlog/src/comment.cpp | 19 + test/qxBlog/src/main.cpp | 425 + test/qxBlogCompositeKey/CMakeLists.txt | 56 + test/qxBlogCompositeKey/debug/.gitignore | 7 + test/qxBlogCompositeKey/include/author.h | 57 + test/qxBlogCompositeKey/include/blog.h | 48 + test/qxBlogCompositeKey/include/category.h | 52 + test/qxBlogCompositeKey/include/comment.h | 47 + test/qxBlogCompositeKey/include/export.h | 18 + test/qxBlogCompositeKey/include/precompiled.h | 8 + test/qxBlogCompositeKey/qt/moc/.gitignore | 7 + test/qxBlogCompositeKey/qxBlog.pro | 34 + test/qxBlogCompositeKey/release/.gitignore | 7 + test/qxBlogCompositeKey/src/author.cpp | 28 + test/qxBlogCompositeKey/src/blog.cpp | 60 + test/qxBlogCompositeKey/src/category.cpp | 19 + test/qxBlogCompositeKey/src/comment.cpp | 19 + test/qxBlogCompositeKey/src/main.cpp | 225 + test/qxBlogCpp11/CMakeLists.txt | 56 + test/qxBlogCpp11/debug/.gitignore | 7 + test/qxBlogCpp11/include/author.h | 57 + test/qxBlogCpp11/include/blog.h | 48 + test/qxBlogCpp11/include/category.h | 52 + test/qxBlogCpp11/include/comment.h | 47 + test/qxBlogCpp11/include/export.h | 18 + test/qxBlogCpp11/include/precompiled.h | 24 + test/qxBlogCpp11/qt/moc/.gitignore | 7 + test/qxBlogCpp11/qxBlog.pro | 36 + test/qxBlogCpp11/release/.gitignore | 7 + test/qxBlogCpp11/src/author.cpp | 28 + test/qxBlogCpp11/src/blog.cpp | 20 + test/qxBlogCpp11/src/category.cpp | 19 + test/qxBlogCpp11/src/comment.cpp | 19 + test/qxBlogCpp11/src/main.cpp | 209 + test/qxBlogModelView/CMakeLists.txt | 76 + test/qxBlogModelView/debug/.gitignore | 7 + test/qxBlogModelView/include/author.h | 49 + test/qxBlogModelView/include/blog.h | 53 + test/qxBlogModelView/include/category.h | 41 + test/qxBlogModelView/include/comment.h | 39 + test/qxBlogModelView/include/export.h | 18 + .../author.model_view.gen.h | 50 + .../blog.model_view.gen.h | 50 + .../category.model_view.gen.h | 48 + .../comment.model_view.gen.h | 48 + test/qxBlogModelView/include/precompiled.h | 9 + test/qxBlogModelView/qt/moc/.gitignore | 7 + .../qxBlogModelView/qt/rcc/documents/main.qml | 37 + .../qt/rcc/documents/main_qt4.qml | 16 + .../qt/rcc/documents/main_qt6.qml | 37 + .../qt/rcc/documents/main_relationship.qml | 93 + .../rcc/documents/main_relationship_qt6.qml | 93 + test/qxBlogModelView/qt/rcc/images/.gitignore | 7 + .../qt/rcc/qxBlogModelView.qrc | 9 + test/qxBlogModelView/qxBlog.pro | 53 + test/qxBlogModelView/release/.gitignore | 7 + test/qxBlogModelView/src/author.cpp | 52 + test/qxBlogModelView/src/blog.cpp | 53 + test/qxBlogModelView/src/category.cpp | 39 + test/qxBlogModelView/src/comment.cpp | 36 + test/qxBlogModelView/src/main.cpp | 325 + .../author.model_view.gen.cpp | 75 + .../blog.model_view.gen.cpp | 155 + .../category.model_view.gen.cpp | 75 + .../comment.model_view.gen.cpp | 75 + test/qxBlogMongoDB/CMakeLists.txt | 66 + test/qxBlogMongoDB/debug/.gitignore | 7 + test/qxBlogMongoDB/include/TestQtProperty.h | 55 + test/qxBlogMongoDB/include/author.h | 33 + test/qxBlogMongoDB/include/blog.h | 29 + test/qxBlogMongoDB/include/category.h | 28 + test/qxBlogMongoDB/include/comment.h | 27 + test/qxBlogMongoDB/include/export.h | 18 + test/qxBlogMongoDB/include/precompiled.h | 8 + test/qxBlogMongoDB/qt/moc/.gitignore | 7 + test/qxBlogMongoDB/qxBlog.pro | 40 + test/qxBlogMongoDB/release/.gitignore | 7 + test/qxBlogMongoDB/src/TestQtProperty.cpp | 25 + test/qxBlogMongoDB/src/author.cpp | 28 + test/qxBlogMongoDB/src/blog.cpp | 20 + test/qxBlogMongoDB/src/category.cpp | 19 + test/qxBlogMongoDB/src/comment.cpp | 19 + test/qxBlogMongoDB/src/main.cpp | 480 + test/qxBlogPImpl/CMakeLists.txt | 56 + test/qxBlogPImpl/debug/.gitignore | 7 + test/qxBlogPImpl/include/author.h | 48 + test/qxBlogPImpl/include/blog.h | 50 + test/qxBlogPImpl/include/category.h | 46 + test/qxBlogPImpl/include/comment.h | 46 + test/qxBlogPImpl/include/export.h | 18 + test/qxBlogPImpl/include/precompiled.h | 8 + test/qxBlogPImpl/qt/moc/.gitignore | 7 + test/qxBlogPImpl/qxBlog.pro | 34 + test/qxBlogPImpl/release/.gitignore | 7 + test/qxBlogPImpl/src/author.cpp | 75 + test/qxBlogPImpl/src/blog.cpp | 73 + test/qxBlogPImpl/src/category.cpp | 65 + test/qxBlogPImpl/src/comment.cpp | 63 + test/qxBlogPImpl/src/main.cpp | 372 + test/qxBlogRestApi/CMakeLists.txt | 76 + test/qxBlogRestApi/debug/.gitignore | 7 + test/qxBlogRestApi/include/author.h | 34 + test/qxBlogRestApi/include/blog.h | 37 + test/qxBlogRestApi/include/category.h | 28 + test/qxBlogRestApi/include/comment.h | 27 + test/qxBlogRestApi/include/export.h | 18 + test/qxBlogRestApi/include/precompiled.h | 8 + test/qxBlogRestApi/qt/moc/.gitignore | 7 + .../qt/rcc/documents/cert_qxorm_ca.pem | 28 + .../qt/rcc/documents/cert_qxorm_server.crt | 24 + .../qt/rcc/documents/cert_qxorm_server.key | 27 + test/qxBlogRestApi/qt/rcc/documents/jquery.js | 2 + .../qt/rcc/documents/logo_qxorm_and_qxee.png | Bin 0 -> 53095 bytes .../qt/rcc/documents/test_http_server.html | 446 + .../qt/rcc/documents/test_http_server.qml | 40 + .../qt/rcc/documents/test_http_server_qt6.qml | 39 + .../qt/rcc/documents/test_rest_api.qml | 503 + .../qt/rcc/documents/test_rest_api_qt6.qml | 513 + test/qxBlogRestApi/qt/rcc/images/.gitignore | 7 + test/qxBlogRestApi/qt/rcc/qxBlogRestApi.qrc | 14 + test/qxBlogRestApi/qt/rcc/src/.gitignore | 7 + test/qxBlogRestApi/qxBlog.pro | 44 + test/qxBlogRestApi/release/.gitignore | 7 + test/qxBlogRestApi/src/author.cpp | 29 + test/qxBlogRestApi/src/blog.cpp | 43 + test/qxBlogRestApi/src/category.cpp | 20 + test/qxBlogRestApi/src/comment.cpp | 20 + test/qxBlogRestApi/src/main.cpp | 490 + test/qxClientServer/CMakeLists.txt | 18 + test/qxClientServer/qxClient/CMakeLists.txt | 70 + test/qxClientServer/qxClient/debug/.gitignore | 7 + test/qxClientServer/qxClient/include/export.h | 20 + .../qxClient/include/main_dlg.h | 56 + .../qxClient/include/precompiled.h | 9 + .../qxClientServer/qxClient/qt/moc/.gitignore | 7 + .../qxClient/qt/rcc/src/.gitignore | 7 + .../qxClientServer/qxClient/qt/ui/qxClient.ui | 448 + .../qxClient/qt/ui/src/.gitignore | 7 + test/qxClientServer/qxClient/qxClient.pro | 39 + .../qxClient/release/.gitignore | 7 + test/qxClientServer/qxClient/src/main.cpp | 22 + test/qxClientServer/qxClient/src/main_dlg.cpp | 223 + test/qxClientServer/qxServer/CMakeLists.txt | 76 + test/qxClientServer/qxServer/debug/.gitignore | 7 + test/qxClientServer/qxServer/include/export.h | 20 + .../qxServer/include/main_dlg.h | 47 + .../qxServer/include/precompiled.h | 9 + .../qxClientServer/qxServer/qt/moc/.gitignore | 7 + .../qxServer/qt/rcc/_qxServer.qrc | 1 + test/qxClientServer/qxServer/qt/rcc/run.png | Bin 0 -> 1739 bytes test/qxClientServer/qxServer/qt/rcc/stop.png | Bin 0 -> 796 bytes .../qxClientServer/qxServer/qt/ui/qxServer.ui | 325 + .../qxServer/qt/ui/src/.gitignore | 7 + test/qxClientServer/qxServer/qxServer.pro | 41 + .../qxServer/release/.gitignore | 7 + test/qxClientServer/qxServer/src/main.cpp | 22 + test/qxClientServer/qxServer/src/main_dlg.cpp | 132 + .../qxService/debug/debug_client/.gitignore | 7 + .../qxService/debug/debug_server/.gitignore | 7 + .../qxService/include/business_object/user.h | 23 + .../include/business_object/user_search.h | 23 + .../qxService/include/dao/user_manager.h | 25 + .../qxClientServer/qxService/include/export.h | 18 + .../qxService/include/precompiled.h | 9 + .../qxService/include/service/server_infos.h | 34 + .../qxService/include/service/user_service.h | 56 + .../qxService/qt/moc/.gitignore | 7 + .../qxService/qt/rcc/src/.gitignore | 7 + .../qxService/qt/ui/include/.gitignore | 7 + .../qxService/qt/ui/src/.gitignore | 7 + test/qxClientServer/qxService/qxService.pri | 37 + .../qxService/qxServiceClient.pro | 11 + .../qxService/qxServiceClient/CMakeLists.txt | 64 + .../qxService/qxServiceServer.pro | 9 + .../qxService/qxServiceServer/CMakeLists.txt | 64 + .../release/release_client/.gitignore | 7 + .../release/release_server/.gitignore | 7 + .../qxService/src/business_object/user.cpp | 17 + .../src/business_object/user_search.cpp | 15 + .../qxService/src/dao/user_manager.cpp | 90 + test/qxClientServer/qxService/src/main.cpp | 20 + .../qxService/src/service/server_infos.cpp | 42 + .../qxService/src/service/user_service.cpp | 133 + .../qxService/tools/build_debug.bat | 4 + .../qxService/tools/build_release.bat | 4 + .../qxService/tools/clean_debug.bat | 4 + .../qxService/tools/clean_release.bat | 2 + test/qxDllSample/CMakeLists.txt | 7 + test/qxDllSample/dll1/CMakeLists.txt | 53 + test/qxDllSample/dll1/debug/.gitignore | 7 + test/qxDllSample/dll1/dll1.pro | 38 + test/qxDllSample/dll1/include/CPerson.h | 66 + test/qxDllSample/dll1/include/QxPersistable.h | 225 + .../qxDllSample/dll1/include/TestQtProperty.h | 53 + test/qxDllSample/dll1/include/export.h | 26 + test/qxDllSample/dll1/include/precompiled.h | 12 + test/qxDllSample/dll1/qt/moc/.gitignore | 7 + test/qxDllSample/dll1/qt/rcc/src/.gitignore | 7 + .../qxDllSample/dll1/qt/ui/include/.gitignore | 7 + test/qxDllSample/dll1/qt/ui/src/.gitignore | 7 + test/qxDllSample/dll1/qx/CPerson.qx.cpp | 70 + test/qxDllSample/dll1/release/.gitignore | 7 + test/qxDllSample/dll1/src/CPerson.cpp | 31 + test/qxDllSample/dll1/src/QxPersistable.cpp | 132 + test/qxDllSample/dll1/src/TestQtProperty.cpp | 24 + test/qxDllSample/dll1/src/main.cpp | 20 + test/qxDllSample/dll2/CMakeLists.txt | 63 + test/qxDllSample/dll2/debug/.gitignore | 7 + test/qxDllSample/dll2/dll2.pro | 50 + test/qxDllSample/dll2/include/Bar.h | 48 + .../dll2/include/BaseClassTrigger.h | 67 + test/qxDllSample/dll2/include/CTestAll.h | 105 + test/qxDllSample/dll2/include/CUser.h | 54 + test/qxDllSample/dll2/include/Foo.h | 67 + test/qxDllSample/dll2/include/export.h | 28 + test/qxDllSample/dll2/include/precompiled.h | 12 + test/qxDllSample/dll2/qt/moc/.gitignore | 7 + test/qxDllSample/dll2/qt/rcc/src/.gitignore | 7 + .../qxDllSample/dll2/qt/ui/include/.gitignore | 7 + test/qxDllSample/dll2/qt/ui/src/.gitignore | 7 + test/qxDllSample/dll2/qx/Bar.qx.cpp | 23 + .../dll2/qx/BaseClassTrigger.qx.cpp | 20 + test/qxDllSample/dll2/qx/CTestAll.qx.cpp | 75 + test/qxDllSample/dll2/qx/CUser.qx.cpp | 26 + test/qxDllSample/dll2/qx/Foo.qx.cpp | 28 + test/qxDllSample/dll2/release/.gitignore | 7 + test/qxDllSample/dll2/src/Bar.cpp | 12 + .../qxDllSample/dll2/src/BaseClassTrigger.cpp | 21 + test/qxDllSample/dll2/src/CTestAll.cpp | 89 + test/qxDllSample/dll2/src/CUser.cpp | 126 + test/qxDllSample/dll2/src/Foo.cpp | 5 + test/qxDllSample/dll2/src/main.cpp | 20 + test/qxDllSample/exe/CMakeLists.txt | 50 + test/qxDllSample/exe/debug/.gitignore | 7 + test/qxDllSample/exe/exe.pro | 39 + test/qxDllSample/exe/include/export.h | 23 + test/qxDllSample/exe/include/precompiled.h | 14 + test/qxDllSample/exe/qt/moc/.gitignore | 7 + test/qxDllSample/exe/qt/rcc/src/.gitignore | 7 + test/qxDllSample/exe/qt/ui/include/.gitignore | 7 + test/qxDllSample/exe/qt/ui/src/.gitignore | 7 + test/qxDllSample/exe/qx/.gitignore | 7 + test/qxDllSample/exe/release/.gitignore | 7 + test/qxDllSample/exe/src/main.cpp | 555 + tools/_build_debug.bat | 2 + tools/_build_release.bat | 2 + tools/_clean_debug.bat | 6 + tools/_clean_release.bat | 5 + tools/clean_all.bat | 571 + tools/gcc_build_all_debug_full.sh | 160 + tools/gcc_build_all_debug_minimal.sh | 4 + tools/gcc_build_all_release_full.sh | 4 + tools/gcc_build_all_release_minimal.sh | 4 + tools/gitignore_recursive.bat | 1 + tools/gitignore_recursive.file | 7 + tools/gitignore_recursive.vbs | 22 + tools/mingw_build_all_debug_qt4_full.bat | 106 + tools/mingw_build_all_debug_qt4_minimal.bat | 1 + tools/mingw_build_all_debug_qt5_full.bat | 113 + tools/mingw_build_all_debug_qt5_minimal.bat | 1 + tools/mingw_build_all_release_qt4_full.bat | 1 + tools/mingw_build_all_release_qt4_minimal.bat | 1 + tools/mingw_build_all_release_qt5_full.bat | 1 + tools/mingw_build_all_release_qt5_minimal.bat | 1 + tools/msvc2012_build_all_debug_32b_full.bat | 76 + .../msvc2012_build_all_debug_32b_minimal.bat | 1 + tools/msvc2012_build_all_debug_64b_full.bat | 1 + .../msvc2012_build_all_debug_64b_minimal.bat | 1 + tools/msvc2012_build_all_release_32b_full.bat | 1 + ...msvc2012_build_all_release_32b_minimal.bat | 1 + tools/msvc2012_build_all_release_64b_full.bat | 1 + ...msvc2012_build_all_release_64b_minimal.bat | 1 + tools/msvc2015_build_all_debug_64b_full.bat | 23 + .../msvc2015_build_all_debug_64b_minimal.bat | 1 + tools/msvc2015_build_all_release_64b_full.bat | 1 + ...msvc2015_build_all_release_64b_minimal.bat | 1 + tools/msvc2017_build_all_debug_64b_full.bat | 26 + .../msvc2017_build_all_debug_64b_minimal.bat | 1 + tools/msvc2017_build_all_release_64b_full.bat | 1 + ...msvc2017_build_all_release_64b_minimal.bat | 1 + tools/msvc2019_build_all_debug_64b_full.bat | 26 + .../msvc2019_build_all_debug_64b_minimal.bat | 1 + tools/msvc2019_build_all_release_64b_full.bat | 1 + ...msvc2019_build_all_release_64b_minimal.bat | 1 + tools/osx_build_all_debug_full.sh | 179 + tools/osx_build_all_debug_minimal.sh | 3 + tools/osx_build_all_release_full.sh | 3 + tools/osx_build_all_release_minimal.sh | 3 + tools/qxorm.ebuild | 67 + tools/qxorm.spec | 84 + 1142 files changed, 185854 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 XdlOrm.cmake create mode 100644 doc/doxygen/html/.gitignore create mode 100644 doc/doxygen/index.html create mode 100644 doc/doxygen/qxorm.doxygen create mode 100644 doc/index.html create mode 100644 doc/php/add_download.php create mode 100644 doc/php/add_download.php5 create mode 100644 doc/qxentityeditor/resource/qxee_all_views.jpg create mode 100644 doc/qxentityeditor/resource/qxee_cpp_preview.png create mode 100644 doc/qxentityeditor/resource/qxee_database.png create mode 100644 doc/qxentityeditor/resource/qxee_entity_clone.png create mode 100644 doc/qxentityeditor/resource/qxee_entity_colors.png create mode 100644 doc/qxentityeditor/resource/qxee_entity_delete.png create mode 100644 doc/qxentityeditor/resource/qxee_entity_modify.png create mode 100644 doc/qxentityeditor/resource/qxee_entity_new.png create mode 100644 doc/qxentityeditor/resource/qxee_entity_settings.png create mode 100644 doc/qxentityeditor/resource/qxee_enum_colors.png create mode 100644 doc/qxentityeditor/resource/qxee_enum_settings.png create mode 100644 doc/qxentityeditor/resource/qxee_export_cpp.png create mode 100644 doc/qxentityeditor/resource/qxee_export_cpp_model_view.png create mode 100644 doc/qxentityeditor/resource/qxee_export_cpp_model_view_output.png create mode 100644 doc/qxentityeditor/resource/qxee_export_cpp_output.png create mode 100644 doc/qxentityeditor/resource/qxee_export_cpp_services.png create mode 100644 doc/qxentityeditor/resource/qxee_export_cpp_services_output.png create mode 100644 doc/qxentityeditor/resource/qxee_export_cpp_services_server_app.png create mode 100644 doc/qxentityeditor/resource/qxee_export_cpp_services_server_app_output.png create mode 100644 doc/qxentityeditor/resource/qxee_export_ddl.png create mode 100644 doc/qxentityeditor/resource/qxee_export_plugins.png create mode 100644 doc/qxentityeditor/resource/qxee_export_print.png create mode 100644 doc/qxentityeditor/resource/qxee_export_source_control_settings.png create mode 100644 doc/qxentityeditor/resource/qxee_export_xml_json.png create mode 100644 doc/qxentityeditor/resource/qxee_global_settings.png create mode 100644 doc/qxentityeditor/resource/qxee_import_json.png create mode 100644 doc/qxentityeditor/resource/qxee_import_mysql.png create mode 100644 doc/qxentityeditor/resource/qxee_import_odbc.png create mode 100644 doc/qxentityeditor/resource/qxee_import_plugins.png create mode 100644 doc/qxentityeditor/resource/qxee_import_postgresql.png create mode 100644 doc/qxentityeditor/resource/qxee_import_source_control.png create mode 100644 doc/qxentityeditor/resource/qxee_import_sqlite.png create mode 100644 doc/qxentityeditor/resource/qxee_js_debug.png create mode 100644 doc/qxentityeditor/resource/qxee_license.png create mode 100644 doc/qxentityeditor/resource/qxee_linux.png create mode 100644 doc/qxentityeditor/resource/qxee_linux_small.png create mode 100644 doc/qxentityeditor/resource/qxee_logo.png create mode 100644 doc/qxentityeditor/resource/qxee_logo_small.png create mode 100644 doc/qxentityeditor/resource/qxee_macosx.png create mode 100644 doc/qxentityeditor/resource/qxee_macosx_small.png create mode 100644 doc/qxentityeditor/resource/qxee_namespace_colors.png create mode 100644 doc/qxentityeditor/resource/qxee_namespace_menu.png create mode 100644 doc/qxentityeditor/resource/qxee_namespace_rename.png create mode 100644 doc/qxentityeditor/resource/qxee_naming_convention.png create mode 100644 doc/qxentityeditor/resource/qxee_organize_diagram_layout.png create mode 100644 doc/qxentityeditor/resource/qxee_plugin_scripts.png create mode 100644 doc/qxentityeditor/resource/qxee_plugins_about.png create mode 100644 doc/qxentityeditor/resource/qxee_post_it.png create mode 100644 doc/qxentityeditor/resource/qxee_post_it_colors.png create mode 100644 doc/qxentityeditor/resource/qxee_project_list.png create mode 100644 doc/qxentityeditor/resource/qxee_project_new.png create mode 100644 doc/qxentityeditor/resource/qxee_project_settings_01.png create mode 100644 doc/qxentityeditor/resource/qxee_project_settings_02.png create mode 100644 doc/qxentityeditor/resource/qxee_property_settings.png create mode 100644 doc/qxentityeditor/resource/qxee_property_settings_access.png create mode 100644 doc/qxentityeditor/resource/qxee_relation_settings.png create mode 100644 doc/qxentityeditor/resource/qxee_relation_settings_access.png create mode 100644 doc/qxentityeditor/resource/qxee_sample.png create mode 100644 doc/qxentityeditor/resource/qxee_sample_old.png create mode 100644 doc/qxentityeditor/resource/qxee_sample_small.png create mode 100644 doc/qxentityeditor/resource/qxee_sample_small_old.png create mode 100644 doc/qxentityeditor/resource/qxee_source_control_output_directory.png create mode 100644 doc/qxentityeditor/resource/qxee_splitter.png create mode 100644 doc/qxentityeditor/resource/qxee_tag_project_state_01.png create mode 100644 doc/qxentityeditor/resource/qxee_tag_project_state_02.png create mode 100644 doc/qxentityeditor/resource/qxee_windows.png create mode 100644 doc/qxentityeditor/resource/qxee_windows_small.png create mode 100644 doc/qxentityeditor/resource/qxee_zoom.png create mode 100644 doc/qxorm_en/customer.html create mode 100644 doc/qxorm_en/download.html create mode 100644 doc/qxorm_en/download_details.php create mode 100644 doc/qxorm_en/faq.html create mode 100644 doc/qxorm_en/home.html create mode 100644 doc/qxorm_en/link.html create mode 100644 doc/qxorm_en/manual.html create mode 100644 doc/qxorm_en/manual_qxee.html create mode 100644 doc/qxorm_en/model.html create mode 100644 doc/qxorm_en/quick_sample.html create mode 100644 doc/qxorm_en/resource/ES.png create mode 100644 doc/qxorm_en/resource/FR.png create mode 100644 doc/qxorm_en/resource/GB.png create mode 100644 doc/qxorm_en/resource/background.jpg create mode 100644 doc/qxorm_en/resource/download.jpg create mode 100644 doc/qxorm_en/resource/gui_qxClientServer.jpg create mode 100644 doc/qxorm_en/resource/gui_qxClient_01.jpg create mode 100644 doc/qxorm_en/resource/gui_qxClient_02.jpg create mode 100644 doc/qxorm_en/resource/gui_qxServer.jpg create mode 100644 doc/qxorm_en/resource/jquery.min.js create mode 100644 doc/qxorm_en/resource/license.gpl3.txt create mode 100644 doc/qxorm_en/resource/link_download.jpg create mode 100644 doc/qxorm_en/resource/link_faq.jpg create mode 100644 doc/qxorm_en/resource/link_home.jpg create mode 100644 doc/qxorm_en/resource/link_link.jpg create mode 100644 doc/qxorm_en/resource/link_model.jpg create mode 100644 doc/qxorm_en/resource/link_quick_sample.jpg create mode 100644 doc/qxorm_en/resource/link_tutorial.jpg create mode 100644 doc/qxorm_en/resource/logo_boost.jpg create mode 100644 doc/qxorm_en/resource/logo_cpp.gif create mode 100644 doc/qxorm_en/resource/logo_linux.gif create mode 100644 doc/qxorm_en/resource/logo_mac.gif create mode 100644 doc/qxorm_en/resource/logo_qt.jpg create mode 100644 doc/qxorm_en/resource/logo_qxorm.jpg create mode 100644 doc/qxorm_en/resource/logo_qxorm.png create mode 100644 doc/qxorm_en/resource/logo_qxorm_and_qxee.png create mode 100644 doc/qxorm_en/resource/logo_qxorm_and_qxee_old.png create mode 100644 doc/qxorm_en/resource/logo_qxorm_small.jpg create mode 100644 doc/qxorm_en/resource/logo_qxorm_small.png create mode 100644 doc/qxorm_en/resource/logo_windows.gif create mode 100644 doc/qxorm_en/resource/paypal_support_qxorm_library.gif create mode 100644 doc/qxorm_en/resource/qt_ambassador_logo.png create mode 100644 doc/qxorm_en/resource/qt_ambassador_logo_150x150.png create mode 100644 doc/qxorm_en/resource/quick_sample.export_drugs.xml.jpg create mode 100644 doc/qxorm_en/resource/quick_sample.export_drugs.xml.txt create mode 100644 doc/qxorm_en/resource/qxBlog.main.exec.result.txt create mode 100644 doc/qxorm_en/resource/qxBlog.pro.jpg create mode 100644 doc/qxorm_en/resource/qxBlog.qxBlog.pro create mode 100644 doc/qxorm_en/resource/qxBlog.table.author.jpg create mode 100644 doc/qxorm_en/resource/qxBlog.table.category.jpg create mode 100644 doc/qxorm_en/resource/qxBlog.table.comment.jpg create mode 100644 doc/qxorm_en/resource/qxBlog.table.person.jpg create mode 100644 doc/qxorm_en/resource/qxService.pri create mode 100644 doc/qxorm_en/resource/qxServiceClient.pro create mode 100644 doc/qxorm_en/resource/qxServiceServer.pro create mode 100644 doc/qxorm_en/resource/qx_blog_rest_api_http_01.png create mode 100644 doc/qxorm_en/resource/qx_blog_rest_api_qml_01.png create mode 100644 doc/qxorm_en/resource/qx_http_server_apache_bench_test_01.png create mode 100644 doc/qxorm_en/resource/qx_http_server_hello_world_01.png create mode 100644 doc/qxorm_en/resource/qx_model_view_01.png create mode 100644 doc/qxorm_en/resource/qx_model_view_02.png create mode 100644 doc/qxorm_en/resource/qx_persistable_cpp.html create mode 100644 doc/qxorm_en/resource/qx_persistable_hpp.html create mode 100644 doc/qxorm_en/resource/qxclassx_dump_sql_schema.html create mode 100644 doc/qxorm_en/resource/qxorm.namespace.qx.dao.jpg create mode 100644 doc/qxorm_en/resource/qxorm.namespace.qx.jpg create mode 100644 doc/qxorm_en/resource/qxorm.namespace.qx.serialization.jpg create mode 100644 doc/qxorm_en/resource/qxorm.namespace.qx.trait.jpg create mode 100644 doc/qxorm_en/resource/qxorm_cmake.png create mode 100644 doc/qxorm_en/resource/qxorm_script.js create mode 100644 doc/qxorm_en/resource/qxorm_style.css create mode 100644 doc/qxorm_en/resource/tuto_01_qxBlog.jpg create mode 100644 doc/qxorm_en/resource/tuto_02_qxClientServer.jpg create mode 100644 doc/qxorm_en/resource/tuto_03_install_QxOrm.jpg create mode 100644 doc/qxorm_en/resource/tuto_install_qxorm_1.png create mode 100644 doc/qxorm_en/resource/tuto_install_qxorm_10.png create mode 100644 doc/qxorm_en/resource/tuto_install_qxorm_11.png create mode 100644 doc/qxorm_en/resource/tuto_install_qxorm_12.png create mode 100644 doc/qxorm_en/resource/tuto_install_qxorm_13.png create mode 100644 doc/qxorm_en/resource/tuto_install_qxorm_2.png create mode 100644 doc/qxorm_en/resource/tuto_install_qxorm_3.png create mode 100644 doc/qxorm_en/resource/tuto_install_qxorm_4.png create mode 100644 doc/qxorm_en/resource/tuto_install_qxorm_5.png create mode 100644 doc/qxorm_en/resource/tuto_install_qxorm_6.png create mode 100644 doc/qxorm_en/resource/tuto_install_qxorm_7.png create mode 100644 doc/qxorm_en/resource/tuto_install_qxorm_8.png create mode 100644 doc/qxorm_en/resource/tuto_install_qxorm_9.png create mode 100644 doc/qxorm_en/resource/work_in_progress.png create mode 100644 doc/qxorm_en/tutorial.html create mode 100644 doc/qxorm_en/tutorial_2.html create mode 100644 doc/qxorm_en/tutorial_3.html create mode 100644 doc/qxorm_en/tutorial_4.html create mode 100644 doc/qxorm_fr/customer.html create mode 100644 doc/qxorm_fr/download.html create mode 100644 doc/qxorm_fr/download_details.php create mode 100644 doc/qxorm_fr/faq.html create mode 100644 doc/qxorm_fr/home.html create mode 100644 doc/qxorm_fr/link.html create mode 100644 doc/qxorm_fr/manual.html create mode 100644 doc/qxorm_fr/manual_qxee.html create mode 100644 doc/qxorm_fr/model.html create mode 100644 doc/qxorm_fr/quick_sample.html create mode 100644 doc/qxorm_fr/resource/ES.png create mode 100644 doc/qxorm_fr/resource/FR.png create mode 100644 doc/qxorm_fr/resource/GB.png create mode 100644 doc/qxorm_fr/resource/background.jpg create mode 100644 doc/qxorm_fr/resource/download.jpg create mode 100644 doc/qxorm_fr/resource/gui_qxClientServer.jpg create mode 100644 doc/qxorm_fr/resource/gui_qxClient_01.jpg create mode 100644 doc/qxorm_fr/resource/gui_qxClient_02.jpg create mode 100644 doc/qxorm_fr/resource/gui_qxServer.jpg create mode 100644 doc/qxorm_fr/resource/jquery.min.js create mode 100644 doc/qxorm_fr/resource/license.gpl3.txt create mode 100644 doc/qxorm_fr/resource/link_download.jpg create mode 100644 doc/qxorm_fr/resource/link_faq.jpg create mode 100644 doc/qxorm_fr/resource/link_home.jpg create mode 100644 doc/qxorm_fr/resource/link_link.jpg create mode 100644 doc/qxorm_fr/resource/link_model.jpg create mode 100644 doc/qxorm_fr/resource/link_quick_sample.jpg create mode 100644 doc/qxorm_fr/resource/link_tutorial.jpg create mode 100644 doc/qxorm_fr/resource/logo_boost.jpg create mode 100644 doc/qxorm_fr/resource/logo_cpp.gif create mode 100644 doc/qxorm_fr/resource/logo_linux.gif create mode 100644 doc/qxorm_fr/resource/logo_mac.gif create mode 100644 doc/qxorm_fr/resource/logo_qt.jpg create mode 100644 doc/qxorm_fr/resource/logo_qxorm.jpg create mode 100644 doc/qxorm_fr/resource/logo_qxorm.png create mode 100644 doc/qxorm_fr/resource/logo_qxorm_and_qxee.png create mode 100644 doc/qxorm_fr/resource/logo_qxorm_and_qxee_old.png create mode 100644 doc/qxorm_fr/resource/logo_qxorm_small.jpg create mode 100644 doc/qxorm_fr/resource/logo_qxorm_small.png create mode 100644 doc/qxorm_fr/resource/logo_windows.gif create mode 100644 doc/qxorm_fr/resource/paypal_support_qxorm_library.gif create mode 100644 doc/qxorm_fr/resource/qt_ambassador_logo.png create mode 100644 doc/qxorm_fr/resource/qt_ambassador_logo_150x150.png create mode 100644 doc/qxorm_fr/resource/quick_sample.export_drugs.xml.jpg create mode 100644 doc/qxorm_fr/resource/quick_sample.export_drugs.xml.txt create mode 100644 doc/qxorm_fr/resource/qxBlog.main.exec.result.txt create mode 100644 doc/qxorm_fr/resource/qxBlog.pro.jpg create mode 100644 doc/qxorm_fr/resource/qxBlog.qxBlog.pro create mode 100644 doc/qxorm_fr/resource/qxBlog.table.author.jpg create mode 100644 doc/qxorm_fr/resource/qxBlog.table.category.jpg create mode 100644 doc/qxorm_fr/resource/qxBlog.table.comment.jpg create mode 100644 doc/qxorm_fr/resource/qxBlog.table.person.jpg create mode 100644 doc/qxorm_fr/resource/qxService.pri create mode 100644 doc/qxorm_fr/resource/qxServiceClient.pro create mode 100644 doc/qxorm_fr/resource/qxServiceServer.pro create mode 100644 doc/qxorm_fr/resource/qx_blog_rest_api_http_01.png create mode 100644 doc/qxorm_fr/resource/qx_blog_rest_api_qml_01.png create mode 100644 doc/qxorm_fr/resource/qx_http_server_apache_bench_test_01.png create mode 100644 doc/qxorm_fr/resource/qx_http_server_hello_world_01.png create mode 100644 doc/qxorm_fr/resource/qx_model_view_01.png create mode 100644 doc/qxorm_fr/resource/qx_model_view_02.png create mode 100644 doc/qxorm_fr/resource/qx_persistable_cpp.html create mode 100644 doc/qxorm_fr/resource/qx_persistable_hpp.html create mode 100644 doc/qxorm_fr/resource/qxclassx_dump_sql_schema.html create mode 100644 doc/qxorm_fr/resource/qxorm.namespace.qx.dao.jpg create mode 100644 doc/qxorm_fr/resource/qxorm.namespace.qx.jpg create mode 100644 doc/qxorm_fr/resource/qxorm.namespace.qx.serialization.jpg create mode 100644 doc/qxorm_fr/resource/qxorm.namespace.qx.trait.jpg create mode 100644 doc/qxorm_fr/resource/qxorm_cmake.png create mode 100644 doc/qxorm_fr/resource/qxorm_script.js create mode 100644 doc/qxorm_fr/resource/qxorm_style.css create mode 100644 doc/qxorm_fr/resource/tuto_01_qxBlog.jpg create mode 100644 doc/qxorm_fr/resource/tuto_02_qxClientServer.jpg create mode 100644 doc/qxorm_fr/resource/tuto_03_install_QxOrm.jpg create mode 100644 doc/qxorm_fr/resource/tuto_install_qxorm_1.png create mode 100644 doc/qxorm_fr/resource/tuto_install_qxorm_10.png create mode 100644 doc/qxorm_fr/resource/tuto_install_qxorm_11.png create mode 100644 doc/qxorm_fr/resource/tuto_install_qxorm_12.png create mode 100644 doc/qxorm_fr/resource/tuto_install_qxorm_13.png create mode 100644 doc/qxorm_fr/resource/tuto_install_qxorm_2.png create mode 100644 doc/qxorm_fr/resource/tuto_install_qxorm_3.png create mode 100644 doc/qxorm_fr/resource/tuto_install_qxorm_4.png create mode 100644 doc/qxorm_fr/resource/tuto_install_qxorm_5.png create mode 100644 doc/qxorm_fr/resource/tuto_install_qxorm_6.png create mode 100644 doc/qxorm_fr/resource/tuto_install_qxorm_7.png create mode 100644 doc/qxorm_fr/resource/tuto_install_qxorm_8.png create mode 100644 doc/qxorm_fr/resource/tuto_install_qxorm_9.png create mode 100644 doc/qxorm_fr/resource/work_in_progress.png create mode 100644 doc/qxorm_fr/tutorial.html create mode 100644 doc/qxorm_fr/tutorial_2.html create mode 100644 doc/qxorm_fr/tutorial_3.html create mode 100644 doc/qxorm_fr/tutorial_4.html create mode 100644 include/QxCollection/IxCollection.h create mode 100644 include/QxCollection/QxCollection.h create mode 100644 include/QxCollection/QxCollectionIterator.h create mode 100644 include/QxCollection/QxForeach.h create mode 100644 include/QxCommon/QxAny.h create mode 100644 include/QxCommon/QxAnyCastDynamic.h create mode 100644 include/QxCommon/QxBool.h create mode 100644 include/QxCommon/QxCache.h create mode 100644 include/QxCommon/QxConfig.h create mode 100644 include/QxCommon/QxException.h create mode 100644 include/QxCommon/QxExceptionCode.h create mode 100644 include/QxCommon/QxHashValue.h create mode 100644 include/QxCommon/QxMacro.h create mode 100644 include/QxCommon/QxMainPage.h create mode 100644 include/QxCommon/QxPropertyBag.h create mode 100644 include/QxCommon/QxSimpleCrypt.h create mode 100644 include/QxConvert/QxConvert.h create mode 100644 include/QxConvert/QxConvert_Export.h create mode 100644 include/QxConvert/QxConvert_Impl.h create mode 100644 include/QxDao/IxDao_Helper.h create mode 100644 include/QxDao/IxPersistable.h create mode 100644 include/QxDao/IxPersistableCollection.h create mode 100644 include/QxDao/IxPersistableList.h create mode 100644 include/QxDao/IxSqlQueryBuilder.h create mode 100644 include/QxDao/IxSqlRelation.h create mode 100644 include/QxDao/QxDao.h create mode 100644 include/QxDao/QxDaoAsync.h create mode 100644 include/QxDao/QxDaoPointer.h create mode 100644 include/QxDao/QxDaoStrategy.h create mode 100644 include/QxDao/QxDaoThrowable.h create mode 100644 include/QxDao/QxDao_Impl.h create mode 100644 include/QxDao/QxDao_IsDirty.h create mode 100644 include/QxDao/QxDateNeutral.h create mode 100644 include/QxDao/QxDateTimeNeutral.h create mode 100644 include/QxDao/QxMongoDB/QxMongoDB_Helper.h create mode 100644 include/QxDao/QxRepository/IxRepository.h create mode 100644 include/QxDao/QxRepository/QxRepository.h create mode 100644 include/QxDao/QxRepository/QxRepositoryX.h create mode 100644 include/QxDao/QxSession.h create mode 100644 include/QxDao/QxSoftDelete.h create mode 100644 include/QxDao/QxSqlDatabase.h create mode 100644 include/QxDao/QxSqlElement/IxSqlElement.h create mode 100644 include/QxDao/QxSqlElement/QxSqlCompare.h create mode 100644 include/QxDao/QxSqlElement/QxSqlElement.h create mode 100644 include/QxDao/QxSqlElement/QxSqlElementTemp.h create mode 100644 include/QxDao/QxSqlElement/QxSqlEmbedQuery.h create mode 100644 include/QxDao/QxSqlElement/QxSqlExpression.h create mode 100644 include/QxDao/QxSqlElement/QxSqlFreeText.h create mode 100644 include/QxDao/QxSqlElement/QxSqlIn.h create mode 100644 include/QxDao/QxSqlElement/QxSqlIsBetween.h create mode 100644 include/QxDao/QxSqlElement/QxSqlIsNull.h create mode 100644 include/QxDao/QxSqlElement/QxSqlLimit.h create mode 100644 include/QxDao/QxSqlElement/QxSqlSort.h create mode 100644 include/QxDao/QxSqlError.h create mode 100644 include/QxDao/QxSqlGenerator/IxSqlGenerator.h create mode 100644 include/QxDao/QxSqlGenerator/QxSqlGenerator.h create mode 100644 include/QxDao/QxSqlGenerator/QxSqlGenerator_MSSQLServer.h create mode 100644 include/QxDao/QxSqlGenerator/QxSqlGenerator_MySQL.h create mode 100644 include/QxDao/QxSqlGenerator/QxSqlGenerator_Oracle.h create mode 100644 include/QxDao/QxSqlGenerator/QxSqlGenerator_PostgreSQL.h create mode 100644 include/QxDao/QxSqlGenerator/QxSqlGenerator_SQLite.h create mode 100644 include/QxDao/QxSqlGenerator/QxSqlGenerator_Standard.h create mode 100644 include/QxDao/QxSqlJoin.h create mode 100644 include/QxDao/QxSqlQuery.h create mode 100644 include/QxDao/QxSqlQueryBuilder.h create mode 100644 include/QxDao/QxSqlQueryHelper.h create mode 100644 include/QxDao/QxSqlRelation.h create mode 100644 include/QxDao/QxSqlRelationLinked.h create mode 100644 include/QxDao/QxSqlRelationParams.h create mode 100644 include/QxDao/QxSqlRelation_ManyToMany.h create mode 100644 include/QxDao/QxSqlRelation_ManyToOne.h create mode 100644 include/QxDao/QxSqlRelation_OneToMany.h create mode 100644 include/QxDao/QxSqlRelation_OneToOne.h create mode 100644 include/QxDao/QxSqlRelation_RawData.h create mode 100644 include/QxDao/QxSqlSaveMode.h create mode 100644 include/QxDao/QxTimeNeutral.h create mode 100644 include/QxDaoRepository.h create mode 100644 include/QxDataMember/IxDataMember.h create mode 100644 include/QxDataMember/IxDataMemberX.h create mode 100644 include/QxDataMember/QxDataMember.h create mode 100644 include/QxDataMember/QxDataMemberX.h create mode 100644 include/QxDataMember/QxDataMember_PImpl.h create mode 100644 include/QxDataMember/QxDataMember_QObject.h create mode 100644 include/QxExtras/QxBoostOptionalOnly.h create mode 100644 include/QxExtras/QxStdOptional.h create mode 100644 include/QxFactory/IxFactory.h create mode 100644 include/QxFactory/QxFactory.h create mode 100644 include/QxFactory/QxFactoryX.h create mode 100644 include/QxFunction/IxFunction.h create mode 100644 include/QxFunction/QxFunctionError.h create mode 100644 include/QxFunction/QxFunctionInclude.h create mode 100644 include/QxFunction/QxFunctionMacro.h create mode 100644 include/QxFunction/QxFunction_0.h create mode 100644 include/QxFunction/QxFunction_1.h create mode 100644 include/QxFunction/QxFunction_2.h create mode 100644 include/QxFunction/QxFunction_3.h create mode 100644 include/QxFunction/QxFunction_4.h create mode 100644 include/QxFunction/QxFunction_5.h create mode 100644 include/QxFunction/QxFunction_6.h create mode 100644 include/QxFunction/QxFunction_7.h create mode 100644 include/QxFunction/QxFunction_8.h create mode 100644 include/QxFunction/QxFunction_9.h create mode 100644 include/QxFunction/QxParameters.h create mode 100644 include/QxHttpServer/QxHttpCookie.h create mode 100644 include/QxHttpServer/QxHttpRequest.h create mode 100644 include/QxHttpServer/QxHttpResponse.h create mode 100644 include/QxHttpServer/QxHttpServer.h create mode 100644 include/QxHttpServer/QxHttpSession.h create mode 100644 include/QxHttpServer/QxHttpSessionManager.h create mode 100644 include/QxHttpServer/QxHttpTransaction.h create mode 100644 include/QxMemLeak.h create mode 100644 include/QxMemLeak/bool_array.h create mode 100644 include/QxMemLeak/class_level_lock.h create mode 100644 include/QxMemLeak/cont_ptr_utils.h create mode 100644 include/QxMemLeak/debug_new.h create mode 100644 include/QxMemLeak/fast_mutex.h create mode 100644 include/QxMemLeak/fixed_mem_pool.h create mode 100644 include/QxMemLeak/mem_leak.h create mode 100644 include/QxMemLeak/mem_pool_base.h create mode 100644 include/QxMemLeak/object_level_lock.h create mode 100644 include/QxMemLeak/pctimer.h create mode 100644 include/QxMemLeak/set_assign.h create mode 100644 include/QxMemLeak/static_assert.h create mode 100644 include/QxMemLeak/static_mem_pool.h create mode 100644 include/QxModelView.h create mode 100644 include/QxModelView/IxModel.h create mode 100644 include/QxModelView/QxModel.h create mode 100644 include/QxModelView/QxModelRowCompare.h create mode 100644 include/QxModelView/QxModelService.h create mode 100644 include/QxModelView/QxNestedModel.h create mode 100644 include/QxOrm.h create mode 100644 include/QxOrm_Impl.h create mode 100644 include/QxPrecompiled.h create mode 100644 include/QxRegister/IxClass.h create mode 100644 include/QxRegister/IxTypeInfo.h create mode 100644 include/QxRegister/QxClass.h create mode 100644 include/QxRegister/QxClassName.h create mode 100644 include/QxRegister/QxClassX.h create mode 100644 include/QxRegister/QxRegister.h create mode 100644 include/QxRegister/QxRegisterInternalHelper.h create mode 100644 include/QxRegister/QxRegisterQtProperty.h create mode 100644 include/QxRegister/QxVersion.h create mode 100644 include/QxRestApi/QxRestApi.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_QFlags.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_QObject.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_QScopedPointer.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_QSharedPointer.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_QSqlError.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_QWeakPointer.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_all_include.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_optional.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_scoped_ptr.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_shared_ptr.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_tuple.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_unordered_map.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_unordered_set.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_primitive_type.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_qx_registered_class.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_std_list.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_std_map.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_std_pair.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_std_set.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_std_shared_ptr.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_std_string.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_std_tuple.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_std_unique_ptr.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_std_unordered_map.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_std_unordered_set.h create mode 100644 include/QxSerialize/QDataStream/QxSerializeQDataStream_std_vector.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_IxParameter.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_IxPersistable.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_IxService.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_IxSqlElement.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QBrush.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QColor.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QFlags.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QFont.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QHash.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QImage.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QLinkedList.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QList.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QMap.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QMatrix.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QMultiHash.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QMultiMap.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QObject.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QPair.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QPicture.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QPixmap.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QPoint.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QRect.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QRegExp.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QRegion.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QScopedPointer.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QSharedPointer.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QSize.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QSqlError.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QStringList.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QUrl.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QVariantHash.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QVariantMap.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QVector.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QWeakPointer.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QxCollection.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QxDaoPointer.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QxInvalidValue.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QxInvalidValueX.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QxSqlQuery.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_QxTransaction.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_all_include.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_boost_scoped_ptr.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_boost_shared_ptr.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_boost_tuple.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_boost_unordered_map.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_boost_unordered_set.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_qx_registered_class.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_std_list.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_std_map.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_std_pair.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_std_set.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_std_shared_ptr.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_std_tuple.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_std_unique_ptr.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_std_unordered_map.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_std_unordered_set.h create mode 100644 include/QxSerialize/QJson/QxSerializeQJson_std_vector.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QBrush.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QByteArray.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QColor.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QDate.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QDateTime.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QFlags.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QFont.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QHash.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QImage.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QLinkedList.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QList.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QMap.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QMatrix.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QMultiHash.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QMultiMap.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QObject.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QPair.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QPicture.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QPixmap.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QPoint.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QRect.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QRegExp.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QRegion.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QScopedPointer.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QSharedPointer.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QSize.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QSqlError.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QString.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QStringList.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QTime.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QUrl.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QUuid.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QVariant.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QVector.h create mode 100644 include/QxSerialize/Qt/QxSerialize_QWeakPointer.h create mode 100644 include/QxSerialize/Qx/QxSerialize_QxCollection.h create mode 100644 include/QxSerialize/Qx/QxSerialize_QxDaoPointer.h create mode 100644 include/QxSerialize/Qx/QxSerialize_QxXmlReader.h create mode 100644 include/QxSerialize/Qx/QxSerialize_QxXmlWriter.h create mode 100644 include/QxSerialize/QxArchive.h create mode 100644 include/QxSerialize/QxBoostSerializeHelper/IxBoostSerializeRegisterHelper.h create mode 100644 include/QxSerialize/QxBoostSerializeHelper/QxBoostInitGuid.h create mode 100644 include/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeHelper.h create mode 100644 include/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeRegisterHelper.h create mode 100644 include/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeRegisterHelperX.h create mode 100644 include/QxSerialize/QxClone.h create mode 100644 include/QxSerialize/QxDump.h create mode 100644 include/QxSerialize/QxSerialize.h create mode 100644 include/QxSerialize/QxSerializeCheckInstance.h create mode 100644 include/QxSerialize/QxSerializeFastCompil.h create mode 100644 include/QxSerialize/QxSerializeInvoker.h create mode 100644 include/QxSerialize/QxSerializeMacro.h create mode 100644 include/QxSerialize/QxSerializeQDataStream.h create mode 100644 include/QxSerialize/QxSerializeQJson.h create mode 100644 include/QxSerialize/boost/QxExportDllBoostArchive.h create mode 100644 include/QxSerialize/boost/QxExportDllMacroCpp.h create mode 100644 include/QxSerialize/boost/QxExportDllMacroHpp.h create mode 100644 include/QxSerialize/boost/QxImportDllBoostArchive.h create mode 100644 include/QxSerialize/boost/QxSerializeInclude.h create mode 100644 include/QxSerialize/boost/QxSerialize_shared_ptr.h create mode 100644 include/QxSerialize/boost/QxSerialize_tuple.h create mode 100644 include/QxSerialize/boost/QxSerialize_unordered_map.h create mode 100644 include/QxSerialize/boost/QxSerialize_unordered_set.h create mode 100644 include/QxSerialize/boost/class_export/qx_boost_class_export.h create mode 100644 include/QxSerialize/boost/portable_binary/portable_archive_exception.hpp create mode 100644 include/QxSerialize/boost/portable_binary/portable_iarchive.hpp create mode 100644 include/QxSerialize/boost/portable_binary/portable_oarchive.hpp create mode 100644 include/QxSerialize/std/QxSerialize_std_shared_ptr.h create mode 100644 include/QxSerialize/std/QxSerialize_std_tuple.h create mode 100644 include/QxSerialize/std/QxSerialize_std_unique_ptr.h create mode 100644 include/QxSerialize/std/QxSerialize_std_unordered_map.h create mode 100644 include/QxSerialize/std/QxSerialize_std_unordered_set.h create mode 100644 include/QxService/IxParameter.h create mode 100644 include/QxService/IxService.h create mode 100644 include/QxService/QxClientAsync.h create mode 100644 include/QxService/QxConnect.h create mode 100644 include/QxService/QxServer.h create mode 100644 include/QxService/QxService.h create mode 100644 include/QxService/QxThread.h create mode 100644 include/QxService/QxThreadPool.h create mode 100644 include/QxService/QxTools.h create mode 100644 include/QxService/QxTransaction.h create mode 100644 include/QxServices.h create mode 100644 include/QxSingleton/IxSingleton.h create mode 100644 include/QxSingleton/QxSingleton.h create mode 100644 include/QxSingleton/QxSingletonInit.h create mode 100644 include/QxSingleton/QxSingletonX.h create mode 100644 include/QxTraits/archive_printable.h create mode 100644 include/QxTraits/archive_wide_traits.h create mode 100644 include/QxTraits/construct_null_qvariant.h create mode 100644 include/QxTraits/construct_ptr.h create mode 100644 include/QxTraits/generic_container.h create mode 100644 include/QxTraits/get_base_class.h create mode 100644 include/QxTraits/get_class_name.h create mode 100644 include/QxTraits/get_class_name_primitive.h create mode 100644 include/QxTraits/get_primary_key.h create mode 100644 include/QxTraits/get_sql_type.h create mode 100644 include/QxTraits/is_boost_intrusive_ptr.h create mode 100644 include/QxTraits/is_boost_scoped_ptr.h create mode 100644 include/QxTraits/is_boost_shared_ptr.h create mode 100644 include/QxTraits/is_boost_unordered_map.h create mode 100644 include/QxTraits/is_boost_unordered_set.h create mode 100644 include/QxTraits/is_boost_weak_ptr.h create mode 100644 include/QxTraits/is_container.h create mode 100644 include/QxTraits/is_container_base_of.h create mode 100644 include/QxTraits/is_container_key_value.h create mode 100644 include/QxTraits/is_container_to_pod.h create mode 100644 include/QxTraits/is_equal.h create mode 100644 include/QxTraits/is_ptr_base_of.h create mode 100644 include/QxTraits/is_ptr_to_pod.h create mode 100644 include/QxTraits/is_qt_hash.h create mode 100644 include/QxTraits/is_qt_linked_list.h create mode 100644 include/QxTraits/is_qt_list.h create mode 100644 include/QxTraits/is_qt_map.h create mode 100644 include/QxTraits/is_qt_multi_hash.h create mode 100644 include/QxTraits/is_qt_multi_map.h create mode 100644 include/QxTraits/is_qt_scoped_ptr.h create mode 100644 include/QxTraits/is_qt_set.h create mode 100644 include/QxTraits/is_qt_shared_data_ptr.h create mode 100644 include/QxTraits/is_qt_shared_ptr.h create mode 100644 include/QxTraits/is_qt_variant_compatible.h create mode 100644 include/QxTraits/is_qt_vector.h create mode 100644 include/QxTraits/is_qt_weak_ptr.h create mode 100644 include/QxTraits/is_qx_collection.h create mode 100644 include/QxTraits/is_qx_dao_ptr.h create mode 100644 include/QxTraits/is_qx_pod.h create mode 100644 include/QxTraits/is_qx_registered.h create mode 100644 include/QxTraits/is_smart_ptr.h create mode 100644 include/QxTraits/is_smart_ptr_base_of.h create mode 100644 include/QxTraits/is_smart_ptr_to_pod.h create mode 100644 include/QxTraits/is_std_list.h create mode 100644 include/QxTraits/is_std_map.h create mode 100644 include/QxTraits/is_std_set.h create mode 100644 include/QxTraits/is_std_shared_ptr.h create mode 100644 include/QxTraits/is_std_unique_ptr.h create mode 100644 include/QxTraits/is_std_unordered_map.h create mode 100644 include/QxTraits/is_std_unordered_set.h create mode 100644 include/QxTraits/is_std_vector.h create mode 100644 include/QxTraits/is_std_weak_ptr.h create mode 100644 include/QxTraits/is_valid_primary_key.h create mode 100644 include/QxTraits/qt_meta_object.h create mode 100644 include/QxTraits/qx_traits.h create mode 100644 include/QxTraits/remove_attr.h create mode 100644 include/QxTraits/remove_smart_ptr.h create mode 100644 include/QxValidator/IxValidator.h create mode 100644 include/QxValidator/IxValidatorX.h create mode 100644 include/QxValidator/QxInvalidValue.h create mode 100644 include/QxValidator/QxInvalidValueX.h create mode 100644 include/QxValidator/QxValidator.h create mode 100644 include/QxValidator/QxValidatorError.h create mode 100644 include/QxValidator/QxValidatorFct.h create mode 100644 include/QxValidator/QxValidatorX.h create mode 100644 include/QxXml/QxXml.h create mode 100644 include/QxXml/QxXmlReader.h create mode 100644 include/QxXml/QxXmlWriter.h create mode 100644 inl/QxCollection/QxCollection.inl create mode 100644 inl/QxCollection/QxCollectionIterator.inl create mode 100644 inl/QxConvert/QxConvert_FromJson.inl create mode 100644 inl/QxConvert/QxConvert_FromString.inl create mode 100644 inl/QxConvert/QxConvert_FromVariant.inl create mode 100644 inl/QxConvert/QxConvert_Qt.inl create mode 100644 inl/QxConvert/QxConvert_ToJson.inl create mode 100644 inl/QxConvert/QxConvert_ToString.inl create mode 100644 inl/QxConvert/QxConvert_ToVariant.inl create mode 100644 inl/QxConvert/QxConvert_WithIndex.inl create mode 100644 inl/QxDao/QxDao_Count.inl create mode 100644 inl/QxDao/QxDao_CreateTable.inl create mode 100644 inl/QxDao/QxDao_DeleteAll.inl create mode 100644 inl/QxDao/QxDao_DeleteById.inl create mode 100644 inl/QxDao/QxDao_ExecuteQuery.inl create mode 100644 inl/QxDao/QxDao_Exist.inl create mode 100644 inl/QxDao/QxDao_FetchAll.inl create mode 100644 inl/QxDao/QxDao_FetchAll_WithRelation.inl create mode 100644 inl/QxDao/QxDao_FetchById.inl create mode 100644 inl/QxDao/QxDao_FetchById_WithRelation.inl create mode 100644 inl/QxDao/QxDao_Helper.inl create mode 100644 inl/QxDao/QxDao_Helper_Container.inl create mode 100644 inl/QxDao/QxDao_Insert.inl create mode 100644 inl/QxDao/QxDao_Insert_WithRelation.inl create mode 100644 inl/QxDao/QxDao_Save.inl create mode 100644 inl/QxDao/QxDao_Save_WithRelation.inl create mode 100644 inl/QxDao/QxDao_Save_WithRelation_Recursive.inl create mode 100644 inl/QxDao/QxDao_Trigger.inl create mode 100644 inl/QxDao/QxDao_Update.inl create mode 100644 inl/QxDao/QxDao_Update_Optimized.inl create mode 100644 inl/QxDao/QxDao_Update_WithRelation.inl create mode 100644 inl/QxDao/QxSqlQueryHelper_CreateTable.inl create mode 100644 inl/QxDao/QxSqlQueryHelper_DeleteById.inl create mode 100644 inl/QxDao/QxSqlQueryHelper_Exist.inl create mode 100644 inl/QxDao/QxSqlQueryHelper_FetchAll.inl create mode 100644 inl/QxDao/QxSqlQueryHelper_FetchAll_WithRelation.inl create mode 100644 inl/QxDao/QxSqlQueryHelper_FetchById.inl create mode 100644 inl/QxDao/QxSqlQueryHelper_FetchById_WithRelation.inl create mode 100644 inl/QxDao/QxSqlQueryHelper_Insert.inl create mode 100644 inl/QxDao/QxSqlQueryHelper_Update.inl create mode 100644 inl/QxDataMember/QxDataMember.inl create mode 100644 inl/QxDataMember/QxDataMemberX.inl create mode 100644 inl/QxFactory/QxFactory.inl create mode 100644 inl/QxRegister/QxClass.inl create mode 100644 inl/QxSerialize/QxArchive.inl create mode 100644 inl/QxSerialize/QxSerializeInvoker.inl create mode 100644 inl/QxSingleton/QxSingleton.inl create mode 100644 inl/QxXml/QxXml.inl create mode 100644 install-deps-debug.bat create mode 100644 install-deps-release.bat create mode 100644 qt/moc/.gitignore create mode 100644 qt/rcc/src/.gitignore create mode 100644 qt/ui/include/.gitignore create mode 100644 qt/ui/src/.gitignore create mode 100644 src/QxCollection/QxCollection.cpp create mode 100644 src/QxCommon/QxBool.cpp create mode 100644 src/QxCommon/QxCache.cpp create mode 100644 src/QxCommon/QxSimpleCrypt.cpp create mode 100644 src/QxConvert/QxConvert_Export.cpp create mode 100644 src/QxDao/IxDao_Helper.cpp create mode 100644 src/QxDao/IxPersistable.cpp create mode 100644 src/QxDao/IxPersistableCollection.cpp create mode 100644 src/QxDao/IxPersistableList.cpp create mode 100644 src/QxDao/IxSqlQueryBuilder.cpp create mode 100644 src/QxDao/IxSqlRelation.cpp create mode 100644 src/QxDao/QxDaoAsync.cpp create mode 100644 src/QxDao/QxDateNeutral.cpp create mode 100644 src/QxDao/QxDateTimeNeutral.cpp create mode 100644 src/QxDao/QxMongoDB/QxMongoDB_Helper.cpp create mode 100644 src/QxDao/QxRepository/IxRepository.cpp create mode 100644 src/QxDao/QxRepository/QxRepositoryX.cpp create mode 100644 src/QxDao/QxSession.cpp create mode 100644 src/QxDao/QxSoftDelete.cpp create mode 100644 src/QxDao/QxSqlDatabase.cpp create mode 100644 src/QxDao/QxSqlElement/IxSqlElement.cpp create mode 100644 src/QxDao/QxSqlElement/QxSqlCompare.cpp create mode 100644 src/QxDao/QxSqlElement/QxSqlElementTemp.cpp create mode 100644 src/QxDao/QxSqlElement/QxSqlEmbedQuery.cpp create mode 100644 src/QxDao/QxSqlElement/QxSqlExpression.cpp create mode 100644 src/QxDao/QxSqlElement/QxSqlFreeText.cpp create mode 100644 src/QxDao/QxSqlElement/QxSqlIn.cpp create mode 100644 src/QxDao/QxSqlElement/QxSqlIsBetween.cpp create mode 100644 src/QxDao/QxSqlElement/QxSqlIsNull.cpp create mode 100644 src/QxDao/QxSqlElement/QxSqlLimit.cpp create mode 100644 src/QxDao/QxSqlElement/QxSqlSort.cpp create mode 100644 src/QxDao/QxSqlGenerator/IxSqlGenerator.cpp create mode 100644 src/QxDao/QxSqlGenerator/QxSqlGenerator_MSSQLServer.cpp create mode 100644 src/QxDao/QxSqlGenerator/QxSqlGenerator_MySQL.cpp create mode 100644 src/QxDao/QxSqlGenerator/QxSqlGenerator_Oracle.cpp create mode 100644 src/QxDao/QxSqlGenerator/QxSqlGenerator_PostgreSQL.cpp create mode 100644 src/QxDao/QxSqlGenerator/QxSqlGenerator_SQLite.cpp create mode 100644 src/QxDao/QxSqlGenerator/QxSqlGenerator_Standard.cpp create mode 100644 src/QxDao/QxSqlQuery.cpp create mode 100644 src/QxDao/QxSqlRelationLinked.cpp create mode 100644 src/QxDao/QxSqlRelationParams.cpp create mode 100644 src/QxDao/QxTimeNeutral.cpp create mode 100644 src/QxDataMember/IxDataMember.cpp create mode 100644 src/QxDataMember/IxDataMemberX.cpp create mode 100644 src/QxDataMember/QxDataMember_QObject.cpp create mode 100644 src/QxFactory/IxFactory.cpp create mode 100644 src/QxFactory/QxFactoryX.cpp create mode 100644 src/QxHttpServer/QxHttpCookie.cpp create mode 100644 src/QxHttpServer/QxHttpRequest.cpp create mode 100644 src/QxHttpServer/QxHttpResponse.cpp create mode 100644 src/QxHttpServer/QxHttpServer.cpp create mode 100644 src/QxHttpServer/QxHttpSession.cpp create mode 100644 src/QxHttpServer/QxHttpSessionManager.cpp create mode 100644 src/QxHttpServer/QxHttpTransaction.cpp create mode 100644 src/QxMemLeak/bool_array.cpp create mode 100644 src/QxMemLeak/debug_new.cpp create mode 100644 src/QxMemLeak/mem_pool_base.cpp create mode 100644 src/QxMemLeak/static_mem_pool.cpp create mode 100644 src/QxModelView/IxModel.cpp create mode 100644 src/QxModelView/QxModelRowCompare.cpp create mode 100644 src/QxModelView/QxNestedModel.cpp create mode 100644 src/QxRegister/IxClass.cpp create mode 100644 src/QxRegister/QxClassX.cpp create mode 100644 src/QxRestApi/QxRestApi.cpp create mode 100644 src/QxSerialize/QDataStream/QxSerializeQDataStream_QObject.cpp create mode 100644 src/QxSerialize/QDataStream/QxSerializeQDataStream_QSqlError.cpp create mode 100644 src/QxSerialize/QDataStream/QxSerializeQDataStream_primitive_type.cpp create mode 100644 src/QxSerialize/QDataStream/QxSerializeQDataStream_qx_registered_class.cpp create mode 100644 src/QxSerialize/QDataStream/QxSerializeQDataStream_std_string.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_IxService.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_IxSqlElement.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_QBrush.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_QColor.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_QFont.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_QImage.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_QMatrix.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_QObject.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_QPicture.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_QPixmap.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_QPoint.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_QRect.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_QRegExp.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_QRegion.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_QSize.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_QSqlError.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_QStringList.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_QxInvalidValue.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_QxInvalidValueX.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_QxSqlQuery.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_QxTransaction.cpp create mode 100644 src/QxSerialize/QJson/QxSerializeQJson_qx_registered_class.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QBrush.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QByteArray.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QColor.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QDate.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QDateTime.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QFont.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QImage.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QMatrix.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QObject.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QPicture.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QPixmap.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QPoint.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QRect.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QRegExp.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QRegion.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QSize.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QSqlError.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QString.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QStringList.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QTime.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QUrl.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QUuid.cpp create mode 100644 src/QxSerialize/Qt/QxSerialize_QVariant.cpp create mode 100644 src/QxSerialize/QxBoostSerializeHelper/IxBoostSerializeRegisterHelper.cpp create mode 100644 src/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeRegisterHelperX.cpp create mode 100644 src/QxSerialize/QxSerializeCheckInstance.cpp create mode 100644 src/QxSerialize/boost/QxExportDllBoostArchive.cpp create mode 100644 src/QxService/IxParameter.cpp create mode 100644 src/QxService/IxService.cpp create mode 100644 src/QxService/QxConnect.cpp create mode 100644 src/QxService/QxServer.cpp create mode 100644 src/QxService/QxThread.cpp create mode 100644 src/QxService/QxThreadPool.cpp create mode 100644 src/QxService/QxTools.cpp create mode 100644 src/QxService/QxTransaction.cpp create mode 100644 src/QxSingleton/IxSingleton.cpp create mode 100644 src/QxSingleton/QxSingletonInit.cpp create mode 100644 src/QxSingleton/QxSingletonX.cpp create mode 100644 src/QxTraits/unit_test_is_container.cpp create mode 100644 src/QxTraits/unit_test_is_smart_ptr.cpp create mode 100644 src/QxValidator/IxValidator.cpp create mode 100644 src/QxValidator/IxValidatorX.cpp create mode 100644 src/QxValidator/QxInvalidValue.cpp create mode 100644 src/QxValidator/QxInvalidValueX.cpp create mode 100644 src/QxXml/QxXmlReader.cpp create mode 100644 src/QxXml/QxXmlWriter.cpp create mode 100644 src/all.cpp create mode 100644 src/main.cpp create mode 100644 test/CMakeLists.txt create mode 100644 test/_bin/.gitignore create mode 100644 test/qxBlog/CMakeLists.txt create mode 100644 test/qxBlog/debug/.gitignore create mode 100644 test/qxBlog/include/author.h create mode 100644 test/qxBlog/include/blog.h create mode 100644 test/qxBlog/include/category.h create mode 100644 test/qxBlog/include/comment.h create mode 100644 test/qxBlog/include/export.h create mode 100644 test/qxBlog/include/precompiled.h create mode 100644 test/qxBlog/qt/moc/.gitignore create mode 100644 test/qxBlog/qxBlog.pro create mode 100644 test/qxBlog/release/.gitignore create mode 100644 test/qxBlog/src/author.cpp create mode 100644 test/qxBlog/src/blog.cpp create mode 100644 test/qxBlog/src/category.cpp create mode 100644 test/qxBlog/src/comment.cpp create mode 100644 test/qxBlog/src/main.cpp create mode 100644 test/qxBlogCompositeKey/CMakeLists.txt create mode 100644 test/qxBlogCompositeKey/debug/.gitignore create mode 100644 test/qxBlogCompositeKey/include/author.h create mode 100644 test/qxBlogCompositeKey/include/blog.h create mode 100644 test/qxBlogCompositeKey/include/category.h create mode 100644 test/qxBlogCompositeKey/include/comment.h create mode 100644 test/qxBlogCompositeKey/include/export.h create mode 100644 test/qxBlogCompositeKey/include/precompiled.h create mode 100644 test/qxBlogCompositeKey/qt/moc/.gitignore create mode 100644 test/qxBlogCompositeKey/qxBlog.pro create mode 100644 test/qxBlogCompositeKey/release/.gitignore create mode 100644 test/qxBlogCompositeKey/src/author.cpp create mode 100644 test/qxBlogCompositeKey/src/blog.cpp create mode 100644 test/qxBlogCompositeKey/src/category.cpp create mode 100644 test/qxBlogCompositeKey/src/comment.cpp create mode 100644 test/qxBlogCompositeKey/src/main.cpp create mode 100644 test/qxBlogCpp11/CMakeLists.txt create mode 100644 test/qxBlogCpp11/debug/.gitignore create mode 100644 test/qxBlogCpp11/include/author.h create mode 100644 test/qxBlogCpp11/include/blog.h create mode 100644 test/qxBlogCpp11/include/category.h create mode 100644 test/qxBlogCpp11/include/comment.h create mode 100644 test/qxBlogCpp11/include/export.h create mode 100644 test/qxBlogCpp11/include/precompiled.h create mode 100644 test/qxBlogCpp11/qt/moc/.gitignore create mode 100644 test/qxBlogCpp11/qxBlog.pro create mode 100644 test/qxBlogCpp11/release/.gitignore create mode 100644 test/qxBlogCpp11/src/author.cpp create mode 100644 test/qxBlogCpp11/src/blog.cpp create mode 100644 test/qxBlogCpp11/src/category.cpp create mode 100644 test/qxBlogCpp11/src/comment.cpp create mode 100644 test/qxBlogCpp11/src/main.cpp create mode 100644 test/qxBlogModelView/CMakeLists.txt create mode 100644 test/qxBlogModelView/debug/.gitignore create mode 100644 test/qxBlogModelView/include/author.h create mode 100644 test/qxBlogModelView/include/blog.h create mode 100644 test/qxBlogModelView/include/category.h create mode 100644 test/qxBlogModelView/include/comment.h create mode 100644 test/qxBlogModelView/include/export.h create mode 100644 test/qxBlogModelView/include/model_view_from_qxee/author.model_view.gen.h create mode 100644 test/qxBlogModelView/include/model_view_from_qxee/blog.model_view.gen.h create mode 100644 test/qxBlogModelView/include/model_view_from_qxee/category.model_view.gen.h create mode 100644 test/qxBlogModelView/include/model_view_from_qxee/comment.model_view.gen.h create mode 100644 test/qxBlogModelView/include/precompiled.h create mode 100644 test/qxBlogModelView/qt/moc/.gitignore create mode 100644 test/qxBlogModelView/qt/rcc/documents/main.qml create mode 100644 test/qxBlogModelView/qt/rcc/documents/main_qt4.qml create mode 100644 test/qxBlogModelView/qt/rcc/documents/main_qt6.qml create mode 100644 test/qxBlogModelView/qt/rcc/documents/main_relationship.qml create mode 100644 test/qxBlogModelView/qt/rcc/documents/main_relationship_qt6.qml create mode 100644 test/qxBlogModelView/qt/rcc/images/.gitignore create mode 100644 test/qxBlogModelView/qt/rcc/qxBlogModelView.qrc create mode 100644 test/qxBlogModelView/qxBlog.pro create mode 100644 test/qxBlogModelView/release/.gitignore create mode 100644 test/qxBlogModelView/src/author.cpp create mode 100644 test/qxBlogModelView/src/blog.cpp create mode 100644 test/qxBlogModelView/src/category.cpp create mode 100644 test/qxBlogModelView/src/comment.cpp create mode 100644 test/qxBlogModelView/src/main.cpp create mode 100644 test/qxBlogModelView/src/model_view_from_qxee/author.model_view.gen.cpp create mode 100644 test/qxBlogModelView/src/model_view_from_qxee/blog.model_view.gen.cpp create mode 100644 test/qxBlogModelView/src/model_view_from_qxee/category.model_view.gen.cpp create mode 100644 test/qxBlogModelView/src/model_view_from_qxee/comment.model_view.gen.cpp create mode 100644 test/qxBlogMongoDB/CMakeLists.txt create mode 100644 test/qxBlogMongoDB/debug/.gitignore create mode 100644 test/qxBlogMongoDB/include/TestQtProperty.h create mode 100644 test/qxBlogMongoDB/include/author.h create mode 100644 test/qxBlogMongoDB/include/blog.h create mode 100644 test/qxBlogMongoDB/include/category.h create mode 100644 test/qxBlogMongoDB/include/comment.h create mode 100644 test/qxBlogMongoDB/include/export.h create mode 100644 test/qxBlogMongoDB/include/precompiled.h create mode 100644 test/qxBlogMongoDB/qt/moc/.gitignore create mode 100644 test/qxBlogMongoDB/qxBlog.pro create mode 100644 test/qxBlogMongoDB/release/.gitignore create mode 100644 test/qxBlogMongoDB/src/TestQtProperty.cpp create mode 100644 test/qxBlogMongoDB/src/author.cpp create mode 100644 test/qxBlogMongoDB/src/blog.cpp create mode 100644 test/qxBlogMongoDB/src/category.cpp create mode 100644 test/qxBlogMongoDB/src/comment.cpp create mode 100644 test/qxBlogMongoDB/src/main.cpp create mode 100644 test/qxBlogPImpl/CMakeLists.txt create mode 100644 test/qxBlogPImpl/debug/.gitignore create mode 100644 test/qxBlogPImpl/include/author.h create mode 100644 test/qxBlogPImpl/include/blog.h create mode 100644 test/qxBlogPImpl/include/category.h create mode 100644 test/qxBlogPImpl/include/comment.h create mode 100644 test/qxBlogPImpl/include/export.h create mode 100644 test/qxBlogPImpl/include/precompiled.h create mode 100644 test/qxBlogPImpl/qt/moc/.gitignore create mode 100644 test/qxBlogPImpl/qxBlog.pro create mode 100644 test/qxBlogPImpl/release/.gitignore create mode 100644 test/qxBlogPImpl/src/author.cpp create mode 100644 test/qxBlogPImpl/src/blog.cpp create mode 100644 test/qxBlogPImpl/src/category.cpp create mode 100644 test/qxBlogPImpl/src/comment.cpp create mode 100644 test/qxBlogPImpl/src/main.cpp create mode 100644 test/qxBlogRestApi/CMakeLists.txt create mode 100644 test/qxBlogRestApi/debug/.gitignore create mode 100644 test/qxBlogRestApi/include/author.h create mode 100644 test/qxBlogRestApi/include/blog.h create mode 100644 test/qxBlogRestApi/include/category.h create mode 100644 test/qxBlogRestApi/include/comment.h create mode 100644 test/qxBlogRestApi/include/export.h create mode 100644 test/qxBlogRestApi/include/precompiled.h create mode 100644 test/qxBlogRestApi/qt/moc/.gitignore create mode 100644 test/qxBlogRestApi/qt/rcc/documents/cert_qxorm_ca.pem create mode 100644 test/qxBlogRestApi/qt/rcc/documents/cert_qxorm_server.crt create mode 100644 test/qxBlogRestApi/qt/rcc/documents/cert_qxorm_server.key create mode 100644 test/qxBlogRestApi/qt/rcc/documents/jquery.js create mode 100644 test/qxBlogRestApi/qt/rcc/documents/logo_qxorm_and_qxee.png create mode 100644 test/qxBlogRestApi/qt/rcc/documents/test_http_server.html create mode 100644 test/qxBlogRestApi/qt/rcc/documents/test_http_server.qml create mode 100644 test/qxBlogRestApi/qt/rcc/documents/test_http_server_qt6.qml create mode 100644 test/qxBlogRestApi/qt/rcc/documents/test_rest_api.qml create mode 100644 test/qxBlogRestApi/qt/rcc/documents/test_rest_api_qt6.qml create mode 100644 test/qxBlogRestApi/qt/rcc/images/.gitignore create mode 100644 test/qxBlogRestApi/qt/rcc/qxBlogRestApi.qrc create mode 100644 test/qxBlogRestApi/qt/rcc/src/.gitignore create mode 100644 test/qxBlogRestApi/qxBlog.pro create mode 100644 test/qxBlogRestApi/release/.gitignore create mode 100644 test/qxBlogRestApi/src/author.cpp create mode 100644 test/qxBlogRestApi/src/blog.cpp create mode 100644 test/qxBlogRestApi/src/category.cpp create mode 100644 test/qxBlogRestApi/src/comment.cpp create mode 100644 test/qxBlogRestApi/src/main.cpp create mode 100644 test/qxClientServer/CMakeLists.txt create mode 100644 test/qxClientServer/qxClient/CMakeLists.txt create mode 100644 test/qxClientServer/qxClient/debug/.gitignore create mode 100644 test/qxClientServer/qxClient/include/export.h create mode 100644 test/qxClientServer/qxClient/include/main_dlg.h create mode 100644 test/qxClientServer/qxClient/include/precompiled.h create mode 100644 test/qxClientServer/qxClient/qt/moc/.gitignore create mode 100644 test/qxClientServer/qxClient/qt/rcc/src/.gitignore create mode 100644 test/qxClientServer/qxClient/qt/ui/qxClient.ui create mode 100644 test/qxClientServer/qxClient/qt/ui/src/.gitignore create mode 100644 test/qxClientServer/qxClient/qxClient.pro create mode 100644 test/qxClientServer/qxClient/release/.gitignore create mode 100644 test/qxClientServer/qxClient/src/main.cpp create mode 100644 test/qxClientServer/qxClient/src/main_dlg.cpp create mode 100644 test/qxClientServer/qxServer/CMakeLists.txt create mode 100644 test/qxClientServer/qxServer/debug/.gitignore create mode 100644 test/qxClientServer/qxServer/include/export.h create mode 100644 test/qxClientServer/qxServer/include/main_dlg.h create mode 100644 test/qxClientServer/qxServer/include/precompiled.h create mode 100644 test/qxClientServer/qxServer/qt/moc/.gitignore create mode 100644 test/qxClientServer/qxServer/qt/rcc/_qxServer.qrc create mode 100644 test/qxClientServer/qxServer/qt/rcc/run.png create mode 100644 test/qxClientServer/qxServer/qt/rcc/stop.png create mode 100644 test/qxClientServer/qxServer/qt/ui/qxServer.ui create mode 100644 test/qxClientServer/qxServer/qt/ui/src/.gitignore create mode 100644 test/qxClientServer/qxServer/qxServer.pro create mode 100644 test/qxClientServer/qxServer/release/.gitignore create mode 100644 test/qxClientServer/qxServer/src/main.cpp create mode 100644 test/qxClientServer/qxServer/src/main_dlg.cpp create mode 100644 test/qxClientServer/qxService/debug/debug_client/.gitignore create mode 100644 test/qxClientServer/qxService/debug/debug_server/.gitignore create mode 100644 test/qxClientServer/qxService/include/business_object/user.h create mode 100644 test/qxClientServer/qxService/include/business_object/user_search.h create mode 100644 test/qxClientServer/qxService/include/dao/user_manager.h create mode 100644 test/qxClientServer/qxService/include/export.h create mode 100644 test/qxClientServer/qxService/include/precompiled.h create mode 100644 test/qxClientServer/qxService/include/service/server_infos.h create mode 100644 test/qxClientServer/qxService/include/service/user_service.h create mode 100644 test/qxClientServer/qxService/qt/moc/.gitignore create mode 100644 test/qxClientServer/qxService/qt/rcc/src/.gitignore create mode 100644 test/qxClientServer/qxService/qt/ui/include/.gitignore create mode 100644 test/qxClientServer/qxService/qt/ui/src/.gitignore create mode 100644 test/qxClientServer/qxService/qxService.pri create mode 100644 test/qxClientServer/qxService/qxServiceClient.pro create mode 100644 test/qxClientServer/qxService/qxServiceClient/CMakeLists.txt create mode 100644 test/qxClientServer/qxService/qxServiceServer.pro create mode 100644 test/qxClientServer/qxService/qxServiceServer/CMakeLists.txt create mode 100644 test/qxClientServer/qxService/release/release_client/.gitignore create mode 100644 test/qxClientServer/qxService/release/release_server/.gitignore create mode 100644 test/qxClientServer/qxService/src/business_object/user.cpp create mode 100644 test/qxClientServer/qxService/src/business_object/user_search.cpp create mode 100644 test/qxClientServer/qxService/src/dao/user_manager.cpp create mode 100644 test/qxClientServer/qxService/src/main.cpp create mode 100644 test/qxClientServer/qxService/src/service/server_infos.cpp create mode 100644 test/qxClientServer/qxService/src/service/user_service.cpp create mode 100644 test/qxClientServer/qxService/tools/build_debug.bat create mode 100644 test/qxClientServer/qxService/tools/build_release.bat create mode 100644 test/qxClientServer/qxService/tools/clean_debug.bat create mode 100644 test/qxClientServer/qxService/tools/clean_release.bat create mode 100644 test/qxDllSample/CMakeLists.txt create mode 100644 test/qxDllSample/dll1/CMakeLists.txt create mode 100644 test/qxDllSample/dll1/debug/.gitignore create mode 100644 test/qxDllSample/dll1/dll1.pro create mode 100644 test/qxDllSample/dll1/include/CPerson.h create mode 100644 test/qxDllSample/dll1/include/QxPersistable.h create mode 100644 test/qxDllSample/dll1/include/TestQtProperty.h create mode 100644 test/qxDllSample/dll1/include/export.h create mode 100644 test/qxDllSample/dll1/include/precompiled.h create mode 100644 test/qxDllSample/dll1/qt/moc/.gitignore create mode 100644 test/qxDllSample/dll1/qt/rcc/src/.gitignore create mode 100644 test/qxDllSample/dll1/qt/ui/include/.gitignore create mode 100644 test/qxDllSample/dll1/qt/ui/src/.gitignore create mode 100644 test/qxDllSample/dll1/qx/CPerson.qx.cpp create mode 100644 test/qxDllSample/dll1/release/.gitignore create mode 100644 test/qxDllSample/dll1/src/CPerson.cpp create mode 100644 test/qxDllSample/dll1/src/QxPersistable.cpp create mode 100644 test/qxDllSample/dll1/src/TestQtProperty.cpp create mode 100644 test/qxDllSample/dll1/src/main.cpp create mode 100644 test/qxDllSample/dll2/CMakeLists.txt create mode 100644 test/qxDllSample/dll2/debug/.gitignore create mode 100644 test/qxDllSample/dll2/dll2.pro create mode 100644 test/qxDllSample/dll2/include/Bar.h create mode 100644 test/qxDllSample/dll2/include/BaseClassTrigger.h create mode 100644 test/qxDllSample/dll2/include/CTestAll.h create mode 100644 test/qxDllSample/dll2/include/CUser.h create mode 100644 test/qxDllSample/dll2/include/Foo.h create mode 100644 test/qxDllSample/dll2/include/export.h create mode 100644 test/qxDllSample/dll2/include/precompiled.h create mode 100644 test/qxDllSample/dll2/qt/moc/.gitignore create mode 100644 test/qxDllSample/dll2/qt/rcc/src/.gitignore create mode 100644 test/qxDllSample/dll2/qt/ui/include/.gitignore create mode 100644 test/qxDllSample/dll2/qt/ui/src/.gitignore create mode 100644 test/qxDllSample/dll2/qx/Bar.qx.cpp create mode 100644 test/qxDllSample/dll2/qx/BaseClassTrigger.qx.cpp create mode 100644 test/qxDllSample/dll2/qx/CTestAll.qx.cpp create mode 100644 test/qxDllSample/dll2/qx/CUser.qx.cpp create mode 100644 test/qxDllSample/dll2/qx/Foo.qx.cpp create mode 100644 test/qxDllSample/dll2/release/.gitignore create mode 100644 test/qxDllSample/dll2/src/Bar.cpp create mode 100644 test/qxDllSample/dll2/src/BaseClassTrigger.cpp create mode 100644 test/qxDllSample/dll2/src/CTestAll.cpp create mode 100644 test/qxDllSample/dll2/src/CUser.cpp create mode 100644 test/qxDllSample/dll2/src/Foo.cpp create mode 100644 test/qxDllSample/dll2/src/main.cpp create mode 100644 test/qxDllSample/exe/CMakeLists.txt create mode 100644 test/qxDllSample/exe/debug/.gitignore create mode 100644 test/qxDllSample/exe/exe.pro create mode 100644 test/qxDllSample/exe/include/export.h create mode 100644 test/qxDllSample/exe/include/precompiled.h create mode 100644 test/qxDllSample/exe/qt/moc/.gitignore create mode 100644 test/qxDllSample/exe/qt/rcc/src/.gitignore create mode 100644 test/qxDllSample/exe/qt/ui/include/.gitignore create mode 100644 test/qxDllSample/exe/qt/ui/src/.gitignore create mode 100644 test/qxDllSample/exe/qx/.gitignore create mode 100644 test/qxDllSample/exe/release/.gitignore create mode 100644 test/qxDllSample/exe/src/main.cpp create mode 100644 tools/_build_debug.bat create mode 100644 tools/_build_release.bat create mode 100644 tools/_clean_debug.bat create mode 100644 tools/_clean_release.bat create mode 100644 tools/clean_all.bat create mode 100644 tools/gcc_build_all_debug_full.sh create mode 100644 tools/gcc_build_all_debug_minimal.sh create mode 100644 tools/gcc_build_all_release_full.sh create mode 100644 tools/gcc_build_all_release_minimal.sh create mode 100644 tools/gitignore_recursive.bat create mode 100644 tools/gitignore_recursive.file create mode 100644 tools/gitignore_recursive.vbs create mode 100644 tools/mingw_build_all_debug_qt4_full.bat create mode 100644 tools/mingw_build_all_debug_qt4_minimal.bat create mode 100644 tools/mingw_build_all_debug_qt5_full.bat create mode 100644 tools/mingw_build_all_debug_qt5_minimal.bat create mode 100644 tools/mingw_build_all_release_qt4_full.bat create mode 100644 tools/mingw_build_all_release_qt4_minimal.bat create mode 100644 tools/mingw_build_all_release_qt5_full.bat create mode 100644 tools/mingw_build_all_release_qt5_minimal.bat create mode 100644 tools/msvc2012_build_all_debug_32b_full.bat create mode 100644 tools/msvc2012_build_all_debug_32b_minimal.bat create mode 100644 tools/msvc2012_build_all_debug_64b_full.bat create mode 100644 tools/msvc2012_build_all_debug_64b_minimal.bat create mode 100644 tools/msvc2012_build_all_release_32b_full.bat create mode 100644 tools/msvc2012_build_all_release_32b_minimal.bat create mode 100644 tools/msvc2012_build_all_release_64b_full.bat create mode 100644 tools/msvc2012_build_all_release_64b_minimal.bat create mode 100644 tools/msvc2015_build_all_debug_64b_full.bat create mode 100644 tools/msvc2015_build_all_debug_64b_minimal.bat create mode 100644 tools/msvc2015_build_all_release_64b_full.bat create mode 100644 tools/msvc2015_build_all_release_64b_minimal.bat create mode 100644 tools/msvc2017_build_all_debug_64b_full.bat create mode 100644 tools/msvc2017_build_all_debug_64b_minimal.bat create mode 100644 tools/msvc2017_build_all_release_64b_full.bat create mode 100644 tools/msvc2017_build_all_release_64b_minimal.bat create mode 100644 tools/msvc2019_build_all_debug_64b_full.bat create mode 100644 tools/msvc2019_build_all_debug_64b_minimal.bat create mode 100644 tools/msvc2019_build_all_release_64b_full.bat create mode 100644 tools/msvc2019_build_all_release_64b_minimal.bat create mode 100644 tools/osx_build_all_debug_full.sh create mode 100644 tools/osx_build_all_debug_minimal.sh create mode 100644 tools/osx_build_all_release_full.sh create mode 100644 tools/osx_build_all_release_minimal.sh create mode 100644 tools/qxorm.ebuild create mode 100644 tools/qxorm.spec diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fa98179 --- /dev/null +++ b/.gitignore @@ -0,0 +1,122 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local + +/cypress/videos/ +/cypress/screenshots/ + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +/build +/.cache +/Third/QxOrm/lib +**/.vscode/ +**/build + +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* +*.qbs.user* +CMakeLists.txt.user* +CMakeLists.txt.user + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + +# Directories with generated files +.moc/ +.obj/ +.pch/ +.rcc/ +.uic/ +/build*/ +.VSCodeCounter +bin +.vs +CMakeSettings.json +CMakeUserPresets.json \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..efcb118 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,676 @@ +############################################################################# +## +## https://www.qxorm.com/ +## Copyright (C) 2013 XDL Team (ic-east.com) +## +## This file is part of the QxOrm library +## +## This software is provided 'as-is', without any express or implied +## warranty. In no event will the authors be held liable for any +## damages arising from the use of this software +## +## Commercial Usage +## Licensees holding valid commercial QxOrm licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and XDL Team +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3.0 as published by the Free Software +## Foundation and appearing in the file 'license.gpl3.txt' included in the +## packaging of this file. Please review the following information to +## ensure the GNU General Public License version 3.0 requirements will be +## met : http://www.gnu.org/copyleft/gpl.html +## +## If you are unsure which license is appropriate for your use, or +## if you have questions regarding the use of this file, please contact : +## ic-east.com +## +############################################################################# + +cmake_minimum_required(VERSION 3.10) + +project(XdlOrm VERSION 1.5.0 LANGUAGES CXX) + +# Include Conan generated files +if(EXISTS ${CMAKE_BINARY_DIR}/conan_toolchain.cmake) + include(${CMAKE_BINARY_DIR}/conan_toolchain.cmake) +endif() + +# Include QxOrm library configuration file (should be included by all projects which depend on QxOrm) +include(./XdlOrm.cmake) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(QX_HEADERS + ./include/QxPrecompiled.h + ./include/QxMemLeak/bool_array.h + ./include/QxMemLeak/class_level_lock.h + ./include/QxMemLeak/cont_ptr_utils.h + ./include/QxMemLeak/debug_new.h + ./include/QxMemLeak/fast_mutex.h + ./include/QxMemLeak/fixed_mem_pool.h + ./include/QxMemLeak/mem_leak.h + ./include/QxMemLeak/mem_pool_base.h + ./include/QxMemLeak/object_level_lock.h + ./include/QxMemLeak/pctimer.h + ./include/QxMemLeak/set_assign.h + ./include/QxMemLeak/static_assert.h + ./include/QxMemLeak/static_mem_pool.h + ./include/QxSingleton/IxSingleton.h + ./include/QxSingleton/QxSingleton.h + ./include/QxSingleton/QxSingletonX.h + ./include/QxSingleton/QxSingletonInit.h + ./include/QxFactory/IxFactory.h + ./include/QxFactory/QxFactory.h + ./include/QxFactory/QxFactoryX.h + ./include/QxCommon/QxConfig.h + ./include/QxCommon/QxMacro.h + ./include/QxCommon/QxHashValue.h + ./include/QxCommon/QxBool.h + ./include/QxCommon/QxCache.h + ./include/QxCommon/QxPropertyBag.h + ./include/QxCommon/QxSimpleCrypt.h + ./include/QxCommon/QxAnyCastDynamic.h + ./include/QxCommon/QxException.h + ./include/QxCommon/QxExceptionCode.h + ./include/QxCommon/QxAny.h + ./include/QxConvert/QxConvert.h + ./include/QxConvert/QxConvert_Impl.h + ./include/QxConvert/QxConvert_Export.h + ./include/QxRegister/IxClass.h + ./include/QxRegister/QxClass.h + ./include/QxRegister/QxClassX.h + ./include/QxRegister/QxClassName.h + ./include/QxRegister/QxRegister.h + ./include/QxRegister/QxRegisterInternalHelper.h + ./include/QxRegister/IxTypeInfo.h + ./include/QxRegister/QxRegisterQtProperty.h + ./include/QxRegister/QxVersion.h + ./include/QxCollection/IxCollection.h + ./include/QxCollection/QxCollection.h + ./include/QxCollection/QxCollectionIterator.h + ./include/QxCollection/QxForeach.h + ./include/QxDataMember/IxDataMember.h + ./include/QxDataMember/IxDataMemberX.h + ./include/QxDataMember/QxDataMember.h + ./include/QxDataMember/QxDataMemberX.h + ./include/QxDataMember/QxDataMember_QObject.h + ./include/QxDataMember/QxDataMember_PImpl.h + ./include/QxDao/IxSqlQueryBuilder.h + ./include/QxDao/QxSqlQueryBuilder.h + ./include/QxDao/QxSqlQueryHelper.h + ./include/QxDao/QxSqlQuery.h + ./include/QxDao/QxSqlDatabase.h + ./include/QxDao/IxSqlRelation.h + ./include/QxDao/QxSqlRelation.h + ./include/QxDao/QxSqlRelationParams.h + ./include/QxDao/QxSqlRelation_ManyToMany.h + ./include/QxDao/QxSqlRelation_ManyToOne.h + ./include/QxDao/QxSqlRelation_OneToMany.h + ./include/QxDao/QxSqlRelation_OneToOne.h + ./include/QxDao/QxSqlRelation_RawData.h + ./include/QxDao/QxDao.h + ./include/QxDao/QxDao_Impl.h + ./include/QxDao/QxDaoStrategy.h + ./include/QxDao/QxDaoPointer.h + ./include/QxDao/QxDao_IsDirty.h + ./include/QxDao/QxSoftDelete.h + ./include/QxDao/QxSqlError.h + ./include/QxDao/QxSession.h + ./include/QxDao/QxDateNeutral.h + ./include/QxDao/QxTimeNeutral.h + ./include/QxDao/QxDateTimeNeutral.h + ./include/QxDao/IxDao_Helper.h + ./include/QxDao/IxPersistable.h + ./include/QxDao/IxPersistableCollection.h + ./include/QxDao/IxPersistableList.h + ./include/QxDao/QxSqlJoin.h + ./include/QxDao/QxSqlRelationLinked.h + ./include/QxDao/QxDaoAsync.h + ./include/QxDao/QxSqlSaveMode.h + ./include/QxDao/QxDaoThrowable.h + ./include/QxDao/QxSqlElement/IxSqlElement.h + ./include/QxDao/QxSqlElement/QxSqlCompare.h + ./include/QxDao/QxSqlElement/QxSqlElement.h + ./include/QxDao/QxSqlElement/QxSqlElementTemp.h + ./include/QxDao/QxSqlElement/QxSqlEmbedQuery.h + ./include/QxDao/QxSqlElement/QxSqlExpression.h + ./include/QxDao/QxSqlElement/QxSqlFreeText.h + ./include/QxDao/QxSqlElement/QxSqlIn.h + ./include/QxDao/QxSqlElement/QxSqlIsBetween.h + ./include/QxDao/QxSqlElement/QxSqlIsNull.h + ./include/QxDao/QxSqlElement/QxSqlLimit.h + ./include/QxDao/QxSqlElement/QxSqlSort.h + ./include/QxDao/QxSqlGenerator/IxSqlGenerator.h + ./include/QxDao/QxSqlGenerator/QxSqlGenerator.h + ./include/QxDao/QxSqlGenerator/QxSqlGenerator_MySQL.h + ./include/QxDao/QxSqlGenerator/QxSqlGenerator_Oracle.h + ./include/QxDao/QxSqlGenerator/QxSqlGenerator_PostgreSQL.h + ./include/QxDao/QxSqlGenerator/QxSqlGenerator_SQLite.h + ./include/QxDao/QxSqlGenerator/QxSqlGenerator_MSSQLServer.h + ./include/QxDao/QxSqlGenerator/QxSqlGenerator_Standard.h + ./include/QxDao/QxRepository/IxRepository.h + ./include/QxDao/QxRepository/QxRepository.h + ./include/QxDao/QxRepository/QxRepositoryX.h + ./include/QxDao/QxMongoDB/QxMongoDB_Helper.h + ./include/QxSerialize/QxArchive.h + ./include/QxSerialize/QxClone.h + ./include/QxSerialize/QxDump.h + ./include/QxSerialize/QxSerializeFastCompil.h + ./include/QxSerialize/QxSerializeInvoker.h + ./include/QxSerialize/QxSerializeMacro.h + ./include/QxSerialize/QxSerialize.h + ./include/QxSerialize/QxSerializeQDataStream.h + ./include/QxSerialize/QxSerializeCheckInstance.h + ./include/QxSerialize/QxSerializeQJson.h + ./include/QxSerialize/boost/class_export/qx_boost_class_export.h + ./include/QxSerialize/boost/portable_binary/portable_archive_exception.hpp + ./include/QxSerialize/boost/portable_binary/portable_iarchive.hpp + ./include/QxSerialize/boost/portable_binary/portable_oarchive.hpp + ./include/QxSerialize/boost/QxSerializeInclude.h + ./include/QxSerialize/boost/QxSerialize_shared_ptr.h + ./include/QxSerialize/boost/QxSerialize_tuple.h + ./include/QxSerialize/boost/QxSerialize_unordered_map.h + ./include/QxSerialize/boost/QxSerialize_unordered_set.h + ./include/QxSerialize/boost/QxExportDllMacroHpp.h + ./include/QxSerialize/boost/QxExportDllMacroCpp.h + ./include/QxSerialize/boost/QxExportDllBoostArchive.h + ./include/QxSerialize/boost/QxImportDllBoostArchive.h + ./include/QxSerialize/Qt/QxSerialize_QBrush.h + ./include/QxSerialize/Qt/QxSerialize_QByteArray.h + ./include/QxSerialize/Qt/QxSerialize_QColor.h + ./include/QxSerialize/Qt/QxSerialize_QDate.h + ./include/QxSerialize/Qt/QxSerialize_QDateTime.h + ./include/QxSerialize/Qt/QxSerialize_QFont.h + ./include/QxSerialize/Qt/QxSerialize_QHash.h + ./include/QxSerialize/Qt/QxSerialize_QImage.h + ./include/QxSerialize/Qt/QxSerialize_QFlags.h + ./include/QxSerialize/Qt/QxSerialize_QLinkedList.h + ./include/QxSerialize/Qt/QxSerialize_QList.h + ./include/QxSerialize/Qt/QxSerialize_QMap.h + ./include/QxSerialize/Qt/QxSerialize_QMatrix.h + ./include/QxSerialize/Qt/QxSerialize_QObject.h + ./include/QxSerialize/Qt/QxSerialize_QPair.h + ./include/QxSerialize/Qt/QxSerialize_QPicture.h + ./include/QxSerialize/Qt/QxSerialize_QPixmap.h + ./include/QxSerialize/Qt/QxSerialize_QPoint.h + ./include/QxSerialize/Qt/QxSerialize_QRect.h + ./include/QxSerialize/Qt/QxSerialize_QRegExp.h + ./include/QxSerialize/Qt/QxSerialize_QRegion.h + ./include/QxSerialize/Qt/QxSerialize_QScopedPointer.h + ./include/QxSerialize/Qt/QxSerialize_QSharedPointer.h + ./include/QxSerialize/Qt/QxSerialize_QSize.h + ./include/QxSerialize/Qt/QxSerialize_QString.h + ./include/QxSerialize/Qt/QxSerialize_QTime.h + ./include/QxSerialize/Qt/QxSerialize_QUrl.h + ./include/QxSerialize/Qt/QxSerialize_QUuid.h + ./include/QxSerialize/Qt/QxSerialize_QVariant.h + ./include/QxSerialize/Qt/QxSerialize_QVector.h + ./include/QxSerialize/Qt/QxSerialize_QWeakPointer.h + ./include/QxSerialize/Qt/QxSerialize_QMultiHash.h + ./include/QxSerialize/Qt/QxSerialize_QMultiMap.h + ./include/QxSerialize/Qt/QxSerialize_QStringList.h + ./include/QxSerialize/Qt/QxSerialize_QSqlError.h + ./include/QxSerialize/Qx/QxSerialize_QxCollection.h + ./include/QxSerialize/Qx/QxSerialize_QxDaoPointer.h + ./include/QxSerialize/std/QxSerialize_std_tuple.h + ./include/QxSerialize/std/QxSerialize_std_shared_ptr.h + ./include/QxSerialize/std/QxSerialize_std_unique_ptr.h + ./include/QxSerialize/std/QxSerialize_std_unordered_map.h + ./include/QxSerialize/std/QxSerialize_std_unordered_set.h + ./include/QxSerialize/QxBoostSerializeHelper/IxBoostSerializeRegisterHelper.h + ./include/QxSerialize/QxBoostSerializeHelper/QxBoostInitGuid.h + ./include/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeHelper.h + ./include/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeRegisterHelper.h + ./include/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeRegisterHelperX.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_all_include.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_optional.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_scoped_ptr.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_shared_ptr.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_tuple.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_unordered_map.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_unordered_set.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_primitive_type.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_QFlags.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_QObject.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_QScopedPointer.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_QSharedPointer.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_QSqlError.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_QWeakPointer.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_std_list.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_std_map.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_std_pair.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_std_set.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_std_shared_ptr.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_std_string.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_std_tuple.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_std_unique_ptr.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_std_unordered_map.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_std_unordered_set.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_std_vector.h + ./include/QxSerialize/QDataStream/QxSerializeQDataStream_qx_registered_class.h + ./include/QxSerialize/QJson/QxSerializeQJson_all_include.h + ./include/QxSerialize/QJson/QxSerializeQJson_boost_scoped_ptr.h + ./include/QxSerialize/QJson/QxSerializeQJson_boost_shared_ptr.h + ./include/QxSerialize/QJson/QxSerializeQJson_boost_tuple.h + ./include/QxSerialize/QJson/QxSerializeQJson_boost_unordered_map.h + ./include/QxSerialize/QJson/QxSerializeQJson_boost_unordered_set.h + ./include/QxSerialize/QJson/QxSerializeQJson_IxParameter.h + ./include/QxSerialize/QJson/QxSerializeQJson_IxService.h + ./include/QxSerialize/QJson/QxSerializeQJson_IxSqlElement.h + ./include/QxSerialize/QJson/QxSerializeQJson_IxPersistable.h + ./include/QxSerialize/QJson/QxSerializeQJson_QBrush.h + ./include/QxSerialize/QJson/QxSerializeQJson_QColor.h + ./include/QxSerialize/QJson/QxSerializeQJson_QFlags.h + ./include/QxSerialize/QJson/QxSerializeQJson_QFont.h + ./include/QxSerialize/QJson/QxSerializeQJson_QHash.h + ./include/QxSerialize/QJson/QxSerializeQJson_QImage.h + ./include/QxSerialize/QJson/QxSerializeQJson_QLinkedList.h + ./include/QxSerialize/QJson/QxSerializeQJson_QList.h + ./include/QxSerialize/QJson/QxSerializeQJson_QMap.h + ./include/QxSerialize/QJson/QxSerializeQJson_QMatrix.h + ./include/QxSerialize/QJson/QxSerializeQJson_QMultiHash.h + ./include/QxSerialize/QJson/QxSerializeQJson_QMultiMap.h + ./include/QxSerialize/QJson/QxSerializeQJson_QObject.h + ./include/QxSerialize/QJson/QxSerializeQJson_QPair.h + ./include/QxSerialize/QJson/QxSerializeQJson_QPicture.h + ./include/QxSerialize/QJson/QxSerializeQJson_QPixmap.h + ./include/QxSerialize/QJson/QxSerializeQJson_QPoint.h + ./include/QxSerialize/QJson/QxSerializeQJson_QRect.h + ./include/QxSerialize/QJson/QxSerializeQJson_QRegExp.h + ./include/QxSerialize/QJson/QxSerializeQJson_QRegion.h + ./include/QxSerialize/QJson/QxSerializeQJson_QScopedPointer.h + ./include/QxSerialize/QJson/QxSerializeQJson_QSharedPointer.h + ./include/QxSerialize/QJson/QxSerializeQJson_QSize.h + ./include/QxSerialize/QJson/QxSerializeQJson_QSqlError.h + ./include/QxSerialize/QJson/QxSerializeQJson_QStringList.h + ./include/QxSerialize/QJson/QxSerializeQJson_QUrl.h + ./include/QxSerialize/QJson/QxSerializeQJson_QVector.h + ./include/QxSerialize/QJson/QxSerializeQJson_QWeakPointer.h + ./include/QxSerialize/QJson/QxSerializeQJson_QVariantHash.h + ./include/QxSerialize/QJson/QxSerializeQJson_QVariantMap.h + ./include/QxSerialize/QJson/QxSerializeQJson_qx_registered_class.h + ./include/QxSerialize/QJson/QxSerializeQJson_QxCollection.h + ./include/QxSerialize/QJson/QxSerializeQJson_QxDaoPointer.h + ./include/QxSerialize/QJson/QxSerializeQJson_QxInvalidValue.h + ./include/QxSerialize/QJson/QxSerializeQJson_QxInvalidValueX.h + ./include/QxSerialize/QJson/QxSerializeQJson_QxSqlQuery.h + ./include/QxSerialize/QJson/QxSerializeQJson_QxTransaction.h + ./include/QxSerialize/QJson/QxSerializeQJson_std_list.h + ./include/QxSerialize/QJson/QxSerializeQJson_std_map.h + ./include/QxSerialize/QJson/QxSerializeQJson_std_pair.h + ./include/QxSerialize/QJson/QxSerializeQJson_std_set.h + ./include/QxSerialize/QJson/QxSerializeQJson_std_shared_ptr.h + ./include/QxSerialize/QJson/QxSerializeQJson_std_tuple.h + ./include/QxSerialize/QJson/QxSerializeQJson_std_unique_ptr.h + ./include/QxSerialize/QJson/QxSerializeQJson_std_unordered_map.h + ./include/QxSerialize/QJson/QxSerializeQJson_std_unordered_set.h + ./include/QxSerialize/QJson/QxSerializeQJson_std_vector.h + ./include/QxRestApi/QxRestApi.h + ./include/QxTraits/get_base_class.h + ./include/QxTraits/get_primary_key.h + ./include/QxTraits/get_class_name.h + ./include/QxTraits/get_class_name_primitive.h + ./include/QxTraits/is_boost_intrusive_ptr.h + ./include/QxTraits/is_boost_scoped_ptr.h + ./include/QxTraits/is_boost_shared_ptr.h + ./include/QxTraits/is_boost_weak_ptr.h + ./include/QxTraits/is_qt_shared_data_ptr.h + ./include/QxTraits/is_qt_shared_ptr.h + ./include/QxTraits/is_qt_scoped_ptr.h + ./include/QxTraits/is_qt_weak_ptr.h + ./include/QxTraits/is_smart_ptr.h + ./include/QxTraits/is_boost_unordered_map.h + ./include/QxTraits/is_boost_unordered_set.h + ./include/QxTraits/is_container.h + ./include/QxTraits/is_qt_hash.h + ./include/QxTraits/is_qt_linked_list.h + ./include/QxTraits/is_qt_list.h + ./include/QxTraits/is_qt_map.h + ./include/QxTraits/is_qt_multi_hash.h + ./include/QxTraits/is_qt_multi_map.h + ./include/QxTraits/is_qt_set.h + ./include/QxTraits/is_qt_vector.h + ./include/QxTraits/is_qt_variant_compatible.h + ./include/QxTraits/is_qx_collection.h + ./include/QxTraits/is_std_list.h + ./include/QxTraits/is_std_map.h + ./include/QxTraits/is_std_set.h + ./include/QxTraits/is_std_vector.h + ./include/QxTraits/is_container_base_of.h + ./include/QxTraits/is_container_key_value.h + ./include/QxTraits/is_container_to_pod.h + ./include/QxTraits/is_ptr_base_of.h + ./include/QxTraits/is_ptr_to_pod.h + ./include/QxTraits/is_qx_pod.h + ./include/QxTraits/is_qx_registered.h + ./include/QxTraits/is_smart_ptr_base_of.h + ./include/QxTraits/is_smart_ptr_to_pod.h + ./include/QxTraits/archive_wide_traits.h + ./include/QxTraits/archive_printable.h + ./include/QxTraits/remove_attr.h + ./include/QxTraits/remove_smart_ptr.h + ./include/QxTraits/construct_ptr.h + ./include/QxTraits/construct_null_qvariant.h + ./include/QxTraits/get_sql_type.h + ./include/QxTraits/generic_container.h + ./include/QxTraits/is_valid_primary_key.h + ./include/QxTraits/is_qx_dao_ptr.h + ./include/QxTraits/is_equal.h + ./include/QxTraits/qt_meta_object.h + ./include/QxTraits/qx_traits.h + ./include/QxTraits/is_std_shared_ptr.h + ./include/QxTraits/is_std_unique_ptr.h + ./include/QxTraits/is_std_unordered_map.h + ./include/QxTraits/is_std_unordered_set.h + ./include/QxTraits/is_std_weak_ptr.h + ./include/QxFunction/IxFunction.h + ./include/QxFunction/QxFunction_0.h + ./include/QxFunction/QxFunction_1.h + ./include/QxFunction/QxFunction_2.h + ./include/QxFunction/QxFunction_3.h + ./include/QxFunction/QxFunction_4.h + ./include/QxFunction/QxFunction_5.h + ./include/QxFunction/QxFunction_6.h + ./include/QxFunction/QxFunction_7.h + ./include/QxFunction/QxFunction_8.h + ./include/QxFunction/QxFunction_9.h + ./include/QxFunction/QxFunctionError.h + ./include/QxFunction/QxFunctionInclude.h + ./include/QxFunction/QxFunctionMacro.h + ./include/QxFunction/QxParameters.h + ./include/QxService/IxParameter.h + ./include/QxService/IxService.h + ./include/QxService/QxClientAsync.h + ./include/QxService/QxConnect.h + ./include/QxService/QxServer.h + ./include/QxService/QxService.h + ./include/QxService/QxThread.h + ./include/QxService/QxThreadPool.h + ./include/QxService/QxTools.h + ./include/QxService/QxTransaction.h + ./include/QxHttpServer/QxHttpRequest.h + ./include/QxHttpServer/QxHttpResponse.h + ./include/QxHttpServer/QxHttpServer.h + ./include/QxHttpServer/QxHttpTransaction.h + ./include/QxHttpServer/QxHttpCookie.h + ./include/QxHttpServer/QxHttpSession.h + ./include/QxHttpServer/QxHttpSessionManager.h + ./include/QxValidator/IxValidator.h + ./include/QxValidator/IxValidatorX.h + ./include/QxValidator/QxInvalidValue.h + ./include/QxValidator/QxInvalidValueX.h + ./include/QxValidator/QxValidator.h + ./include/QxValidator/QxValidatorError.h + ./include/QxValidator/QxValidatorFct.h + ./include/QxValidator/QxValidatorX.h + ./include/QxModelView/IxModel.h + ./include/QxModelView/QxModel.h + ./include/QxModelView/QxNestedModel.h + ./include/QxModelView/QxModelService.h + ./include/QxModelView/QxModelRowCompare.h + ./include/QxExtras/QxBoostOptionalOnly.h + ./include/QxExtras/QxStdOptional.h + ./include/QxOrm.h + ./include/QxOrm_Impl.h + ./include/QxMemLeak.h + ./include/QxDaoRepository.h + ./include/QxModelView.h + ./include/QxServices.h +) + +# To reduce compilation times, it is recommended to define _QX_UNITY_BUILD compilation option +# More details in QxOrm.cmake configuration file, section 'Unity Build' +if(_QX_UNITY_BUILD) + + set(QX_SRCS + ./src/all.cpp + ) + +else() # _QX_UNITY_BUILD + + set(QX_SRCS + ./src/QxSerialize/QDataStream/QxSerializeQDataStream_primitive_type.cpp + ./src/QxSerialize/QDataStream/QxSerializeQDataStream_QObject.cpp + ./src/QxSerialize/QDataStream/QxSerializeQDataStream_QSqlError.cpp + ./src/QxSerialize/QDataStream/QxSerializeQDataStream_std_string.cpp + ./src/QxSerialize/QDataStream/QxSerializeQDataStream_qx_registered_class.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_qx_registered_class.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_IxService.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_IxSqlElement.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_QBrush.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_QColor.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_QFont.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_QImage.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_QMatrix.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_QObject.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_QPicture.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_QPixmap.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_QPoint.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_QRect.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_QRegExp.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_QRegion.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_QSize.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_QSqlError.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_QStringList.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_QxInvalidValue.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_QxInvalidValueX.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_QxSqlQuery.cpp + ./src/QxSerialize/QJson/QxSerializeQJson_QxTransaction.cpp + ./src/QxMemLeak/bool_array.cpp + ./src/QxMemLeak/debug_new.cpp + ./src/QxMemLeak/mem_pool_base.cpp + ./src/QxMemLeak/static_mem_pool.cpp + ./src/QxSingleton/IxSingleton.cpp + ./src/QxSingleton/QxSingletonX.cpp + ./src/QxSingleton/QxSingletonInit.cpp + ./src/QxFactory/IxFactory.cpp + ./src/QxFactory/QxFactoryX.cpp + ./src/QxCollection/QxCollection.cpp + ./src/QxCommon/QxBool.cpp + ./src/QxDao/QxSession.cpp + ./src/QxDao/IxDao_Helper.cpp + ./src/QxDao/IxPersistable.cpp + ./src/QxDao/IxPersistableCollection.cpp + ./src/QxDao/IxPersistableList.cpp + ./src/QxDao/QxSqlRelationLinked.cpp + ./src/QxCommon/QxCache.cpp + ./src/QxCommon/QxSimpleCrypt.cpp + ./src/QxConvert/QxConvert_Export.cpp + ./src/QxRegister/IxClass.cpp + ./src/QxRegister/QxClassX.cpp + ./src/QxDataMember/IxDataMember.cpp + ./src/QxDataMember/IxDataMemberX.cpp + ./src/QxDataMember/QxDataMember_QObject.cpp + ./src/QxTraits/unit_test_is_smart_ptr.cpp + ./src/QxTraits/unit_test_is_container.cpp + ./src/QxDao/IxSqlQueryBuilder.cpp + ./src/QxDao/QxSqlDatabase.cpp + ./src/QxDao/IxSqlRelation.cpp + ./src/QxDao/QxSqlQuery.cpp + ./src/QxDao/QxDaoAsync.cpp + ./src/QxDao/QxSqlRelationParams.cpp + ./src/QxDao/QxSoftDelete.cpp + ./src/QxDao/QxDateNeutral.cpp + ./src/QxDao/QxDateTimeNeutral.cpp + ./src/QxDao/QxTimeNeutral.cpp + ./src/QxDao/QxSqlElement/IxSqlElement.cpp + ./src/QxDao/QxSqlElement/QxSqlCompare.cpp + ./src/QxDao/QxSqlElement/QxSqlElementTemp.cpp + ./src/QxDao/QxSqlElement/QxSqlEmbedQuery.cpp + ./src/QxDao/QxSqlElement/QxSqlExpression.cpp + ./src/QxDao/QxSqlElement/QxSqlFreeText.cpp + ./src/QxDao/QxSqlElement/QxSqlIn.cpp + ./src/QxDao/QxSqlElement/QxSqlIsBetween.cpp + ./src/QxDao/QxSqlElement/QxSqlIsNull.cpp + ./src/QxDao/QxSqlElement/QxSqlLimit.cpp + ./src/QxDao/QxSqlElement/QxSqlSort.cpp + ./src/QxDao/QxSqlGenerator/IxSqlGenerator.cpp + ./src/QxDao/QxSqlGenerator/QxSqlGenerator_MySQL.cpp + ./src/QxDao/QxSqlGenerator/QxSqlGenerator_Oracle.cpp + ./src/QxDao/QxSqlGenerator/QxSqlGenerator_PostgreSQL.cpp + ./src/QxDao/QxSqlGenerator/QxSqlGenerator_SQLite.cpp + ./src/QxDao/QxSqlGenerator/QxSqlGenerator_MSSQLServer.cpp + ./src/QxDao/QxSqlGenerator/QxSqlGenerator_Standard.cpp + ./src/QxDao/QxRepository/IxRepository.cpp + ./src/QxDao/QxRepository/QxRepositoryX.cpp + ./src/QxDao/QxMongoDB/QxMongoDB_Helper.cpp + ./src/QxSerialize/QxSerializeCheckInstance.cpp + ./src/QxSerialize/QxBoostSerializeHelper/IxBoostSerializeRegisterHelper.cpp + ./src/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeRegisterHelperX.cpp + ./src/QxSerialize/boost/QxExportDllBoostArchive.cpp + ./src/QxSerialize/Qt/QxSerialize_QBrush.cpp + ./src/QxSerialize/Qt/QxSerialize_QByteArray.cpp + ./src/QxSerialize/Qt/QxSerialize_QColor.cpp + ./src/QxSerialize/Qt/QxSerialize_QDate.cpp + ./src/QxSerialize/Qt/QxSerialize_QDateTime.cpp + ./src/QxSerialize/Qt/QxSerialize_QFont.cpp + ./src/QxSerialize/Qt/QxSerialize_QImage.cpp + ./src/QxSerialize/Qt/QxSerialize_QMatrix.cpp + ./src/QxSerialize/Qt/QxSerialize_QObject.cpp + ./src/QxSerialize/Qt/QxSerialize_QPicture.cpp + ./src/QxSerialize/Qt/QxSerialize_QPixmap.cpp + ./src/QxSerialize/Qt/QxSerialize_QPoint.cpp + ./src/QxSerialize/Qt/QxSerialize_QRect.cpp + ./src/QxSerialize/Qt/QxSerialize_QRegExp.cpp + ./src/QxSerialize/Qt/QxSerialize_QRegion.cpp + ./src/QxSerialize/Qt/QxSerialize_QSize.cpp + ./src/QxSerialize/Qt/QxSerialize_QString.cpp + ./src/QxSerialize/Qt/QxSerialize_QStringList.cpp + ./src/QxSerialize/Qt/QxSerialize_QTime.cpp + ./src/QxSerialize/Qt/QxSerialize_QUrl.cpp + ./src/QxSerialize/Qt/QxSerialize_QUuid.cpp + ./src/QxSerialize/Qt/QxSerialize_QVariant.cpp + ./src/QxSerialize/Qt/QxSerialize_QSqlError.cpp + ./src/QxRestApi/QxRestApi.cpp + ./src/QxService/IxParameter.cpp + ./src/QxService/IxService.cpp + ./src/QxService/QxConnect.cpp + ./src/QxService/QxServer.cpp + ./src/QxService/QxThread.cpp + ./src/QxService/QxThreadPool.cpp + ./src/QxService/QxTools.cpp + ./src/QxService/QxTransaction.cpp + ./src/QxHttpServer/QxHttpRequest.cpp + ./src/QxHttpServer/QxHttpResponse.cpp + ./src/QxHttpServer/QxHttpServer.cpp + ./src/QxHttpServer/QxHttpTransaction.cpp + ./src/QxHttpServer/QxHttpCookie.cpp + ./src/QxHttpServer/QxHttpSession.cpp + ./src/QxHttpServer/QxHttpSessionManager.cpp + ./src/QxValidator/IxValidator.cpp + ./src/QxValidator/IxValidatorX.cpp + ./src/QxValidator/QxInvalidValue.cpp + ./src/QxValidator/QxInvalidValueX.cpp + ./src/QxModelView/IxModel.cpp + ./src/QxModelView/QxNestedModel.cpp + ./src/QxModelView/QxModelRowCompare.cpp + ./src/main.cpp + ) + +endif() # _QX_UNITY_BUILD + +set(QX_INLS + ./inl/QxCollection/QxCollection.inl + ./inl/QxCollection/QxCollectionIterator.inl + ./inl/QxConvert/QxConvert_FromString.inl + ./inl/QxConvert/QxConvert_FromVariant.inl + ./inl/QxConvert/QxConvert_FromJson.inl + ./inl/QxConvert/QxConvert_Qt.inl + ./inl/QxConvert/QxConvert_ToString.inl + ./inl/QxConvert/QxConvert_ToVariant.inl + ./inl/QxConvert/QxConvert_ToJson.inl + ./inl/QxConvert/QxConvert_WithIndex.inl + ./inl/QxDao/QxDao_Count.inl + ./inl/QxDao/QxDao_CreateTable.inl + ./inl/QxDao/QxDao_DeleteAll.inl + ./inl/QxDao/QxDao_DeleteById.inl + ./inl/QxDao/QxDao_ExecuteQuery.inl + ./inl/QxDao/QxDao_Exist.inl + ./inl/QxDao/QxDao_FetchAll.inl + ./inl/QxDao/QxDao_FetchAll_WithRelation.inl + ./inl/QxDao/QxDao_FetchById.inl + ./inl/QxDao/QxDao_FetchById_WithRelation.inl + ./inl/QxDao/QxDao_Helper.inl + ./inl/QxDao/QxDao_Helper_Container.inl + ./inl/QxDao/QxDao_Insert.inl + ./inl/QxDao/QxDao_Insert_WithRelation.inl + ./inl/QxDao/QxDao_Save.inl + ./inl/QxDao/QxDao_Save_WithRelation.inl + ./inl/QxDao/QxDao_Save_WithRelation_Recursive.inl + ./inl/QxDao/QxDao_Trigger.inl + ./inl/QxDao/QxDao_Update.inl + ./inl/QxDao/QxDao_Update_Optimized.inl + ./inl/QxDao/QxDao_Update_WithRelation.inl + ./inl/QxDao/QxSqlQueryHelper_CreateTable.inl + ./inl/QxDao/QxSqlQueryHelper_DeleteById.inl + ./inl/QxDao/QxSqlQueryHelper_Exist.inl + ./inl/QxDao/QxSqlQueryHelper_FetchAll.inl + ./inl/QxDao/QxSqlQueryHelper_FetchAll_WithRelation.inl + ./inl/QxDao/QxSqlQueryHelper_FetchById.inl + ./inl/QxDao/QxSqlQueryHelper_FetchById_WithRelation.inl + ./inl/QxDao/QxSqlQueryHelper_Insert.inl + ./inl/QxDao/QxSqlQueryHelper_Update.inl + ./inl/QxDataMember/QxDataMember.inl + ./inl/QxDataMember/QxDataMemberX.inl + ./inl/QxFactory/QxFactory.inl + ./inl/QxRegister/QxClass.inl + ./inl/QxSerialize/QxArchive.inl + ./inl/QxSerialize/QxSerializeInvoker.inl + ./inl/QxSingleton/QxSingleton.inl +) + +if(_QX_STATIC_BUILD) + add_library(XdlOrm STATIC ${QX_SRCS} ${QX_HEADERS} ${QX_INLS}) +else() + add_library(XdlOrm SHARED ${QX_SRCS} ${QX_HEADERS} ${QX_INLS}) +endif() + +target_compile_definitions(XdlOrm PRIVATE -D_QX_BUILDING_QX_ORM) + +target_include_directories(XdlOrm + PUBLIC + $ + $ + $ + $ +) + +if(NOT TARGET xdlorm::xdlorm) + add_library(xdlorm::xdlorm ALIAS XdlOrm) +endif() + +if(COMMAND target_precompile_headers) + target_precompile_headers(XdlOrm PRIVATE ./include/QxPrecompiled.h) +endif() # (COMMAND target_precompile_headers) + +target_link_libraries(XdlOrm ${QX_LIBRARIES}) + +# set_target_properties(XdlOrm PROPERTIES +# ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_SOURCE_DIR}/build/UI/Main +# RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_SOURCE_DIR}/build/UI/Main +# LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib" +# RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib" +# ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/lib" +# LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/lib" +# ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/lib" +# LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/lib" +# RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/lib" +# ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/lib" +# LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/lib" +# RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/lib" +# ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/lib" +# LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/lib" +# RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/lib" +# ) + +# To sort all files by folder in MSVC and XCode solution +qx_auto_source_group("${QX_SRCS};${QX_HEADERS};${QX_INLS}") + +# Define CMAKE_INSTALL_PREFIX variable to put the location where to copy all files +install(TARGETS XdlOrm DESTINATION lib) +install(DIRECTORY ./include/ DESTINATION include) +install(DIRECTORY ./inl/ DESTINATION inl) diff --git a/README.md b/README.md new file mode 100644 index 0000000..647449f --- /dev/null +++ b/README.md @@ -0,0 +1,92 @@ +# QxOrm 库 - 官方仓库 +[QxOrm 库](https://www.qxorm.com/) 是一个为 C++ Qt å¼€å‘者æä¾›çš„**对象关系映射 (ORM)** å’Œ**对象文档映射 (ODM)** æ•°æ®åº“库。主è¦åŠŸèƒ½åŒ…æ‹¬ï¼š +- [æŒä¹…化](https://www.qxorm.com/qxorm_en/manual.html#manual_30)ï¼šæ”¯æŒæœ€å¸¸è§çš„æ•°æ®åº“,如 SQLiteã€MySQLã€PostgreSQLã€Oracleã€MS SQL Serverã€[MongoDB](https://www.qxorm.com/qxorm_en/manual.html#manual_95)ï¼ˆæ”¯æŒ 1-1ã€1-nã€n-1 å’Œ n-n 关系); +- [åºåˆ—化](https://www.qxorm.com/qxorm_en/manual.html#manual_60):JSONã€äºŒè¿›åˆ¶å’Œ XML æ ¼å¼ï¼› +- [åå°„](https://www.qxorm.com/qxorm_en/manual.html#manual_70)(或[内çœ](https://www.qxorm.com/qxorm_en/manual.html#manual_70)):动æ€è®¿é—®ç±»å®šä¹‰ï¼Œæ£€ç´¢å±žæ€§å¹¶è°ƒç”¨ç±»æ–¹æ³•ï¼› +- [HTTP Web æœåС噍](https://www.qxorm.com/qxorm_en/manual.html#manual_96):独立的多线程 HTTP 1.1 Web æœåŠ¡å™¨ï¼ˆæ”¯æŒ SSL/TLSã€æŒä¹…连接ã€Cookieã€ä¼šè¯ã€åˆ†å—å“应ã€URL 调度/路由); +- [JSON API](https://www.qxorm.com/qxorm_en/manual.html#manual_97):与 C++/Qt 以外的其他技术(REST Web æœåŠ¡ã€QML 应用程åºã€è„šæœ¬è¯­è¨€ï¼‰çš„äº’æ“作性。 + +## 文档 +QxOrm 网站æä¾›äº†[用户手册](https://www.qxorm.com/qxorm_en/manual.html)。 + +本文档的目标是æä¾›ä¸€ä¸ªç”¨æˆ·æŒ‡å—,学习如何使用 QxOrm 库的功能。本手册é¢å‘正在寻找解决方案æ¥ç®¡ç† C++/Qt 中æŒä¹…æ•°æ®å±‚的开å‘人员和软件架构师。ç†è§£æœ¬æ–‡æ¡£éœ€è¦ C++ 和数æ®åº“æ–¹é¢çš„æŠ€æœ¯æŠ€èƒ½ã€‚ + +**注æ„:** 本手册/用户指å—中æè¿°çš„æ‰€æœ‰åŠŸèƒ½éƒ½å¯ä»¥é€šè¿‡ **QxEntityEditor 应用程åº**(QxOrm åº“çš„å›¾å½¢ç¼–è¾‘å™¨ã€æ•°æ®æ¨¡åž‹è®¾è®¡å™¨å’Œæºä»£ç ç”Ÿæˆå™¨ï¼‰å¿«é€Ÿè½»æ¾åœ°å®šä¹‰ã€‚QxOrm 网站上æä¾›äº†[专门针对 QxEntityEditor 应用程åºçš„æ–‡æ¡£](https://www.qxorm.com/qxorm_en/manual_qxee.html)。 + +## QxEntityEditor åº”ç”¨ç¨‹åº +[QxEntityEditor](https://www.qxorm.com/qxorm_en/manual_qxee.html) 是 QxOrm 库的图形编辑器:**QxEntityEditor æä¾›äº†ä¸€ç§å›¾å½¢åŒ–æ–¹å¼æ¥ç®¡ç†æ•°æ®æ¨¡åž‹**。 +QxEntityEditor 是多平å°çš„(适用于 Windowsã€Linux å’Œ Mac OS X),并为所有环境生æˆåŽŸç”Ÿä»£ç ï¼šæ¡Œé¢ï¼ˆWindowsã€Linuxã€Mac OS X)ã€åµŒå…¥å¼å’Œç§»åŠ¨è®¾å¤‡ï¼ˆAndroidã€iOSã€Windows Phoneã€Raspberry Pi 等)。 +QxOrm 网站上æä¾›äº†[QxEntityEditor 应用程åºçš„用户手册(文档)](https://www.qxorm.com/qxorm_en/manual_qxee.html)。 + +QxEntityEditor 基于æ’件,æä¾›äº†å¤šç§å¯¼å…¥/å¯¼å‡ºæ•°æ®æ¨¡åž‹çš„æ–¹æ³•: +- [è‡ªåŠ¨ç”Ÿæˆ C++ æŒä¹…ç±»](https://www.qxorm.com/qxorm_en/manual_qxee.html#cpp_export_settings)(在 QxOrm 上下文中注册); +- [è‡ªåŠ¨ç”Ÿæˆ DDL SQL 脚本](https://www.qxorm.com/qxorm_en/manual_qxee.html#ddl_export_settings)(数æ®åº“架构),适用于 SQLiteã€MySQLã€PostgreSQLã€Oracle å’Œ MS SQL Serverï¼› +- ç®¡ç†æ¯ä¸ªé¡¹ç›®ç‰ˆæœ¬çš„æž¶æž„æ¼”å˜ï¼ˆALTER TABLEã€ADD COLUMNã€DROP INDEX 等); +- é€šè¿‡ç½‘ç»œä¼ è¾“æ•°æ®æ¨¡åž‹å¹¶[快速创建客户端/æœåŠ¡å™¨åº”ç”¨ç¨‹åº](https://www.qxorm.com/qxorm_en/manual_qxee.html#cpp_services_export_settings),使用 [QxService 模å—](https://www.qxorm.com/doxygen/html/group___qx_service.html)ï¼› +- [导入现有数æ®åº“结构](https://www.qxorm.com/qxorm_en/manual_qxee.html#wnd_mysql_import)(使用 ODBC 连接或原生驱动程åºï¼‰ï¼Œé€‚用于 SQLiteã€MySQLã€PostgreSQLã€Oracle å’Œ MS SQL Server æ•°æ®åº“ï¼› +- 因为æ¯ä¸ªé¡¹ç›®éƒ½ä¸åŒï¼ŒQxEntityEditor æä¾›äº†å¤šç§æ–¹æ³•æ¥è‡ªå®šä¹‰ç”Ÿæˆçš„æ–‡ä»¶ï¼ˆå°¤å…¶æ˜¯ [JavaScript 引擎和集æˆè°ƒè¯•器](https://www.qxorm.com/qxorm_en/manual_qxee.html#js_engine))。 + +![QxEntityEditor 应用程åº](https://www.qxorm.com/qxentityeditor/resource/qxee_sample_small.png) + +## ä¾èµ–项 +默认情况下,QxOrm 库仅ä¾èµ–于 [Qt 框架](https://www.qt.io/) 中的 [QtCore](https://doc.qt.io/qt-5/qtcore-index.html) å’Œ [QtSql](https://doc.qt.io/qt-5/qtsql-index.html) 库。 +如果å¯ç”¨ [QxOrm HTTP Web æœåС噍](https://www.qxorm.com/qxorm_en/manual.html#manual_96) 功能,那么 QxOrm 库还将ä¾èµ–于 [QtNetwork](https://doc.qt.io/qt-5/qtnetwork-index.html) 库。 +æŸäº›åŠŸèƒ½éœ€è¦ [boost](https://www.boost.org/) ä¾èµ–项(默认ç¦ç”¨ï¼‰ã€‚ + +## 作者 +QxOrm ç”± XDL 团队开å‘,该团队自 2003 年以æ¥ä¸€ç›´æ˜¯è½¯ä»¶å¼€å‘工程师。 + +## 许å¯è¯ +QxOrm 是一个 C++ 库,å¯åœ¨åŒé‡è®¸å¯è¯ä¸‹ä½¿ç”¨ï¼š +- [GNU/GPLv3 许å¯è¯](https://www.qxorm.com/qxorm_en/resource/license.gpl3.txt):开æºä¸”å…费(用于开å‘å¼€æºé¡¹ç›®æˆ–评估 QxOrm 库); +- [专有许å¯è¯](https://www.qxorm.com/qxorm_en/download_details.php),用于分å‘基于 QxOrm 库的应用程åºï¼Œä¸å— GNU/GPLv3 许å¯è¯çš„任何é™åˆ¶ã€‚ + +## Conan 打包方法 +### 安装 Conan +é¦–å…ˆï¼Œç¡®ä¿æ‚¨å·²å®‰è£… Conan 包管ç†å™¨ã€‚如果尚未安装,å¯ä»¥ä»Ž [Conan 官方网站](https://conan.io/downloads.html) 下载并安装。 + +### 构建和安装包 +1. 克隆仓库并进入项目目录: + ```bash + git clone <仓库地å€> + cd XdlOrm + ``` + +2. 构建并安装包到本地 Conan 缓存: + ```bash + conan create . --profile:build default --profile:host default + ``` + +3. 或者,直接在项目目录中构建: + ```bash + conan build . --profile:build default --profile:host default + ``` + +### 在项目中使用 +在您的项目的 `conanfile.py` 中添加ä¾èµ–: +```python +requires = [ + "xdlorm/1.0.0" +] +``` + +或者,使用 `conan install` 命令安装: +```bash +conan install . --build=missing +``` + +### é…置选项 +QxOrm 库æä¾›äº†ä»¥ä¸‹ Conan 选项: +- `shared`ï¼šæ˜¯å¦æž„建为共享库(默认:False) +- `enable_boost`:是å¦å¯ç”¨ Boost 支æŒï¼ˆé»˜è®¤ï¼šFalse) +- `enable_boost_serialization`:是å¦å¯ç”¨ Boost åºåˆ—化(默认:False) +- `enable_qt_gui`:是å¦å¯ç”¨ Qt GUI 支æŒï¼ˆé»˜è®¤ï¼šFalse) +- `enable_qt_network`:是å¦å¯ç”¨ Qt Network 支æŒï¼ˆé»˜è®¤ï¼šFalse) +- `enable_mongodb`:是å¦å¯ç”¨ MongoDB 支æŒï¼ˆé»˜è®¤ï¼šFalse) +- `no_json`:是å¦ç¦ç”¨ JSON åºåˆ—化(默认:False) +- `unity_build`:是å¦å¯ç”¨ Unity 构建(默认:False) + +例如,构建共享库并å¯ç”¨ Boost 支æŒï¼š +```bash +conan create . --profile:build default --profile:host default -o xdlorm:shared=True -o xdlorm:enable_boost=True +``` \ No newline at end of file diff --git a/XdlOrm.cmake b/XdlOrm.cmake new file mode 100644 index 0000000..5e05625 --- /dev/null +++ b/XdlOrm.cmake @@ -0,0 +1,384 @@ +############################################################################# +## +## https://www.qxorm.com/ +## Copyright (C) 2013 XDL Team (ic-east.com) +## +## This file is part of the QxOrm library +## +## This software is provided 'as-is', without any express or implied +## warranty. In no event will the authors be held liable for any +## damages arising from the use of this software +## +## Commercial Usage +## Licensees holding valid commercial QxOrm licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and XDL Team +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3.0 as published by the Free Software +## Foundation and appearing in the file 'license.gpl3.txt' included in the +## packaging of this file. Please review the following information to +## ensure the GNU General Public License version 3.0 requirements will be +## met : http://www.gnu.org/copyleft/gpl.html +## +## If you are unsure which license is appropriate for your use, or +## if you have questions regarding the use of this file, please contact : +## ic-east.com +## +############################################################################# + +# QxOrm.cmake is the configuration file for QxOrm library if you are using CMake as build tool +# QxOrm.cmake file is equivalent to QxOrm.pri configuration file for qmake projects +# QxOrm.cmake file should be included by all projects which depend on QxOrm library +# QxOrm.cmake file contains some settings to enable/disable QxOrm library features +# QxOrm.cmake file also tries to find Qt and boost libraries required by QxOrm library + +if(NOT QXORM_CMAKE_CONFIG_FILE_INCLUDED) + set(QXORM_CMAKE_CONFIG_FILE_INCLUDED TRUE) + + ########################################### + # QxOrm library options / features # + ########################################### + + option(_QX_ENABLE_BOOST "If you enable _QX_ENABLE_BOOST option, then QxOrm library will support some boost header-only classes like boost::optional, boost::shared_ptr, boost::unordered_map, etc..." OFF) + option(_QX_ENABLE_QT_GUI "If you enable _QX_ENABLE_QT_GUI option, then QxOrm library will be able to serialize Qt Gui objects (QBrush, QColor, QFont, QImage, QMatrix, QPicture, QPixmap, QRegion)" OFF) + option(_QX_ENABLE_QT_NETWORK "If you enable _QX_ENABLE_QT_NETWORK option, then QxService and QxHttpServer modules of QxOrm library will be available (network transactions to transfer persistent data layer + standalone multi-threaded HTTP 1.1 web server)" OFF) + option(_QX_ENABLE_MONGODB "If you enable _QX_ENABLE_MONGODB option, then QxOrm library will be able to use mongoc driver to store all QxOrm registered classes in a MongoDB database" OFF) + option(_QX_STATIC_BUILD "Enable _QX_STATIC_BUILD option to build QxOrm as a static library" OFF) + option(_QX_NO_JSON "If you enable _QX_NO_JSON option, then QxOrm library will not provide JSON serialization engine" OFF) + option(_QX_UNITY_BUILD "If you enable _QX_UNITY_BUILD option, then QxOrm library will be built faster (using only 1 'all.cpp' source file)" OFF) + option(_QX_USE_QSTRINGBUILDER "If you enable _QX_USE_QSTRINGBUILDER option, then QxOrm library will define QT_USE_QSTRINGBUILDER compilation option of Qt library to provide some optimizations with QString class" OFF) + + ########################################### + # QxOrm library requires a C++11 compiler # + ########################################### + + # Qt framework requires a C++11 compiler since version Qt 5.7 + # So for all previous Qt versions, we need to define CONFIG += c++11 + # Please note that QxOrm library doesn't require a full compliant C++11 compiler : for example, QxOrm library can be built and used with MSVC 2012, GCC 4.5 or Clang 3.2 + + set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + + ###################### + # QxOrm Library Path # + ###################### + + set(QXORM_DIR ${CMAKE_CURRENT_LIST_DIR}) + set(QXORM_INCLUDE_DIR ${QXORM_DIR}/include) + include_directories(${QXORM_INCLUDE_DIR}) + + ########################################### + # Boost Header-Only Dependency (optional) # + ########################################### + + # Since QxOrm 1.4.4, QxOrm library doesn't depend on boost framework anymore (the boost dependency has been fully removed, replaced by some C++11 features) + # So QxOrm library is now a pure Qt library which depends only on QtCore and QtSql by default + # QxOrm library still supports some boost classes (boost smart-pointers, unordered containers, boost::optional, etc...) : you have to define _QX_ENABLE_BOOST compilation option to enable these features + + ###################################### + # Boost Library Configuration / Path # + ###################################### + + if(_QX_ENABLE_BOOST) + + add_definitions(-D_QX_ENABLE_BOOST) + set(QX_BOOST_DIR "" CACHE STRING "Define where boost library is located, or leave QX_BOOST_DIR parameter empty and define a BOOST_ROOT environment variable") + + if(NOT QX_BOOST_DIR STREQUAL "") + set(BOOST_ROOT ${QX_BOOST_DIR}) + endif() + + find_package(Boost 1.38.0 REQUIRED) + + if(NOT Boost_FOUND) + message(FATAL_ERROR "boost library not found : please define where boost library is located using QX_BOOST_DIR parameter, or leave QX_BOOST_DIR parameter empty and define a BOOST_ROOT environment variable") + endif() + + include_directories(${Boost_INCLUDE_DIRS}) + + endif() # _QX_ENABLE_BOOST + + ########################### + # Qt Library Dependencies # + ########################### + + # Use Qt from Conan or system + set(QX_QT_DIR "" CACHE STRING "Define where Qt library is located if CMake doesn't find it by default") + + if(NOT QX_QT_DIR STREQUAL "") + set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${QX_QT_DIR}) + endif() + + # Find Qt (either Qt6 or Qt5) + find_package(Qt6 QUIET COMPONENTS Core) + if(NOT Qt6_FOUND) + find_package(Qt5 COMPONENTS Core REQUIRED) + set(QT_VERSION_MAJOR 5) + else() + set(QT_VERSION_MAJOR 6) + endif() + + if(_QX_ENABLE_QT_GUI AND _QX_ENABLE_QT_NETWORK) + find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Sql Gui Network REQUIRED) + set(QX_LIBRARIES Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Sql Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Network) + elseif(_QX_ENABLE_QT_GUI) + find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Sql Gui REQUIRED) + set(QX_LIBRARIES Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Sql Qt${QT_VERSION_MAJOR}::Gui) + elseif(_QX_ENABLE_QT_NETWORK) + find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Sql Network REQUIRED) + set(QX_LIBRARIES Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Sql Qt${QT_VERSION_MAJOR}::Network) + else() # (_QX_ENABLE_QT_GUI AND _QX_ENABLE_QT_NETWORK) + find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Sql REQUIRED) + set(QX_LIBRARIES Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Sql) + endif() # (_QX_ENABLE_QT_GUI AND _QX_ENABLE_QT_NETWORK) + + if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + endif() # (${QT_VERSION_MAJOR} GREATER_EQUAL 6) + + ####################################### + # MongoDB Driver Library Dependencies # + ####################################### + + if(_QX_ENABLE_MONGODB) + + add_definitions(-D_QX_ENABLE_MONGODB) + + set(QX_MONGOC_INCLUDE "$ENV{MONGOC_INCLUDE}") + if(NOT QX_MONGOC_INCLUDE STREQUAL "") + + include_directories("$ENV{BSON_INCLUDE}") + include_directories("${QX_MONGOC_INCLUDE}") + + link_directories("$ENV{BSON_LIB}") + link_directories("$ENV{MONGOC_LIB}") + + set(QX_LIBRARIES ${QX_LIBRARIES} bson-1.0 mongoc-1.0) + + else() # (NOT QX_MONGOC_INCLUDE STREQUAL "") + + find_package(libbson-1.0 REQUIRED) + find_package(libmongoc-1.0 REQUIRED) + + include_directories(${BSON_INCLUDE_DIRS}) + include_directories(${MONGOC_INCLUDE_DIRS}) + + set(QX_LIBRARIES ${QX_LIBRARIES} ${BSON_LIBRARIES} ${MONGOC_LIBRARIES}) + + endif() # (NOT QX_MONGOC_INCLUDE STREQUAL "") + + endif() # _QX_ENABLE_MONGODB + + ############################## + # Build Mode Debug / Release # + ############################## + + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS + $<$:_QX_MODE_DEBUG> + $<$:_QX_MODE_RELEASE> + $<$:_QX_MODE_RELEASE> + $<$:_QX_MODE_RELEASE> + $<$:_QX_MODE_RELEASE> + $<$:_QX_MODE_RELEASE> + ) + + ######################### + # No Precompiled Header # + ######################### + + # By default, CMake doesn't support precompiled headers natively, so _QX_NO_PRECOMPILED_HEADER compilation option is defined with CMake + # There is a project named 'cotire' (compile time reducer) which is not deployed with CMake but can be used to support precompiled headers, more details here : https://github.com/sakra/cotire + # Moreover, some versions of MinGW on Windows have a bug with large precompiled headers (for example, MinGW GCC 4.8) + # More detais about this problem here : https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56926 + # And here : http://stackoverflow.com/questions/10841306/cc1plus-exe-crash-when-using-large-precompiled-header-file + # To fix the crash during compilation, you have to disable precompiled headers : just enable the following compilation option _QX_NO_PRECOMPILED_HEADER + # Note : there is a side effect disabling precompiled headers => compilation times are considerably increased ! + # Other note : to reduce compilation times, it is recommended to define _QX_UNITY_BUILD compilation option + + if(NOT COMMAND target_precompile_headers) + add_definitions(-D_QX_NO_PRECOMPILED_HEADER) + endif() # (NOT COMMAND target_precompile_headers) + + ############################## + # QxOrm Library Static Build # + ############################## + + # To create only 1 EXE including Qt, boost serialization (optional) and QxOrm libraries without any dependency : + + if(_QX_STATIC_BUILD) + add_definitions(-D_QX_STATIC_BUILD) + endif() + + ############################################################ + # Boost Serialization Shared Library Dependency (optional) # + ############################################################ + + # By default, QxOrm library doesn't depend on boost::serialization shared library, but it is possible to enable it defining the compilation option : _QX_ENABLE_BOOST_SERIALIZATION + # Without this compilation option, QxOrm is a much lighter library, generated binaries based on QxOrm are smaller, and QxOrm depends only on Qt binaries (and boost header files) + # But in this case, serialization features are limited (based on QDataStream and QJson engines) : limited qx::clone, no XML serialization, limited binary serialization, limited QxService module (network transactions), etc... + # If you define _QX_ENABLE_BOOST_SERIALIZATION compilation option, then boost serialization is enabled with XML and binary engine by default (see _QX_ENABLE_BOOST_SERIALIZATION_BINARY and _QX_ENABLE_BOOST_SERIALIZATION_XML for more details) + # Note : if you are not using serialization functions in projects based on QxOrm library, then you can define or not _QX_ENABLE_BOOST_SERIALIZATION compilation option without changing any line of your source code + # Other note : to persist containers in database (not relationships, for example : std::vector), without _QX_ENABLE_BOOST_SERIALIZATION it is stored as QByteArray (based on QDataStream engine), with _QX_ENABLE_BOOST_SERIALIZATION it is stored as XML (based on boost serialization XML engine) => so be careful, in this case it is not compatible + + if(_QX_ENABLE_BOOST) + + option(_QX_ENABLE_BOOST_SERIALIZATION "If you enable _QX_ENABLE_BOOST_SERIALIZATION option, then QxOrm library will provide a serialization engine based on boost::serialization (you have to build boost::serialization shared libray to use this feature)" OFF) + + if(_QX_ENABLE_BOOST_SERIALIZATION) + + add_definitions(-D_QX_ENABLE_BOOST_SERIALIZATION) + + option(_QX_ENABLE_BOOST_SERIALIZATION_BINARY "Enable boost::serialization binary engine" ON) + option(_QX_ENABLE_BOOST_SERIALIZATION_XML "Enable boost::serialization XML engine" ON) + option(_QX_ENABLE_BOOST_SERIALIZATION_POLYMORPHIC "Enable boost::serialization polymorphic engine" OFF) + option(_QX_ENABLE_BOOST_SERIALIZATION_TEXT "Enable boost::serialization text engine" OFF) + option(_QX_ENABLE_BOOST_SERIALIZATION_PORTABLE_BINARY "Enable boost::serialization portable binary engine" OFF) + option(_QX_ENABLE_BOOST_SERIALIZATION_WIDE_BINARY "Enable boost::serialization wide binary engine" OFF) + option(_QX_ENABLE_BOOST_SERIALIZATION_WIDE_TEXT "Enable boost::serialization wide text engine" OFF) + option(_QX_ENABLE_BOOST_SERIALIZATION_WIDE_XML "Enable boost::serialization wide XML engine" OFF) + + if(_QX_ENABLE_BOOST_SERIALIZATION_BINARY) + add_definitions(-D_QX_ENABLE_BOOST_SERIALIZATION_BINARY) + endif() + + if(_QX_ENABLE_BOOST_SERIALIZATION_XML) + add_definitions(-D_QX_ENABLE_BOOST_SERIALIZATION_XML) + endif() + + if(_QX_ENABLE_BOOST_SERIALIZATION_POLYMORPHIC) + add_definitions(-D_QX_ENABLE_BOOST_SERIALIZATION_POLYMORPHIC) + endif() + + if(_QX_ENABLE_BOOST_SERIALIZATION_TEXT) + add_definitions(-D_QX_ENABLE_BOOST_SERIALIZATION_TEXT) + endif() + + if(_QX_ENABLE_BOOST_SERIALIZATION_PORTABLE_BINARY) + add_definitions(-D_QX_ENABLE_BOOST_SERIALIZATION_PORTABLE_BINARY) + endif() + + if(_QX_ENABLE_BOOST_SERIALIZATION_WIDE_BINARY) + add_definitions(-D_QX_ENABLE_BOOST_SERIALIZATION_WIDE_BINARY) + endif() + + if(_QX_ENABLE_BOOST_SERIALIZATION_WIDE_TEXT) + add_definitions(-D_QX_ENABLE_BOOST_SERIALIZATION_WIDE_TEXT) + endif() + + if(_QX_ENABLE_BOOST_SERIALIZATION_WIDE_XML) + add_definitions(-D_QX_ENABLE_BOOST_SERIALIZATION_WIDE_XML) + endif() + + find_package(Boost 1.38.0 REQUIRED COMPONENTS serialization) + + if(NOT Boost_SERIALIZATION_FOUND) + message(FATAL_ERROR "boost::serialization library not found (with _QX_ENABLE_BOOST_SERIALIZATION option enabled, QxOrm library depends on boost::serialization binary)") + endif() + + set(QX_LIBRARIES ${QX_LIBRARIES} ${Boost_SERIALIZATION_LIBRARY}) + + endif() # _QX_ENABLE_BOOST_SERIALIZATION + endif() # _QX_ENABLE_BOOST + + ############################ + # Qt Gui Module Dependency # + ############################ + + # By default, QxOrm library doesn't depend on Qt Gui shared library + # If you want to serialize Qt Gui objects (QBrush, QColor, QFont, QImage, QMatrix, QPicture, QPixmap, QRegion), then you have to define _QX_ENABLE_QT_GUI compilation option + + if(_QX_ENABLE_QT_GUI) + add_definitions(-D_QX_ENABLE_QT_GUI) + endif() # _QX_ENABLE_QT_GUI + + ################################ + # Qt Network Module Dependency # + ################################ + + # By default, QxOrm library doesn't depend on Qt Network shared library => it means that QxService module (network transactions to transfer your persistent layer) and QxHttpServer module (standalone multi-threaded HTTP 1.1 web server) are not enabled by default + # To enable these features (QxService and QxHttpServer modules), just define the compilation option : _QX_ENABLE_QT_NETWORK + # For more details about QxService module, a tutorial (qxClientServer) is available on QxOrm website : https://www.qxorm.com/qxorm_en/tutorial_2.html + # For more details about QxHttpServer module, a manual is available on QxOrm website : https://www.qxorm.com/qxorm_en/manual.html#manual_96 + + if(_QX_ENABLE_QT_NETWORK) + add_definitions(-D_QX_ENABLE_QT_NETWORK) + endif() # _QX_ENABLE_QT_NETWORK + + ################################ + # No JSON Serialization Engine # + ################################ + + # QxOrm library supports JSON serialization : this feature is enabled by default if you are not working with Qt4 (JSON engine requires Qt5) + # To disable this feature, you can define the compilation option : _QX_NO_JSON + # Using _QX_NO_JSON compilation option, qx::serialization::json namespace will be not available + + if(_QX_NO_JSON) + add_definitions(-D_QX_NO_JSON) + endif() + + ########################################## + # Unity Build : Reduce Compilation Times # + ########################################## + + # QxOrm provides a way to compile the library faster using the Unity Build system : http://stackoverflow.com/questions/543697/include-all-cpp-files-into-a-single-compilation-unit + # To enable QxOrm Unity Build feature, just define the _QX_UNITY_BUILD compilation option + # This compilation option changes the make file : instead of compiling each *.cpp files separately, QxOrm is compiled using a unique 'all.cpp' file (which #include all QxOrm *.cpp files) + # Note : your environment must have enough memory to support this compilation option, and your compiler must support it too, otherwise your compiler can crash with a memory error + # Other note : C++ projects generated by QxEntityEditor application support this compilation option => this is a way to reduce compilation times of C++ persistent classes generated by QxEntityEditor + # Other note : if you define _QX_UNITY_BUILD compilation option, it can be interesting too to define the _QX_NO_PRECOMPILED_HEADER compilation option to reduce compilation times (it depends on number of other *.cpp files to compile, like moc files for example) + + if(_QX_UNITY_BUILD) + add_definitions(-D_QX_UNITY_BUILD) + if(WIN32 AND MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -bigobj") + elseif(WIN32 AND MINGW) + # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj") + endif() # WIN32 AND MSVC + endif() # _QX_UNITY_BUILD + + ########################################## + # More Efficient Qt QString Construction # + ########################################## + + # Qt provides some optimizations for QString class (construction, concatenation, etc...) + # More details about these optimizations are available on Qt web site : http://doc.qt.io/qt-5/qstring.html#more-efficient-string-construction + # To enable these optimizations, you can define QT_USE_QSTRINGBUILDER compilation option + + if(_QX_USE_QSTRINGBUILDER) + add_definitions(-DQT_USE_QSTRINGBUILDER) + endif() + + ############################# + # Compiler / Linker Options # + ############################# + + if(WIN32) + if(MSVC) + if(MSVC_VERSION LESS 1700) + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /OPT:NOREF") + endif() # MSVC_VERSION LESS 1700 + elseif(MINGW) + # For MinGW : we need to add these linker flags because of some issues to dll export extern template instantiations from shared library + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-export-all-symbols -Wl,-enable-auto-import") + endif() # MSVC + endif() # WIN32 + + #################################################### + # Macro To Source Group By Folder For MSVC / XCode # + #################################################### + + macro(qx_auto_source_group QX_ALL_FILES) + foreach(QX_FILE ${QX_ALL_FILES}) + get_filename_component(QX_PARENT_DIR "${QX_FILE}" DIRECTORY) + string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}" "" QX_GROUP "${QX_PARENT_DIR}") + string(REPLACE "./" "" QX_GROUP "${QX_GROUP}") + string(REPLACE "/" "\\" QX_GROUP "${QX_GROUP}") + source_group("${QX_GROUP}" FILES "${QX_FILE}") + endforeach() + endmacro() # qx_auto_source_group + +endif() # QXORM_CMAKE_CONFIG_FILE_INCLUDED diff --git a/doc/doxygen/html/.gitignore b/doc/doxygen/html/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/doc/doxygen/html/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/doc/doxygen/index.html b/doc/doxygen/index.html new file mode 100644 index 0000000..e493cf1 --- /dev/null +++ b/doc/doxygen/index.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/doc/doxygen/qxorm.doxygen b/doc/doxygen/qxorm.doxygen new file mode 100644 index 0000000..ff5f0cf --- /dev/null +++ b/doc/doxygen/qxorm.doxygen @@ -0,0 +1,1761 @@ +# Doxyfile 1.7.4 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = QxOrm + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 1.5.0 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "C++ Object Relational Mapping library" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = ../qxorm_en/resource/logo_qxorm.png + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = ../doxygen + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = ../../include + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is adviced to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the stylesheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = YES + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the +# mathjax.org site, so you can quickly see the result without installing +# MathJax, but it is strongly recommended to install a local copy of MathJax +# before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = _QX_ENABLE_QT_GUI:= \ + _QX_ENABLE_QT_NETWORK:= \ + _QX_ENABLE_BOOST:= \ + _QX_ENABLE_BOOST_SERIALIZATION:= \ + _QX_SERIALIZE_POLYMORPHIC:=1 \ + _QX_SERIALIZE_BINARY:=1 \ + _QX_SERIALIZE_TEXT:=1 \ + _QX_SERIALIZE_XML:=1 \ + _QX_SERIALIZE_PORTABLE_BINARY:=1 \ + _QX_SERIALIZE_WIDE_BINARY:=1 \ + _QX_SERIALIZE_WIDE_TEXT:=1 \ + _QX_SERIALIZE_WIDE_XML:=1 \ + _QX_USE_MEM_LEAK_DETECTION:=1 + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will write a font called Helvetica to the output +# directory and reference it in all dot files that doxygen generates. +# When you want a differently looking font you can specify the font name +# using DOT_FONTNAME. You need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..362f370 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,18 @@ + + + +QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic editor (data model designer and source code generator) + + + + + diff --git a/doc/php/add_download.php b/doc/php/add_download.php new file mode 100644 index 0000000..de44941 --- /dev/null +++ b/doc/php/add_download.php @@ -0,0 +1,31 @@ + + + +alert('" . $errors . "');"; } + if ($nb_errors == 0) + { + $_POST['created_by'] = nl2br(htmlentities(stripslashes($_POST['created_by']))); + $_POST['message_text'] = nl2br(htmlentities(stripslashes($_POST['message_text']))); + $createdby = "

**** Downloaded by " . $_POST['created_by'] . " (" . date(d."/".m."/"."Y") . " " . date(H.":".i) . ")

"; + $msg = "

" . $_POST['message_text'] . "

"; + $filename = "./version_1.5.0.php"; + while ((file_exists($filename)) && (! is_writable($filename)) && ($try_count < 200)) { usleep(100000); $try_count++; } + $fp = fopen($filename, "a"); + fputs($fp, "\n"); + fputs($fp, $createdby); + fputs($fp, "\n"); + fputs($fp, $msg); + fclose($fp); + } + echo ""; +?> + + + + diff --git a/doc/php/add_download.php5 b/doc/php/add_download.php5 new file mode 100644 index 0000000..952793f --- /dev/null +++ b/doc/php/add_download.php5 @@ -0,0 +1,39 @@ + + + **** " . date(d."/".m."/"."Y") . " " . date(H.":".i) . " ****"; + while ((file_exists($file_log)) && (! is_writable($file_log)) && ($try_count < 200)) + { usleep(100000); $try_count++; } + $fp = fopen($file_log, "a"); + fputs($fp, "\n"); + fputs($fp, $text_log); + fclose($fp); + } + catch (Exception $exc) { echo '

' . $exc->getMessage() . '

'; } + + echo '

If your download does not start automatically within 5 seconds, please click here to manually download the file.

'; + echo '
'; + echo '

QxOrm and QxEntityEditor home page

'; + + echo ''; + } + else if ($is_valid) + { + echo '

File to download ' . $file_to_download . ' does not exist !

'; + echo '
'; + echo '

QxOrm and QxEntityEditor home page

'; + } + ?> + + diff --git a/doc/qxentityeditor/resource/qxee_all_views.jpg b/doc/qxentityeditor/resource/qxee_all_views.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6671596ae272d884d44778c208460e60a2f7a2ea GIT binary patch literal 179658 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

{sqo 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 literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_cpp_preview.png b/doc/qxentityeditor/resource/qxee_cpp_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..9bce3ae632032a7cf71266c801a0327df8ddeaa4 GIT binary patch literal 66864 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 literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_database.png b/doc/qxentityeditor/resource/qxee_database.png new file mode 100644 index 0000000000000000000000000000000000000000..4fddce275535b5c30fa4ba3a01079710d8210170 GIT binary patch literal 42253 zcmcG#by!qg`#*}J0#YIZ(jqM-EiEmgASDe-N=x^Q3W$`H(jn3*8|HxcA!kz1AI{&%N#y^-M>T@;cLXA|fJ6tw(D5L`1}C zL`0V(NiPGxxLCfv0sM2xTVGR!sA`C13;1%yQTeGd5m8M%IrimM;5*stN2cCHL{uGr z{x0=+l-Lmw6@1lFQ#K5++G{1xvNtx`K)k=AOcEJbl3%i7`ws0N648F=P~@DPuTRy7 z<*f#n3h9ljjX%GD!(O~my3(bI67{=!N&UqygA9{9HqL?{)F?@{A`|XvMYN1%@9RM2 z=Chj!b!TRnz*8B2MDVae-Eb=u=~!GlJmXT`I@Gwce^hQzU-ko6JZT;I{-4i8b5Z#2 zA7zM$-dJ7YAph$sh%8+2uZvCY8<4*)zN9{c{m~q_>GFTxWJ@|7+M|o>Iyr0GW%pM) zW8V+lFFtL>il12o;qa|JI?md7_H_wL0{-$_ z$-!*C(|hsF#~IKjXWWbkzJGnasNt{I68$F2kJv2@3-$b+{n21EeRv< zklpu$dE5o~e6)6`<;0#Eit{C`t=EGv)6fGsY77plxH4P~LLD-&q1OdKC<1OL7Z=*|pX20U5JIbJaP*CsoL6TWA^qmvqlN*62pS|wRS~cD)TExnWH@3O1+M84SL9YXb)z3g{YhoOCTe_;+ z#E#sJR|lVD$Mk|%+&snnWQm`QP8N}Q2>DjZ{5&oOX8)(& z#{}-X)3Sxp+kq6>+1$N^CjhvSK41USd8s~2^>1<5kZ}gD_UTNl1wy2flKIq-UIyg}9w&rfY&dU*6nj^A!odGI zx2U+xb>^k&8o=7(^;>S0c}5SPxupup$@ zBu2$p!I=@@BFu1 z6X6KcJ3O)5jcVy>6}_%W(v~|~tlsLMn$s6P<(YS7F2y=jMekk{A) z7h>b*L*1Is$O{j-GUL96P)Jq9ZQWJ7F#cthlyAqyOz4kvNGb3A6AOjz%$id!O9V4b zY>QFm>O}0W4TDD|brp}^J7!(1Ic5hQk+Xxw`k@=^h=B8+9KyVAD0&#*j0{|N0pUh8 ztIyXtjTMiJ&J;Cy&Py_*=AtCl-{*OVQF%pqJYJ~ePFwq$XvAJ1Kc+EHcUOA2IK!N7 zFJI%8RoB~VzJek}_t(AL`7BhYlbtTkrC0fAlfOW`S$4AeejgplmnWolHSi7%#Z+io zwxC)d^e0(@r^gRC8Mqoq)!-@tE_L*Cr{1wF5Onlyi)(6;RPW|` zD~{knYy9+QZAZ{SspGmjw_EUx2tTX-WPj@0<&&Gc^fz6k5%-_zD5ej{ojVu6k;c3P6HW$H^4&O!}) z@e+NPm&Du+m*NWF1>j%eCB9JKyJYLkY4STqC%y|1{v87=>PUxM@Qfoy9EtB@Tva`wz$3Ex4$npE272CjF@P!mwYp@<75Gz@{; z(wswQQbRWe5htGsf$KxXAWWOfgq_NVCe4P-@YcE#X6*p-n*-S}sQT5~d;a;d!_gx% z<8v^fLw}M>$t{?>kXW7&P{> z`0BX7XOOyUmUFO7N+$g|LX6)rD!cjB_cs|1>m#Hm=hlj`6P0EX3uZ!B>v4%1tsjEa z%iQT9urk>^z7I&Ge4UGB8I5OnLH%QcX@QYf4^w=~BLkD-2y3e1mNKU7vs&x96wJC# z7KYa-i~vM1K)psfUErFn-~W*w(uwY~2V+_Z7k!1|)q#iUJ4)d7xY^;kfc^CPEdO;k z5t*ZP@8Op9Eri#~$H^F%o4%Ph=H^upj#t|HNkeKyulcH3iVqJ$5?k~pb(se3cyA@) zKhLqOI-qvjg45De&FAllbt^lkx^(7#mL=PJNh0_*Qq41F=0;gQnClS@k9~(mXFvJD zbfCMiNbNiCs`YdW!=>sMJon6}yklsBN}ON5t1BXP4{K;UkC<#1&rtR6vQHr6{r&Q| z!)oD<=IiPH5~Qi>=3=f=mxT80t+2IQ675f3$=uRvOQd{-NmBOiDOpf{YgN6-;cVZR4|7|h;w6x?>gNjP~4 zKIouE9CT{#1#~U46Aswp*%a^#%-tKe%gXV1BmqyGd*11&c$CiU-mtXT9+sJzxqNAz$EZ_BAyv5=&oijUBFmll_rFEgsEcd6A5^ zt141vQc6*-Sl3eaP!2fu@F}=xOtVBeJ33BYf1P(MO;*~>&*{Di6Qi92!zEzESMl;3 zvv^Clxi^(O-)clNnSH#IdPn$&V~RW?5-8ig^$v67-k;svDWMTT96Qcz*LfMXu~ghv zNpgCUnK@e1&y1~38y(oZ3k*f%mc(mU>f+uoPS^Q$zkBdnB%_&`m0vsW*;(?aFq6nu znP{DbtJ9+rMr1U%@FzCQI>%LR#$01OL?S!eHw&Fy30>TNc&)`kBhR66Y&nPAMfazM zVRrkQ?m-yoSHqUIcdO~J)xo&zlV>OQxH&tk4P|F;^A;9q=Gb{n_EdC)q9?!bn3Bv! z)%JE&yfS_sP@Y+Ga`g|YzNJ>1P-p1zVtrkcKz*$>(+11>f-WKSBs-CW7bQ)8uUtGD z9yiIDzjlva40d^CIl{|?(|17YO1dU1T(ULscg1@0XRDQ~8+WiC!nc0E9~+x9lXP@6 z9&FdKGgPaMuF*bp7Y&2HTCEsO5Hv~+?j?nLX={@6O4*2{bNX6UAzl>_ysd1Pyim0V zVZjWA8Qr%(lL2D+C@;{O`kH%7i#PSVeD7*r;XlsnfKH<8b(lkLf;ZHjhb|Uwqxo%p z8<}FVWvKAc=tm{%`Bd))%AM?9{<_lMn%4gAgnZ-$J|p&uv`fi1;Wt|S4)${CYIyuT zw(lAZRow%XvLr7<>oZ6}V72cQH^~#DI18S;N^OVizUJ{$*w6H7NfWmDmcS|dXO>=Q z_tHX6t2XvQ?N8%9{PI*IUc3iqX3gbRpnvMZPYt@ay(G!_j;8$GJk%Ldg5ba)p*E(! zeR9@F&TP!gk5nWcoc3;SmODhE&Yu>bm()}-f=Dj~pDRGFI~eSdMpM zhssLLu6S!)1PIJjJx&T

C9F8G9A(AZh*f_$md%@*OPl_GX&YC-d_4-CvTLiFOCICA(*bXWb9xJi3G9e4f%$xJ69{=of?=x2tLFl{F10r7R= zKaSy}4cN>Sb9!4lf7k`3;!yuwUR=ztg*wI+i=%w574&Qcta?V5$)I1IzV;P&X5zU( z#+Q9#)>1uOt@3r+CefOa-#&gPsi)|1__#I7UUu=4{SQ&E4?GRuCSHjjjPb+aaoA{KEc|k*XcV_?=Y#h=4y6`{D^o4Zz62 z=lCY6>0cMWweQ&dQIhDP$lHWJ+=7UxwV+z%RP3+IZ&SxcFg8WM>y-ccxFwqBhVPm$ z9MW(7_0*dP5|#fR@x~ZpCw8E{Gvvk_8I``KN8je(`fVA)6p?|$ax1Z8!*a-$ei3Mh zx3@vX^cQsVC9iM6&t}c3<}uLAKP`wj$myf^!*?SXtO6E}a*x(_6wjc9Z8vJnx(9kR zwT+9I19Q4J+w70*P3A3s%m7DFCWOt0%`AA`v1_0E>uD1he%8$qiy31(G@8{x_@SL< z@i^%8>7tu8jur83@9nG(7)7ju__3!v`vccQ0+#gE3I6>9jH#sN#{3xjp|Hw99r%-& z?amQ%AiUg4GGMwIyJKW*fw=(7Z*#^74UR0X7fhL6dvZ)Y9<=8xzgarwjFGi|b1U9hmCo{U(p|_Z|rs{HI6+p;s4l zSk=7$6(3JHkGIC3Q{{ z&2Lv|{Pz9A>1n;>gIMib(Nxnmai69^iUOO@lMT^E? z)J(SS`z{v*o|Id++V*@@2tI9Dm#hxlwPg<;l$?aU;n0P#%@+I!JjP?<=b(YBM|892mu+U zuoKSj$5?EE1Ah*^JKn`5(=ZqXE$edZ8&GdV6Hf1V|E9VDdEx#h;VQYhpj~|^d@HkK zsr8JFODOmlmX`;jZ$4XXBksr#eow-77Nb-H4%kGYPzOsR`Dc4_UUta+`E41OlWy0G zxediLu+sX7_1wucxix0nI|q{`porT#*{Q;JB3h6usiCMy&}Jm;trB_gN}O~Xc1?s3 zhd7Vho2^&6=+!MUJ$4_z#3G}T!d2m}xeq0v_?>yFbL30I1t|v&`}Iv?(z=&)70-)B z8aCCr%!ivd)i>IXyUxObb^B6xRBth=1 z1+e%!k7m?)s86YjOyD8j*z#N?o`7Tr4;T>EDZ!|#5EePmb{qqEsXKFJm{FW72D~<@ zAaYN^A;#U(5iH=~R1Tw(W|pjKF}V8^zzWASnx z(6x`VZIB~XWFXoAyv?s0*e9NQF(BP`5H}1utyRKp=^}!5Dhphk=%bv-UczV~+q74U zsj_7QH&?ZizCG?*@JE2G73`c%3m;I}WH3%H`0O=$$i`pU@wO)PTc01oU027zJ@!a> zf{;jH4+7f-MDppJ)g)*WUTgULO7B?n84Cv1)8@6h*-OB;^ecJ8-5GTba;WFeM-dc? z`L0e0VM^E}sFVAO;^aV-*3;JOMWrChqg0tqihqK~*SmJ?KG*n*0QaVV>-w`QfNYS! zH2JgN-f#78I7?Yt@CZ8W7)G!4EJ(F%{6zg+TEg3&vEQcp{4?lx7+dF9d6um2_CheS z6)+yf)0+6!4FzN%G8hp&ZJ!#tQxEDdP3GO7_CD)TM86du3Rq-bK)o+k*mELahY3%R z54{vUm(>pCwoFw{$6w~#X#H9U#-6vW^qe`9(R9=%spVVSFGkTUOI+o$8V(&J|Km}^ zji^Q&rex~}{wMJSY>d}^7AnwAu5jC`nM}#s&V6aZ&%)kF2yMUe#JlYams3YO&F+FC zVaP?BykxYg|8((yg#tJZKfgWT*s=YP`>vpn$A>`Mz+O2si6oWN=$N4ezx_h5FY)2E zB26l$!U~he8efWvDJ&gR{uHWjFUX-(9IfKP3Zm(+hH*nT`Yks25lEh zNl8($$xoqZ(K0HhtEFM5%7Ud+wmthYey+oK90uxxOI0L93_~}-(8!r`YuxsFxK?da z(!!kl9uPq`z(^>e+$VQW-WWH_Qv6m`lWN|7Sy6Es3f=a)2@%0&AAt{kZG6O#U)RZL zA#4rfFx7;GGsUG1CH(h|ki&^tYVcP783@CGJeB2GJym|cebr8eC#7T5Qma*e9WR<_K@^pqW>ZiX}&*2}6tc)G6Bm@yE?9XH@= zhaPYY1D}n4_50RGk_?fo?~tv6;?a%@JEm|1V)BU!Ea_LRnQDxQ^_W!wTHJ`Ub9dRm z4(SvAKNiGYEHy_?<36;ddoiCKS8^$7-{>P*hTtY*Hi zpQ~i+*>4MOz*H?+2ERX>lWN)9U&?9iAa+#1>5+^hg`er#C7(I4R}!SO$n|r-&j4Ao zx^X>&L=p8CsEjtVAAhL^jRbCl5Wu*F)VAZoLBzvh8c2Z+^2l&^BtQ5g%Y5i6%rY^> zP3Qdkn2wz9_9b2*ZX(;pE)bYo#d?Hs{^#H2vwy`tIFOz2Fg1o|ES^B+8_wy*hX6|- z2c4XO_LAaT_WT!cDL@v{Ip|+Jm5WbEmrUeKZc)c(Y#VM zubHcKeoPb8CBdsRr(27^us%cO0SoXc2Oq9+l7P1tdIXfZ$SO&97#YVV-%+sh(7jY$ zw{BVY{-I7wIM*di4O@_#+W4$fio#zqrw_kJ?H96j>Xh2`--L9!SYu{9>s*asK`N@f zQ_;Je4rRR`rn_`C+^vLp(|?5UILs|p@_2XAt*C$PGBASwtedpLkV8@AsTN*QEH0Y~ zu@YQ8KsImx5V5OTpPdJK=Mq2|JX^qTT8I2{>%i4hARB|u3z;LW7o=Nugh$nRrKu>+ zkj!jBtKw|Ilc>eee9)A@at}M7JOm@vdF5;BlK44Uq@ns|-u zrGaq0L^gXMYF2{PyVx2rsHJ8q_NJrX?Q$VPvsw4;eRrp7IwIM9Hh<@ZkwDkqEGr)) zmO7{(fUxJK2D6VojRCwFylhm}=s79Ux}SBlfVIKI*p`C#GlzoL*jt^EZ(El?we#YK z_mqxT*b~E+P?}&gWDVM4MI}Kv`UMXBH4MhirCQfGB1J_LliCi5$V`(Oy>>-w5 zK!^5jl23z0RQwvi0j^su#jX_86>Lr0DNFnF9nJf!3%+&@n5oyuY+m=~lW|r@+*x63 z2dypHbnZoZZs2Kk>(VIUs1S}TAXu)|0oXRs+WVZJ@DiKLqMME%|)p_=nUzBRA-<8yDRLl#>BAKIU9 zMz$Ow76Lc2>k)Y0dISiQ?XURZiu*i(D!6|0S)KGi+lPUnk`hAOGpz#Jb8!=G#2C8= z@k-~lmcyU}rPl=5NHUjhKKoJAOI4={e=tfzW+DUy$t3hov;U5kFXH3qpHgLmbbs&) z7c{RJK~A;l2C7U11q_uF&l}+J*!y2HcIKz1_OBLHHC6}Cr=#96v@EWPfE!t2fh4R| z9T3eW^x!*snFDSQo*WzPqT2b$U}U=+^xU2XJWkP#XnRg;f8S&%^LIl0uhUS@mG!bx zqk1(lIB8UxjrWB~tP)NsG|x~vEC2Gxbh}Zdz|XP&qcoEe&QDTqpL{Rn)k;(atG~cZQPQAG&NEdBcpWK zMVvb;*AfxFo417TQCK+$j_f(3h_|~wZBVSEupt9Ij%9$Nqb=)Ok8ohjP&{F$y6wP{ z%MJjLeP^x6P{-WR(>Upc24qe%Vc(V=G(Jt(V*np8Y*q4!D?q|`Yl1&I(FL!5)2|_+ zUhqUM?vd=bo-71=%tHwRnA7dEZ??X6TFmNd^x}y0sb0NEn z-!lp%z8!~On$sX^Xz@e;d|<-?OBWX>J0V5vw?B6=)uVWd-U&nMNZeO}6+Y@F^_+R{CrJD2VfHdQ1A zW!;nN@I$?J_8MK?xKA}r7I9re<5x;!uewYZL~|G2gxJvr@C6-hgM4XA+;8z!I;oZRoOI2tty3@Fvv8Cv>3lO+_@EH7k*Dg<^5Avm^P2iyPP%5W zEn*>azj32GQRVcHcUIPegt%Yt)e%yDM2Ub75c22k)I1UB_mghS(?ejIt}05ifJr_T=&OGs61bqYrEvav`K69G34m}92X04zQX$d*+F{V<&b`4tkQ&Z2^ z2R^o3(L*ZQ<74s`;hqi>zmncmOiWwWPkGT(2-h%As*zM)0qCe7^ZR4CC zvR@HMB4^Rd`O2#CHCe;oU)K|k#f3_!lWL(z$4$ATLQ3MKSk`j}Q z1Fv+%hImQy4va-XlK8#>hCUfXQU^P(^d()Od|B*_`BT2kxbkUw%ei()?RP7GsZCUG zL`5)RM!E!Pi(ZdQ% zmPXT+&O((AciBS+P7BSE&3jX*&86hX3*^{M%CjxNds{bwMc=N5?$4N?Lf4Y0q38Af z*mg`+Ko=6cxXX*j4&!%+|5Q1G&-N8wFaFe2JWR~RH2z_XBKSf3)Yjd?4ML|bwv#po zb>%OrgUvHZ`W8ub*hA(%4@L@M&JZmgh{0f_-aWrz1?kvZ5Jw=w1E0D{&>%w_Dxw^U zlB5P0g_&Lrd#mSp5|p{^oHKJ4p%ik8kAdl?>MLk;sf;9pKZasS+M;!9#=BYUdrH@fN{aSB@^CxWh z?L!ZiMG^vVOl2=30H3L!eb-Ca%#}B?Wzox818z4Up5XulTJ``+I&i zgrrTK>YDgiO@VcfQH`29>{^hf7FDs*_=~lA`l&WAI^AN25Z#EV{o(5@63PSH-@2I} zixjgk9o2*)U=5AVW!^D33Z8dy1x4I8(Y@;iNZo6pb1j<7IK)zVoa5V3IuU04mOYinB;r!VfF)-B6wj~8er42pA zg*T#BbPiXzAjR@)a&~x1d-|6bs zgKVh(RCxK0I3)iv7ep~S@Y+OpKQSmGk1HPD-PL3|@*Z{ps z-rd;D+XxLfhd`%WUb!8K*FO%wM>xDtK;S=|%_2(FDFF7jaIhfKuN=+np?F^481jp7 zu6!lD;+V-v+F?UwVj1DI8^_y1TpFCB%vBciBBD(0HD2n`?F1b6_>%^l-vwGIH#(sb zMo}4bh4GOy6QWkEY?YE$i(BavH{;lKVU3H;+?_+sU#}=HuHatNt4gJ(;~Pty#b$Z} zO2)sY^%W<*?m|?U`|4f!8#-lvQ(%SW{sB&c4tlASre8bFY*;vUetYO0?fY6gqdgW% zL}2DJ3w$9PM1LYS9Y-*Fl7*crK4(r|lFQR)8CBX^U=NVPzyT2@_s{?gYX@<2WTb~; zz%2jGrs92eemi8t%Ew6D2D0h2iZ-_yJ%hluLIaMPCqXOmiigDU_$W<*)|6wV3W?Qe&pprLpjJX=^d; zk9w+MY{wE9Q@dmCH?5LgS*mxV0PjwrYDlD^ zD}93=bB!151g|5d7TY#rJ7S|H*cz!&KGf z7v{Ah;6Wu1@Q9K``6$_|gs@SUl`VcCQH5{_5HkUcLi{h}5>r>}F>tt2lbmz|f_>s9 zH9RmwtnwqZWTRGGKEe7TN%9L+LS385t6 z6+FB=K-n>E;kqf2&A)236EeQr<87gsiOET&v&P|2{827+t0?o4rc}w)HI3`j0J7xo zjB!F84T6brh)vobNsKup3S<-5B2pg==dC_{YLQ7d)f|o@Oxx~=lME# zKICKu1qUl(_xhzp%$V^*#ZrXLzNKbGCG4z-hgIk#>IZQq3E&6-ctv?g~Uz<~x(wGY>Vy?`xzDiXj`+L`nNbs(m$6q^S zL|=?QB(yTTVLKf1Z%b}tN+#}zvBuhLorLcwUxLK|n`B>%fnnLirB$ATgRi(U{^=F? zZE#cNxuO8o!^B7F&fDP?pw0q}DpuEs9`eE*C?7HxL{wm@sEMv~8o(Y`^Zx!Z z)A-DToEpr;jm1yJGY|Ifd5+K^7e+ok$P58sY#NR({Fh`gMx$> z71&PZH-I%ob7VxnXJHRLfP3BNJe+t$cL8TmGII?Vm%dst$!PVBOecLTKkDAVfUv1c zyLVNfxgxE&JL*SrIv*QA>llCp!^?9Evc2H2zhrD3QnUTEF|R&Q>D3KL<#w0U9S(u5 zw4B-}Pij=8Hn`h%u{Go`-!1p1CGmN=k;BVnoUf-%FzN}`s!1&@?}@i_vuf!K{9WVO z;}KUr5cmT>f%Q@p%}aQ?yI|nH>9#;E79n_Y)GDsN@@vrMbsebjemeMy|pp@}iGRKi~Pas&cwr-c$XKESlH$F}y4F$K~{?QvMg) z&RjNey;mHwhXvnlbh`dsb#)d?z6HGug!um0@ZC1=ddm7nzr(Kr-!DdOeF9F^{kMpT zx*b$F%71u(3C|)6;aZ$;~D8``)kRqkZ^WJZto{aNs zPqmljGtt$oU$b1zc%YRZnFc3hiBU*!EDaa7?QC1g8i|%x8DuS~DY*(LvoNYWZw>cy z)x0UH-YAr+BSpqi`_ zQ=JdS(qkuAq|lAiZQE1*%fBKt*Y8v5jBn5G}5O|Iia2Xc{ zm>?%^FMPF23)ndCN|_1&Gw}n4Ft%oiZB^L2>+HBq&BBdDiJQsSO;!BhFZxgS`P-}Z zcIS+}=c%e5HC=>mo0M-Ppsg$(!_4FpHwT_qGinw2Sn~0zk~eM>dqnTVOEBFN?G3|I zGVruCI!BWLoQ{LS* zCMlW|vXdw`bYLD;k=m<*d{&>E7RYaY1n*x%yPFid$2sY`NS^0!@dq}VX7Ok1h77iH za5#x3S}@VC=n|;C4wkv-S*3dmpr)@oyGq41`i%tb60QQlRJqybjvcaT)0NaTVf{~^ zRD|_;gtl;_+DA#BM;c5?w(*bZrEugC;S4_kGy`AE{kD9L; zTS)(#zJpt%k6cWA=D1vN*~z_qvC(mV@j0L!Ws8`De8yg$Be+YQcd+=Xq1>6it&D#z zvr(un%*u1qb>#-B>HLO6p_^oz?&-FJp1ULa@~~t<+>7$4@2+70>!*Xb6YIK9OX6OS zO~z*JGt!O`L&7WS_&vK75QC$Rc*_88YKc_R52Jb=;@J>)De{cl}cAS3mKABoyvw!<@H@o+*{t z8D$`g)RVY8rYz4gu2 zsR!F%r`=zFbr?T^ueQtyx<{wRi)nza(Y9-N@td}6NyP<$#Wq*g)q9?b?G00CAqoumOFLHe{*tZ{te1AyISjx0%$SL5m;?ixB&0URn zRLc|h<+e$`RJn=GC@;i0na=J=ipTerzhm5q0s;G)gkd+uS_V}dE_y<#cAw1To(jFs z)3^j*lyYq&TdfX;Q09DmG4G`OeEV(wv$fDRZN3^ZsX_-fvipu-D0U#M^H? z$Y*Z>x^OJ-whwbK+72@J36_G;+OcAsO>Y_-10UUCdPJ}=T+67{y~`bTIHFWkbiY;r z?=W8$bN7!`Yocev_qNV)1wdn(ooLK+2vGeM8he}YRNgzgG+|8-<9U_G>597Y%^#*+ zKuDVY`Zi(Si+*kBo_@!6l7zXpq@jDw@+EKJ6w0eq(eR4ms}~#s+FD0HPxOWO!s6-2 z6%&0~bwBg0n6KO}kNIAg>-eA`MTbcl0dxS=68S&R6>4PKzL+O{OSyifW&UAvEQcK# zNYI&COY6sZk|DT1C&G2HbVCle4X(Bpw8GX7SC!3T;gzmy4mq^EaepBU(>HSEx{=2D zXq?l1!s6bwKQ8I=63%z%b4{Wr_hkEv2>|5;e`do;jEM(s-MalMRSdwPU(5;2kUMq^ z^uYEh`D@3Mb~usv)vMQL3i&v8Lhs~Rb=|xHAqVpM|H2t!c?nlvX#Y!eb_>e;1|>h; zuYOSg!dq^dHb=kNbyuN78-NMkw?!4o{hhIa9P0Mz4`NEQJujjLz>8X20^GXzPZQA# zDqyl(ul(Id1bEm#2X|EH|LIbS1whlSm`|ku2nOyqd6)t8aE1av?XNF@TJk{03~()Y z=c&r6uLoLH;=4`ef8VV^YI<<_MfamYdmvH)w*i7ZyQ1$s{jc*uD)hJBj$26m-NOke zrG7^;mGbWiyJ;b6kNY7K$6c&D4|y3&c|XK}2KcW@^X22e$UgUc#5dc^=1!(~qf;-w>aoPi z#?vSUh@nqK#@dBpPrJ{fL8rTGOsrx6MyDm6<~jChbo~Wd;PqDH>`>e0bs4^E zz^qQGq?nkSlh;)|QY}7F+Xzh3yPxum{@U^=vYIN^p9Qw4``sizgU_1Fp@C zAC0S0(ZQJG$RfWQ{nC>qg*+Ct6K(qlv(cgZ9{!hc{cl9O{VXBs?uO z`L(SsgnVAveZyu+a|>r ztzlL{!eGyGnKS(whu?kvt3{Nf%W?7zH!?o5!7V&Trjvywaa-lJr`9L@N$Hxdp+EHZ z0%L0pV@}vxiFstrlY=6SB)aHGhw33GbzZ_NEh!vH{?Vf0u5r=a@~w|7&r0A^dkv!> z9iG{86*DMDl4iXdA@$&^iDu~QeZI31*PYhFvbxJZx&0uGspq<}&EO}@1#o5=anr|@7I8J{r^j17p{I=- zISTgc=HC0+FLW!ryj`gz7wVhLE5S;l+wo_EbcVb+GF;GxIsxa0=nJ=)SWocui4k|GNFbil&-6YH9*n&L!H9q7})pB378H~Y%#HD(73PoKYr+^PE>#Oa7FUv#uFIACqiB`{gE zITy)6k~1!see|!?5TD;NxEEx31de;^FzW4Jd;TC$AO4`oJF&C25a%Ki#-y$<7N5eJAHZRhE>32ZU?dBjvs-JKM}6pg6rypdFUySR6hpDH#~E%{^1Oe)MS$4=?1JHFx*i{`n_%L+Cf;L%pR3yYye#c6BfNkTQa zrfhj`Lax>41!1Z~$_?DAx;r}~L`oZx04)67!`V2h+mnLH_n{8u{MvDkBxqCtG!WVoh z@Sdcsg#eA2?RBmq&6Kp~I|$v_XQ?7>f>pY#$wAAqvmcqupZQW@B#T)@yXd~u2_D20 z9DW>g88R5G>v%tIamq_veTy^aUqSz_-qGUn>OK3W8xS2R`tgL*W8I+VMlVMPMTclj zxiu{B-hA4#;r`xU$f9yHH|8|-(3d%VaRKu+bef9T#)Z_VoBg@Pp{8ntfZ#at+JJym?5b8B|>ppA2 z+pYa&nyj=xT2MPWVfj<^JYAJ&ev8fIR-8BqSZ^Ody(}_AWI+$deE!s*i=->;U)AAp zomjYLPK0k|tW@@`lY4n(`24hep%=Q!w&MDRL-uMppI=?Kt&kD1|G3(8@TtWB$i8=P zL}idL@EY@r2XkoSK5QEd7gX43@O1&Pjyn;cTr#AtN8J)CQge`eOVhpTJZ|r=NGZ<* zvp>$R_jp}u#a+E>!+v}C@Q(FKyG(HT<7?l&UEYn0Uu^YgPrG96%1S@V`#S=ZFeU53 zL|R&P>B+FRd8RG0aAmtbiGz!CQAH!~rIpcbI({}XFY#2SH-8rW|GSw>6 zMbWq{gEp0-`K#VQh4Y_pi0e&vZAosJ#%Y{;({@}DGK|WX=SBjBsV}5zU)S6J_|t#| ztXp5C(6l-5uJ+2;yily^52aB)&HlGe3u!vAAZdoyMDm5Fu_M>OUnk>vuO0AG>T8!v zd+|J(vR`K0)5;32@})P=s?0$$(_Y$(UK(Y55`u<*HnbYqk(_R9HgzqPDOF=ZjP3MX z5(23DD=!(9<|6BCzKg67rXt!Zk;YD{(tm}CUhNfo^{GTu*QnBL!C+E`0xO}#!eq}u zM{AIt{QxGn_5n~!JC@WSuVXW^wWZxBXK1iPTm$cr*Qw?F?-o`|56MFt+~(Hiq~BR) zo;Ity60Rb}VW^O0H9~U{85H4v{13DFgI(t?8U?(HX-wGp1!UH^NR_D$U_&ST$2J@ zFT3Tmh3wFZ{m^x_#H~c_x+eac2}Y_+$UmU0iMKSj!TSn^yJi5-kb}@fSMOIFKl~Fs z$)g`~IkQ=0Srk&_z;wFjz*TSF{0EBq15Q7v4Ti-emwy5PwC_V0Px_?5x5Z-WXG8!f z`ZwJHACSFcmkhu{6Vw<9-y7hSK&qz?uR!nHJ=*pT*^ewzNa`M$8a|mWlzF^n5E0gH zNB(Ar<{!5K+WZHo8vEE9;s*b1h+LRd`RHl5V6RcWdFt9&Xw-x5{-Ch0|2#S7&p|Hx zUg|(b;}0~>)?B4vrZK)B4|AFO&@jaBl>{^-fB~cmoKb_~cc7rj=7p|Ma#NTvN%Y5X z*T?D4-EK`|DPktCcHO-xNiD2XrT-5J0FHz?O#toP7i}q(W_OJqlf3)zNTKN-0wWDt zxHbAU2=?SdJY_F$1zly6>}#D~#xM8(ah$(_Kl!7S1n!Wv4E-9lJ9d;4k5~!$6%)68 z4UJD=382`F+bACKM4!sIt9{D!$Bcc06zs9)*U2G}~ITH>YmLGC4WL z?jMxD@BZFpoPSN+==Ikr*tujG{QIYzc6D!ukE`*Axpk#YEf#(16>>tU$^Vt={pWTsy=x^Cte)>(3^H)`^T@7k509BHWDGlPr&E(m|Bo(}{b7dNdMYY@OQPbF z5Uq<*xi*e5oXjw>o{pIB#y|Ps1_XEcuOY7eElyCXcZ|vpf$rd6vN_UI_x=(5A?%d= z-{e)e1;GkRAvwx}zQy#^g#MSshyRCYbK1ax2qw=vmJ$aBcDLeZ_a zF4fx~*-7RScJmnY*6Jr%R9eit-kJCo{B<2}?{kSz^UlNlR5)!Y#ud-a+b37F~s^!O7#fmSYaPt}Z+ zMp$E`R8l@5-iq%&PkN29vciVwUVr_R%!b|Q5VPIaEL`FLVTknfgp&oGZF3cDcJf-y zf^BUPWNRv`)V57Ss&~iMcM&8oA0Ve8SKwW=Q&zLJN&EXz(~O(Zkou6?%Fvw$V?beP zWAnj>`x7p7>%023o)Ly+4jgW)`yX7&6$BXUEN)8c)w-i5m_0|z=s)M+^W}Z)bjyPJ z>_oYS#njVk%_g7LX?KyKFSzt8vx?$MHD{qivaZ{3X{`xf+G6Kx#I%7-yXR>I-a3|Z z2~D*(tW!<755BDO*ohPQ&N(-@bQ={=R(mp*=Gu30k*yK4n+lCr+(iaaArdN z%6(e-Ck%X_R5~VvfNOz|wAxJk!Bi%@+bVxVS-67J_AOahZ4kax@8c6}gyEd=^ zd7k%t-upSfd+y;+_L_6eIp&D(h&dK$Mf^?uT+QCE*~T0u;hT^c0~Br=GZ`-5Uz$_> z*4%9@W0#|~Z3A-acvh(G@8QB8e6Vd@I6IRg=m=js_GZ%atF&fLV`}qloo|DRw+;)& z6RQLp+EMimsOP3VMl>@^^RKFuBN3Y&E?thHdL5-EF z8pJUt+HApOHqD`A?fJkl-)h))pqJr5-D|?d zATfT4k{;xq;x(YwtsjWt{6pU@b4U{>wHp#t1=f(TSyN^V-?PNs59{wymV0fUM6Q|7 zYSNq;oe!!p$$Eyfl4NhIP)D<5uG%ij&2THYS%3R|$)5BZ72nQrHUkE$L9wSZZ@rU$ z!~iw&kI10b3DDb7sm*B*DM?LAx#HT}rj;9VZSA?ATPUfey3^KW$`PAbz1*-@Wsa_N zN24oc>IjSwR~o1-p|WGHiQ3r_8+B>7dcZ7~p*+AQBtsmJdiXIwBuLrKljvpQl3Dn8 zggB+idj6d5L{;+ZjL1wXTBWFT|7WX5n3zIr5;`WqDvhclY;z7mya6~5G+x|(vhKps zatoV-w#Iu?wPzHq!6W`od6{KaqkR9UlQ7l$dMf%KrKLu`KiivI_R5_U@2#kRE7PZH z}Mx80JcgKn+Ha)u$*w9|! zy^44PzHyhVKmh{*x#_{aQEFSv_*`23?E(=8wyy3z2DZGB(X?LLlq84LmtN#11B1?C z82dZXdx+_uZ`Z4j&*z-D+*_?*9;Z%@4VH__RA^66Tumt+uIILFT8YTBHcjBFaeQ2#$t0h1?uto0fF z#jkvFPPBZpkK_kuhZ<8gfmQ@q8ns7a<|Z$1EKoohM%c;nJN;~#yUkO3(x9HYcJ)PR z^~36X%>VhQV~^lB&ZrSOJBnl(k?DXL&cuY5N(rIBg0d>`XGA0LjNC;wJTdrNe2 z+Uty8@j@IV%;<-)c~2EuGt%i}J6N15^ZC#XK^J?g)dj%G1=0}NwA)w-E-fHh7$R0) zFsPwGC-N~KSAKa{?e<~hld)CmiDG+{oyXE}>QK(|iCV|K06D-s02!xoD~krP5gB&k z8VIv!OVZ-ZrO_D_RFY&u-G0B*aJ3E&CPVqpSi!zexK^l>O-wol2dlH7TDO$8gm|OT z58)T3u$NI0!-|<4HwEtp0!!WgvNw-#q5{q>2tYJXA1GRaK%M5kdLxkLvAbb>o56cn zDTAUvPnie%Vl#Y^z7;Yl?8z~#$Pl5XvZk*p7VpBV*YHZn`$3J-BOFMFsIXE+>>C|&h?A922NxDJT`tBeo}zqso7Z{NEyX^sx6it{pBHE@!972tY5^nn z7m8R{>sXwV5gC}}NW1=ICL9nh_g|Y2=SmAH+YRh*P6vTDJmIr_FC)^}`{{{O_g&AX&+Doz-+p zM_t?p)6t@jk~X7Oal6F~+ON^^P3E47Y)_%;`p)T?D9xCU%BR*ejpFxNH1|(xIi0v} z<}IX6e4U!GMDLk6P7O8tYYFVIpvE2FJgaGh={gbcKxTNW!cpU|iM5|b)*Jo(eXd8L z6T?zN)2r`0n>*I<#&}nKZ!7&+sF_=Q{~?v`+4R230fxVoMePg2Ro4Od8icFpENN04 zWerEyI!C5JpT7C8i5Z)MWnG8)+>qSd(;SUP1Mt^ApA0NiNqV+TPNyCwbE|zuUA3Q? z*hxkCC&3nN;`o~$L=-Td1P(xY9C%dQIvz4u@mRQu2wS}fV2QjRx3ur$R^8(FJl_&lNSkZHox1<-%pDe8b{RMpO+@dt@j0(jEo#!`f3T;Zwa>=xV zYhP2(b7)97A0^tWm)*FEFUSm!#}x_lX1lEl+D!#b@&)a?QX1LZj$6_edPx9JU+>|5-Hs>%< zTnREXnQEFsTrGa(y;mhoeX^THF%(*5ULCP~z`EqC0hCT%2221aq@+H4q$ndCCE@LD zZG5W-X1hmB5$1O*XPQ!1nIAnpUGpnTK5Ys(Sf3|4fXD%f{RRSswvM#E70tb3f)0%J z{5=84yqDHcsD9|B@XzCFqpMo4-zx5?xv)i~Y~Nz8dtCfoHNw{*oN;Zz$%2Tah1!$P zb1X*m_VzZ`IsWXt6P_lI{Jim_oZS!?qY> zeX((;7W#5^#R__QdRoR3-8#i;1BD)#tBQ)#CPz70qJrZilN{9(0W$>H#`?ZNvBJ&L zL;jWzYISf_oP3TsZ=&;lEiSETKU~VxRA;^+6Tz^+omNF_&1cZRuTuUMB0@>!^}1@~ zH@R|Z!`4M))UtaHgpb1~SbqyH0*9Ar1dUlO!Bd4s^96nn{F4_JCfg zo?Fx^+OE{Jv5#Q({BoPOZk)hW7mR_;xZ3hAvz+?f;+N;}Z*w$EU#;8;9#Uf<=Z)>6 zBmDZ*kJnFo23dW&2(Mq5qZ0v!=;@#?&^v zitg4!hT3-szbFh0Q>l3V`#!!e1~UkS3`%w(Yv7xgdO8d^WajCQ;AQ8Uo1!ts%X_{z z&iPbs4-Y-~UwA;uBH2F16`VjE_^Ynw=+Lx-S_KGEk(Wt{gD)y#Xcc0QcFs>Tj z{J`to1GN+XOa1u}FmgaBV9$3jf@at(KKqB0B!^kpGZ3nI!W)FFnHaW}x6{K7o@ua& zL{Lk~h^&6v(IL}C1E=zKt9Sxd26@(l_+({<+Xz5!622i-ehRZv^3}Uv4c= z3gY4Aa3^p4y{NM^EFc^SkBdbfce#o|0}g^3LlCp{iHoikv}CPe^AF?Tz-%ZHd32Ao z%ouqxRX&sS3Ib5eNzIWd{spl<&HeR?3r2Nwi$uD=-V)kbmvDMhUz6~s_M7>@E0PDU zKPjINT(=LpV$eA)H)c5i(>ltlR@}5UF`fD?RM$mh!0K;Z4nUCqY$LC$u>Wq#q=2Z| zr=AI0rGRyMqL+h8Upqp$KxC!309a^(~`U-~m?KVYp39=CLRqu$`Swu>nWz%%_!OB8Qo%f`{p%f9{)En`ej*Cd!`ajpm5+K*7|^6334YyE=TC^O?_b?#q0Pv%EByM1JhEVFm+2Z)KjK2BiJqx`f zCPUG8k52MFe{LiEpT2}e)y2g*z(97S6n+BUbp~4w@j-Zq@3sLsUbIMO04vw`IwLt8 zsz^ESmo4$)juDuugI|@GZ%)-nq1D2j(3w=W8Z`>lurKJg=PmqjcA$pFRHcuFC#Gjm zY?L3JG*R3V*mdp8qr~D$%ojr`Rbrnm1AS7@%{A0wPKUqEQK`MHx}Zn@ zeUW47B9K{zGMQH0?1FU?mkk`h!oVC_o|C!QDoI`mR{Fci_D=!dY)XpCKDFWOwWy8# z%8hQm|E>-ho9kH95sL=EBEM4oEb!`lb#89&-*F#y07SUIW`?P?wR{NBD~HL)0m_VO z?B=~OA<`t~s#4Bf*X^h^mJW9b$Lj^8U2LdIt~!2NzI`SpR;mCy{^2igC1Yxi&F6%A@$QDenOamB>O#o^(qN|D**TYo(wOofeHrQ~@H>Cwd{ zc3)ClzntpfM?^mS2!hgVG-%~H`82V4Hp4N{Szof7P zet&N^+6osJ>-918pXmR+C>R51J%P>0uUHvC7u6{FlM@fjPT_uJ}vf{bAW&YXy~o) zf9YKch{Zo&?XV-7u(hj&O@LKu=rAC*P8p7#9?47D38&;%S5K;HbM?-OPV9S3{LiWeOFdY1=zuaf{#56a3cJ{q;uhzLaPG>ZC=);Q0s} zPV8tuu57Zq6sQDW;od=~ye26$PVE?rj)Mi|q3dQ{6^Rcp>ouzPQfrm?De_ z0foQ~iNv<_m{R-CFXm_*Ie*-7r~JlKI*Zx%gAN(HWswL66FT2%l~mhJz;(3>&Xq15 zjbIn+8QQ0$PD6Dljf66ZM|CCyo3&UeUja_%kmn7!u8DJ~IZ9=a{C$~X1!Mpc0R&gABE}@wcGEKuf4m9X>D0{eGCD3UF z+C z(%Env>o~%>0-kCkgF^;ZJe1YfDiyJ|1&6m?O&D$Fd4E2=#lIG3bZ9g*70wXYUi_Xd z-15}4Tk|o`Hn<_N5gdvqY^MyUt2mL#e|N)bas&cPfcZINg}+`Z3?69_*<6aLBeae) z0PxQamn3DDaF&RB6LY~&-fT8&O^rDTvER;*4MSEmRrp5Q1!A;_zbo7i2+$s3w45hY z@3qkxs975SA{8~>lO1kn?oKyO0QELI4K~asVqEltaT}wEz)SChtCBw*o>Xs*CQg|8lmC z5Ai2UcEbg@5=PIC(mH2N861cb9)Vd60bYv>TJAb`D@Os34RBZ0DCBQ`QPeqeQiyO5 z6Y3BE5Wl**+UT?%<9AcRi>x-~!JWpbFQj8>x1hSGn%PjzIgxa0rClyj)iRIUCWq+o zxs|H2p(Tz4FWPuO@;A7$_x;WFuDl=8)ts4qZmX=jltJ9oTA+#a??W@Ud4WC%WG4T# zJpuR|3Ce4|y^@lBOv$hPqKGe&b?YeVHOq)U?^l*__fAe{J^qfE?xIf+pfE(b{5i<# zr>Tz{j2V&3V}eP*Z|fhd-)M5k8O1gJiJ`n#vhEU|uzPMxmmN44HVHTG@(z{)Zqu0# zsf?mDuVw%WZiUaK`MvZ54SAO{KoeiDz;zC$ud5AUN!i&(K*|WAM1SX52V1eq=i*-* zI61dsyIxEM`vDCE8l2=8+hP&Mg3vg)*jy2HRQU!*Mq->vxD35pqk{E5u%JXBSN@BA z;9}-Y2@)3m_0XY@Z~+ToqqA7qnJ9NZYFQ}rNkUZDx3VhBHWH_+k^g1;;dwEqSy*A3 zX_Jzai}tMZwj!pqt#w!5>d_X1ZrK!1afw@wAH&`MR=GhO(l0%4=s0Lz%YC*K)fvHK zwaZ?o4u6`_AZ&+RKA2n`VyCmcP&-mhV!jV1L-B`dY#_SH2{G%vKtwMEI8}NT9jB}8 zG=*D9i!Jq&j^*9`np~3Op94-jW?^`|!~3yfaij*M+h=A+Bng9qLET0|_;3)p+WCoOPtp=L5|=|ITCm zubtqeNu2LlK$V}J0RV%Ax|jaZ#fM(3bE79LhcP#kWj7FnB(G^ zC-A;ghQDi{c;v58?TYp0dM@I{4zryh@( zwu!Y-GhA~s#JjBoyxR-ywTR@&^}qQ;Z@=-{`Y;{qyO&60;pmD zC1KZjlFZO#s3}}=bMsdgYmM%Xs`Y^b=gRtGg>AtJ?9dd?&gn+qu4*}F^_~W#Rg8c@ zcy)NOi3LZR%HB(MB{XxeM?cr}Kf}?Z4N;CM{QhgG8ncTn25sA5ym&jiJ0Qcju#Eyr z-(~jmlaYx7f{T}t;3>C7(rm@o!{s{1G6=j5_geUeJ#!qTTO%Pp46IU}4l9rWfdn9Y5(3taj*ebwH_m7Be5cRsIoW1RD!nfu2{ ziL+Dypht4`)l^j#HEIc)KQ>9)Rj0gbyQtfPgcTp990P&+Uk_jCZ67$)471NM$!%)f z_WW25)%2EnoO{+DBFqiK^xKHQ#R7Qjbfn{X6buyjf1FD4( zSBnQ4LoXpdKA;MLBK_%b9ZMZb-YFSSbliyt)Z4$(qiTb9jgQS$JOOG-9TGH>P1Q8}EFfys_1&MyA$B9mo$dPO6#d})c2ohOlXlJ+@ zmV{)V#tdcd8#$0pq*__fFEMG-ne1%IB&zz=o?yt!{;P|2=^bvj7wipV#5GE40WsQ` zk}y<4N97N@55=!)w-oCfj!*GQ4!b-F1dKRSmOD>Mx?Z%26C>e2o&EOYc6yIfC{)A$n;pEWX4g?B<3cEfgO_u#?)8bSs9+Zm z%L49GhHb}009@MD*!1lbcWwx*cKAouE+GI?Uhjd9uP+`KR~<6y!v8#pL(HWl484{O znNR#2%t(g{tGYohtI(ta13EUNRrsPGf0v`A!%IrtujRy{uOn82V(r3;aMd;Gop z7$b}^G{Xo~PXGh;ZkVZqYyGL2h$SczF`=*zTKje&teT!g5F;nZ9$+a;Gj!+L?}+JF z3YX5V%`lK%Gcu5F!8pL>zRMbv(^kUX@W54`i_#nDT7U}0%NmJ`n>$zQnf^~rmum!S zY-wq+_`t;bofx=f^OOFK<|Poipj=Fdwz|56+2r^Q4)Q|isQ{P8fla^mK9e%wc1!YQ z+Vw2-eU2_W0i}M9E;m=UV`}k#r*Iq<$|1bjQst#*@9;C%V<96X7Bg^)c+l51meUtQ zuk6}*$>FE8h`I!e<_}gyh$*mVmsDfB?%MrOHYt?>!X*V?UqGvi6mDAXbqUd5pTRmE zjG;=AKZK!RK$&Bo@Ao8z&%mEE)O3%ulx{2&S3I&I)9LY~whQK13f`^|%Jt#<0hW?L z8liWvNNlAda?>&>whIsKDQq6mH|e-g0bNd!FASBc7%%+1X)opfA_G8xEj32}fIoYl z;s9*el$I||KtB|91XoJDXN(jqJ$BEiQp7yL9}(joTedqMNPXe(-7z9_n(su zXIv-{{ZDS~#j4c%a{U!o9nW*oLi{@)4)9CcP2jfvMVJ}NLszrm9l{AGHBsoNa*=3r zHIoclkQAD4JGC1XNl(5k*`M|9PPDo77GYs3p+{)|)V(}8(VRotXCQsHe44=WU0JT&ONs<4h$T=0j{iGoP}eigL+a4byzVEsA@9 zdp`5+NjNee8`^ioOKg66U?XQiJsn~vI>U|tvF;nh|KckE-ixVxkfdIq!Mx?Y0;LRz z;pGE+qEr>7B^)=O$;+(Yg>wPq)l`cf9|n42MgwkdRCFPF{@Yg!Xd@v}&fC?vZCfNFjNdr~hpK~DW+ji6UYEBIL&%bz3 zPy;dt1KCxE`d>bn6TsjK!@~dO?xXYD2hgX5x!qcpfgAf}J}3&%uo`rBhn%GpL4Ssi z1pikItD`V{u#+^XW!s5uH*{l@&t$sRD$?Wkf*qn8B5pPm0(ewIDD&jucd3~WuR7`L&PI(B~c7a zK zI7Gs|;r*?5Jw|QB!CWUK>Zbu8Cor74) z_WT=Lz_SWhEi{Iy20Du*+tn57FX9tIvzgkDfKofKG=f+2Ps4|3*I$=lg ztvo!ajxfY8Y^XJ%_~^;<;X@)Vx`Z)>o(eZA;UKHia*EJ_?GvNjiN#lh2nPRHAOugf zFroe+aiWXU3lqI}U>_!yqM`ZF04^<%d(d80M0P&eNO4$LF6v4$1aiPn9-fELDw*$r zP811js}&w)!=9)~-%v*vl{(&M5dqv^E8^>{4d6@*;Eu`n&btI5U64`H@oReW1qSxy zc$`|cTt{OPoofH z%>g`rfuDpPh(6WbPUXICw1J%3wSYD%)Q+Az6~w1bg~F$I2SGK-S_u+jYDXBO`;I0m z`J;w}Jw?OHTkYPKD^Y0vTVSGO7N!@X(Zf6vETHR)xsZrj*)>-YpqcNe??|ughbnYS zU3b-nWp$lr29-~^2OqEy{9X~{DG7_e*Y1D)408cI<qeIwf%wlWc{((hoD$)QHoHgt?Xxs{E3m@g0g~%1bjJL{ zWP7GG1XAXraaJhS9t&n&sU(8F6!^BzrP~Yr=l>aP{eQ?%T>1%q9;&ydUqBpz^yKh`JgyF( zqzk+YRT9v``M3niGK~U;_%Pzq-gYbwh8vO>?lhFF0I{>h`nXUgRpDs-EhC{W2-G@0 zJ_atznX2!-S9w9&+yG(}e$Hxs+)%|3k*=yjN~VWUGK`@^v}A-^j)GW=dYz3fB1@VD zc)zm@Gx)^(flkrIq<&>-^he|Uj}hQBgRiT;2H;BVPdfIuq>}qeVtJAjH~6N)Pg%P5 zBC`%fA3z>W3Gf4kLLDndfQJz$525*}3-JZ~P{0}H`2NTXjG1nTlcZ79?d$n609&St>--N);m*0XAQLixqhFe{9Sq_+;{u!3IDK9 z0Lfuu|M6dlwhk+n5CE9=LQ>8{M1X7d|2};4U=8AC{OvqE0gOM?1H8)fS3Se4|A+d7 ze`)iK=ri~Gs@exSRRhle?^`6i`tN@WHQ`k5mIpw3#Q;8#2N1Nnbh&BOkjZZWpHu>V z(Q5&y3$0Cp0bf=dTts%xJEp9y=uHD%?}%XTUJ&#=ZuL(O_iTkY=Kv;G{}jMjGB7U# zLa59cl6z0nLTozAWqjicb8QSt(2(V-<{~BT1|f2{^7SD|1?# zB$*9WG+!|HWhCr0^_D2AsM(8WHuO2DIV=i0fSzf>)Yys;^_Th)Q2dO~R(laDfRYL@ zo7K|Cc${&qng93=VA$soy>OK(;WCs!h2e1sR6fslLHi}R^mwLijP#aw)H*#>dd^uQ z_w?Gr>DR!!fV;w%D*1fyUmW-UYb5e7tqnb(OXgezt{gi2cxhvD8J_-UbW&)rT956^ zT-qa>>!GPLoeEq)rMk{CXWDec3n;Vr=rMM%fi2b7Cq_%x!-E&4F2{oAkM!9>a{5D> z3SpU;U4to+^k8;8ryGtsYHz@|<1QoQFR}sM3oz0bc=3GYS75~|tq(X`o?hBXNsPUn z*M(GV4|b|$JMPs^*cR0*GzYoC%H)Z?HHYatiyD-xj+f{H${Xh}8`QZhrWm6jp-7Dt zZoUhfZ_GP*mob63*TArlk0J<{7d8LFYD2m4R2gqk>VZrBEqVH5SRzs5IV8Ud9qB1v zC&F2Yv=1C=7AQJF(lFmKcQ{=((9Ksld|KT&nVaP6A>yzGmvR*Zal;T#BV6Y)BM<~% zOTZ@EET~1XVg$F}afgqIzVDHfRAa@mWwK0)KDs!jh>RPa_Nzuq_iYeb`Y_Q16KvPz zJ?9IBQ;@gMYEC6<>jYbNi+CFO`kUNA^n)C?ebAH*l#qstqpUQnHO9nI8shqQ!u}~2 zmcP22|cLjbF;r)s1FK#6WZN;Bf`{&U@ zt5G88rMqz}GY6sY70O_Mom9v%0vQTnUMKcx&WoW)u|rSA60J;K?d8keHwxvd;G>X< z)sj0Hum9L}DmjB0(AaoIkKcvTm5{m8)wO#!joI3bA%IdOdJ(Dv$6R6W0Ns#I)e#H} zCP=EZtW2faGCbGtIoNN*{?){%jPHbx2+WI~r0EBy8Tju%b*?L(z)4hf9dMwd85KyW<#z3RhUf_|6i7@_+7>((NK&qp`|~jV!G@Vo zl7xX@am9X6vncn`b5f&nhGG@gEV1IOWO1eEN3BsjR-Y*@8ms{)iupyV?T5m)-OVJP z+3ZZK+(k(1J-|*sJP@9^pEnkTkS3{A{4B?$u8GE!_=2oMqtum)jlV*AS}g8>G;uDv zxt-lvyQ2IrX!2imc7p`+P75UdcZXf6e>M&|qNw>vSheGKG3j67bk&uCGHv28F>@vW z;6UDgnOurW?mO#t0dwvFibFg=d(-&(hNj%^@Hyo!z~OWAJtFhbyXi7D7C!a8rO4Is zN^?6ss?#QDC8m=NCfORM+nLAnC2Vfl6OV`5KtRv44PI)$CD;(ri&sMmWlf`E+V2vZ z_OIZ_GdV^o8r(WqQxP%9YNdK|6YIt07EW1gn2ho_h+g>qH&YNH&@pY2g#7B!LgM35 zF&u_nsR(<&eQI%<`Y_`IEKr=F1z(L;8t#^7X`liE-LKAwud75=we&DTfwCs zQ7~*yuP!M{2@v~SM1kIanTQvRcY2@kg3(TEKrbeO<+sR>D_DP|Hz5iKCBF9I)i%j4* z&p@M~)rRWM4c5!Gokk08Q22uQhE|5y>4?A28?I%F-8W^}{QxKB|5K!v6z;&-ZJ=L0 zxE~>S`|8N&pcKUl|MD&JUm*G;0fQ>T(%yIe&)#dz?!$Uv%aOFW?Jt(T%RdhGcz8wq zse>m9pO8ldf*`!N7>bpp;EQz2rXe#`IFyu;Tm#&N`inMOFd~((a{GAv+Q?JK>-fE2 z|6R(TyLTQRerq8&qy;M*Wj`=q>NEGpPTU}C(*^I#P@`dXnGMiwpaf$990Q?tSAh|T zAHC&sUy!j7=D?lH-MfQU(`C#bhsoYEq*iJT9>3C5(wjsuI^O2H9Jzr|#ie4!046-^ zM~Qdp^ju8!KcbudYfYGWnJG`CUAZ&1wO5El-$N5OA2W$znLV`UC zg#MOJhk;l=kr$fh;xb?DMp9uyfI#6c$wAz(wEXj7FCP&|V-0&*gh6UC_?PV~O?vU> zFZ8OX#-FG0IFjiRMqlXDS%GNE(!~-CRQ#SL{P-3JHWnxO91;$kBMVR?{3AXI8^ITs1J5f!KzN_;sH3moi=#}+*UB;(_DkdXxCtb`r{>g{{s6JC1Q+_` z{4$e2w9{+b%U4U>+e? z@i@}Re>_wyy8qi=`NW0hM!cb+BEj&5W<-4a%9^|wU8i%fz?EaX;15bLD$L)y@(X>d zhp8%k_Or@yp}tElgMO_ zqx+`!kn8?a5iul8GTy!eLvg}xgV)lsOz6Z&M@|+1KNvd+t5QXFm;gB_Z)Cez7$pks zWvt5-b5fq|BpievzOt25yOiY#yBtO7CRHd>MtIVi7>Wjl{xf7b`mq zgv{S!Mzgj@MaQQrERTIYG+WaP->@weOY*2I9Atg8<(RXpC1m6xrr+Qz^#e2)Ov>B|9_E}Zt$Mx^}F$caD#e)$Vo z5;PSvql8WM-=;#TetgpNu(dD%JMrOEWoVH~HT6Jy&Si)8sKvIt+&||*}1mt=CH~vd2dHOkbJtu(7 zr)?JjvKUhv4iBCFg_r|ea=r{gI@x8jDGW%g3+6le)dj#@ffO(gxqxE?+Uma=;DoeV zTNNNTL8N;0>RT!~^(4@LjlwPvW;f(AP^(RB30#hd@?+F5C6avX;sf9h;|byYVqVG} z9nNw!n7Wna_D0xEG?DzqJ9ANdsiNL?89V&~Ts@+h8-|pSn@# zk-b2KK}POll)_uM#{=8@p#OCd=Ux>}>s>E! zi0UAZ2ACjnNa_SxEVM4ppJ#78R5-`TimuBxZ|HkW zy-51P)Ew^J;Ww~L*zB1glZ}H290dno$!q))Kdn>KlZHA|ynwnhl+yNO)3AnJdb^Xs zhC9}hAokUF_JNhld8zd)!`}p~rwGq~;t^iLQN1O2*UzasFU2E4$NgHG z9o74*Rkib%MwdPHYC6o8kkZ@P$uu3e38=Gj(JyBG%!b}=~f~;+@r49)ugiH z?uF`T#`)zC^deQ`n-Pq&1VAv_h875iyv7sW zkG1i#+747U$+)5Uyk!6TRw<*wTyZx}Wgu(IkP)cpPnYD0VsFxO$B?FkI@A3ELaHat zp;NE14jl(9%iAW3CRslb!^GW$)O`VmcSK{jmqzpB{x07!1I<^}vgPfw>2NDG?wW7M z<)X*1vD?%3_5~CKAGO_&7@zk~Hr+!r@5mWtcJ6g?ww(OXXwU+1D?B9J4V=a9c5tbF z--esIcuKd|UatTRt=n_L-sEP0<<9DEe^Lpm;P zQmp`NLHq4Z2|?Er;AF^_Cln-pUYyF`i_-#I;{)`ca|4JU3Eb>vAINX5R_uNvtU5EU zxJpXVyC?xCR-GN3=VIyEYz%(P zI41lgD&Q1dUejR!`Z}-T=8}t~GEL;MeSdFV4EEb8xVpA}hz>oEC|wbs3;lKOh!G4& z{Y;yd6hcFgVq5eq;BMr5zMNecO_OiG<3;~QT_%!gS{FoeY{tD_MaT8cpo2=q#YZfLi8K*!cc*oUMVQheo$1moWvqQCIa%3{4Zl(8dQ}}75D=pz;QLO%->&azl_zLtD23|w{{vco{fC0&mslqvBuNYqA7mVyQo8b0${34l5h z=tsK?U#R?nhOK9Q!fQluK5Uyl1@{^jaMu*+Uw=5ayvq;4=>|~-cj`NU+*4Sz%q{PF zG3&Y_LMF)I^JkK3qT=h6`%@%0SYTjS3=Bta=*>l^Sod40S%0L3ZDJSWB8I_%so%@DZUL4LMIe(%>aK+2B~OGeCt{5ve71nYaQQMwe(h{~DBd$} zKSXhM(IHrZ&Jf9!ecV+2a-FW?)(xC>+VxKh&PA=iG=KqQQ#5vUov)&&t$uS0F9fCm z-zYl$TmmuTB*OgV8VN7QL%yA$haLrxpi}H}V~`s1CvcEGS40y!T@gk-eXfL;4ae@U z)Bdba$_~EbKIgB$9zLtWi%VG7VqRoxeD@37neIsoJXlq8n!tfc0|7gSCOm|}T#yd_ zCo}rvBiU!ML^-pw(o?npNF*e)Wq89f`#@l_pleo|fMp;{jwk_%`c_~v4;x+>5F2^L zG;}u=m1CF%&%eM%Z7j_QpF`pek6T!Oe9a{4%Q8(ec7uG8kp!$`$depEgj&S#bWQ3RyDi>yZ7vQGuFf0o?zK~)UABC$fI>pFXZucpXd~8PkcK}XR zajWWYQ|yJ!@MV#pVAWC~D>sOuXpf*!j!@xtKHFI!g?UQ-DTFGuRY~EKrD=Y8KIpc<&6q1}*@e zPX#nSgMgXXPMSR~yf=3mjue^G^F5mHV56Gq@V&zD{1O#HWl<=!fnYHnIg!1sKr*iU zyS@kb=*)zwI?t^DbqloUWRxPM3gG)?5CwGR<*Fi8Pf>?xRMmszzAU4+FY|j<3r+PT z`@eGnktxn^Gc73084`oWtIxN3Y{57vwrtjp>z+#5*XR49eUNT9Bl^TNA= z0R($?^pT9Fu$9SR_8?B>%QWWJGF{sf zBmL%s6(GC?d_IH(__Uh7k| z^>%!Vd@3Jat38|FR#*G1#6K{q^%HdX+bUuzYxRKxC^EI~8}QKtdCXA?Rysxf1=`FS zr5h;HJqEk<3u?=69FsR6B<#?z^~#mwjtQ#gt9$h>Cp7W!Jzx0VT4JD>mHewauTP0f zPlIQ z3MXuaQtk8&yd=r+SoiUw^zhqQ(hlrnTBJ>V=wPO;`qUGxmO4nk@>N>MQq1cQAXvVo z{I`XmH(?I)Rq4bW z!slbLAH_?y=-MIDSz{USMv~j?Jm1YBU2jDZQ*)$HlC7J6r+`au&}vGb$HynQV4Q&z zV`emqgYR7CWZ$^<%gnETS3_aTyq~+&G{`u4{8w!K%lubcVES?!#-$8hda)@osjvW` zxAm@7blDf}P8Zi%WH-y1z9_`+<+?qOvKCXlY|ZZpb=4?^h9%Vo*qObGgsV=M{PezL zwwk6^A~b)DXFL*5O!r326S3Lo8I-XE=QYV`uIl8o-55lxm8?L9i(mFJ(QMIIA`}y8jw6R-RO#7;YE#s@h<)u z-gOS5oc$cGUo)v9_~BRYCVhwF;eSr$Ig%`@MLbEqffP-V2P?-++FxGEk3Fa#Z9Zx6b8{9@ie){}toRHw5yIH`H&QiRmPqp)?r>1Tc2>-Bq z9%(XDl9I6&4rw^}O|j!Ly@21wRPfP@I~eG*4Z-ZrKJWT;hoX>^$)$)A;iXmg{oi{F zaH@QJ%C?rXMui>Uyj!y2#Jb~k$A(wtv#?*0efUx?o-6!g3*G<~6*sXE75L1sG1?A< zalFVUO08#PA$^7i!pSo9o{9_Tf1~c8%aMAwNAVQl=MgHsp7|p*fmg zn?dlhc>TPtSSY};T`PUUaE7wQ4g=G1RP7`DwqniC_TMNuKtI^kuTM%l9ln~LV3mJ* zo5!Uyj$%nY@@F;Xi+6?JqfS!pjJQ^$%@}2|iiRJ22Gv@f!U*bQZy!l4k?!JiKC*mT z+cnsUN^D6q%}wkzl#N+gfI>xK!(8(dm)}Drraamgp_n;3!c5mtld!lvEjCsW!o2i` z_)5lfA{)oSt~h@LIDYUZ2YY;u6;MH2zcrTVu`9kbqEup*--CI}lSopV+AvlROu2tA zj(y?#0NHNVQ?Hx1Lkd=T-@g_9i~+A$7*EHF5!Q4ww<7f|`SQXluMGo%Elo{Aoar2D z**A_Uj6S~YF>zZu4?TXJPw->%W4kkml`j)wD9TmoH*vY>49UJc+UvlXX0=xQK#=s+ z!*cK_gpuskd+(-x;Bu%ocK?&HN~Q+Xqm$(p8|nT^>i0hnN99>mNI8DqvN$~PQJN~F zZCNT8UpEwIQ`tba{1NyTd)881geY{cD0C5CA~hM5-&+WUGmp701Te4`SxC!*JFzN=^yP9_$wV3sjQ@N~z;tju^Q?4gVXZf&x z@}_G#*{`nMLXUjI?Z26^b}UBtmObz(tSMNwMv358EYiJSO4ju!$O{`WI2zqrSsK1e#f{OnbWi9pYZN3#q$tGb^g@vQaoPqzo$#$Wv%@{1wtydQu8+% zl!oOkKRse^6@bhXd^c28yfU{?+mp6@?lbRbBEva78D+ojz(5#+ zv*0+k@>H<5A|{LNBdxFyp0REf0)gx;;+y^?+Cw9>tE+uZcMlvUsu z-2HJTm7Y;4D&irYCsaMu%Y$N83rzS{sMF=_d?oqj=-b=Q61n8A`BRqN0>oZXKN!Y^ zq6R;@7g1DyXhG#5E3XaO)c&BRjLb$Q9)^yIs7~iBl^UCfkDoh6+?#*fI3IsO3f&)3 zjs@{sv&_3eLN2v&LZgrJ&EHv*J`CuNmVM3eWmx!C?SnR{-RPzr{?Z2S5Uht4#b}(3 z9~Stl>BW%|mb%(QTBZO6SJ^@{_{GR15O-8!UX}yJ}%Dj(s&f@qKSS&i+9I|h|}>wJLVBJ=I55o*;wmz)`-p9kD@)^N4+dY z%4K?JggspzZkAR87@h*IWW1k+<5JqL>h!LvF~P8^#0;k<=ZV8&yeD9Btb2czwH_@< zIz19AOZhD;VYGUbk2zqh6(-qBmeJHa_N5u^OG%7dI0uLP^8k-c<%|>NFc(Mr&P4Hq zsQwO|r$=KbCcYNjCcAscGzw2B`07IN3pifZgm9C=9h4>f(6G#+O-8Hg*SQ6YxtCg1bAxEx0efxO*Vj26uN4?iSn~7J|DIJi%dc4ZgU$U7q)S-tX3} zTlbG!bx!Th)Sh!@db<19)2BB=MM)YJi3kY>1_o7DMnVk+<^$mEL_>gk>zUJWMt}Qw z=d30TgsB=Q*?()mnu{rl!NAnUAwL;@d}|{*$b5B%fkEs2=X{4tFZUe==CxQ>LQKQM z;3ymM8}7jKP&ezXFDM>{6&Cm%O#kUUS;Rs{_}rYDpjBmt#cR@>f$UUyrMrCMoRz$# zty-K;^64yoV{m_ZQ@cEZ_O&)uWaD+Mlq?a;k z6!!19((0oW^Dk8;k(b(6Yi-vpkAxS3g)wIpOZzUi5%h#vk^BrqPcvCzAm$%3k@MPj z!Yt4c`+oGp(0u&wv_cKj&W8@Z!E&SOL%F>+qsnzUx^HoQY^^v9jM^!nAB+9T+2Zpn z`QYm-sMR%#QECvBTI{f3XY?Bn_}giKmRKhPT5*Um)P0syyjBkN3>iqjT(WJ@u&fsy$K-D z0)J)IOU13oAe@L4V6Xk5d7up8?!|aga-es98C!awZSGS7c5ZsKQ6c8G0wt4F`3EC9 zR8P$hhucN;Jzv;=)*N!GSbUZU%`ppAPbc4XlkUhqmvYwA^rTlD!}I>%;#LJ{=0lds z#PyAlGjs$nasGzigZm`Nh4aq9mv445GK16huB8V*MhfJkHv9bp-SU(NXu7$5OpVOc79Cp63R!G05kH~9WZ8JVMWh-Zl5XymLT1i6fqySBb z*9#6Dqxr2oPob|VNV7Ouyw~UTtb3le9pxd1{Tb4RlM|_jL&0efEN`E1pwA9UVQuFB zop0ja{(ezHan-CCOGbM+w(i}SS4u{-skwT67p8|6+_7?39Xj05xcML2ZL>=C4iz)) zRZzfyE#t_)s=oW#NE14V@DWgm*$JF!aCa%tLLgv|sv@ImYirxsKsWnk&she9|s#<8iQ=rDr%^QO zXInuRUvQOB%%BoNt+9k|Gp4jPF$U=YIyp#0AqJs$BZCv+>^3k}MTFX^BN(ABSeW8# zdR+(x!hmRb*-d5Hnf2pvT1d6#uG$wQL-s&g?FNGrT2)s%xtcOAbgLhD&DLNE%Oz0~ z-$6jFK78J_#AveB(gRAx2qW<8>r08@EE2L^vX}ub@)}v@ziiA7YFwLTHxsRVEJS^*?xd-i(4Ly@ zIZ{)!2hPSp!a;TKxj?~2#Vxi!L42&qr1Vwqs`&@!wy|&3pfQJ<%}LycJxC4VM@dT= z4iTIGw@9!LK7uCfsA*BS5=y1{>5bUoD$VyGUzuj`j+Q@Il`-HOJ2; zwU^1^CrVe)U1e;Tn{MW({_kas8<#oGub5ml?ia{y{53IzE+iyW07Q0ib=|!5eN{GkjIlj2z}wZ~_AE3G^a(G^UARh* zjo5KRj4~w?gKw2miK&V#;y1;^V_ud}0#&9dDpBxvLNprUU=>CA3~k@c{~Ar2GhQYz zEYGk|eFA^eEELmj5RZ$yIj^c19+0cz;^sPBgM2MT7`CY-tgxPuFjmJ?F3!q%qdHE^ zZHb+dky%XGmhmr>##wkWtNzt9ls*NiBQKYK?K0GmmiY7kX*lw0L;pv#UOU?>hv`lMSLq-4B*5BKXo=(3o`k=h$ho1p;3+Gx5Nl}L0wxH7xLHQLsCJ%dS~wZ zOK+lSIrX70^ttM{kNxd84E$F7s?nbnTPa42{v#HfUd8#ejY4Ih&YQzx)?nxRVr150+ukr>x8?{8n#o(2SWVU@0)7;tBhIl$Kf9!o1TI6ZNrOo#lex@s<)Z%G& zY}k@ZVTTtB(C9>RGFmh}MRzj-kaT@$e?%qw;*Q9vbK?@#8dzc#-y<1J@en%d5OW@T z7Ke3^5rz!Nq&%|RFyqp*jAG==tsuIicvWa8hfd2u1|KpjQG1+n@=EMm%jK{r%eZWR zEmI1y$tqc;7%!mIx*}v7W0@P-{VEer?(o2=DCaoNtjS8k*L z`M{AF#l4)6kS!&LwnNGdLNQZ;l5OgJ! zbR-Ho*y#N3V=pR@McEUcHrgTBUn$xV(=VFPrc3$yYsr&*zqNS(#VmHVfi4>{Xl;av>{+nh4#ZWjwL=<9?a90T6`g@)^ZPSU)vQBpYM#Pi z)L3P`z!9i1jWkee-%r#^ZaghPZ_0cz3QwE2D^`@b(^?HQ$iO;LEBa<@P#j!pr;tOFfa>YAB)Hwq95@PV79s$jqvP)=Ya0yTlcF4vedw!8%ZaYB~oYVwth0 zXGYgt8cwP>V%k&KH|MH&0t`D>P0&jus}NfyitH6g4@y}T6rHaS467uim;iD)PJd6< z28=7J(ms%y0bR6h+RTnS;x<<@I--bc@;`b0Qa9l~jfqz^@GdpB_782XHqK;a{(II~ ztM)%(P8B8wk}Sr0{Y8CPa9wjx;-XGXU;A=klIg0*$-Zc(zo@)uptr22AdmFLjA86^ zmYZOP8FBCQWiyqC5N@%j^}w8hJy?AfZ5I26K*Cp2gALLza;P{~v4{AT9~al&RX>Au zjJYvk?kum1$WmM)vCKZ9OgU_c)#}^2Lc7_a9_n6}HPl{M%78}EbY&6BS)+Q`M8S&A zfvLP~pdPx-ehm$GR<}7g6d!7Z`w5r9h}!$$Nj}v|QxwLA-}>j^J3;Vuhj})8%Z+hp z<+Gy$Xov}f)-z>oTzO>tk5d_%B;40*mRAe3S@ojURNS@y-qOp?wnrW6Q#6!nsP9kC z6-tPv_a9*t4*l`{hlmo~uHw6*_5A;F-f8tP|IjV#*I)a(qFpn~P#N?85R}kguVtU8 zk1y`QvObw%56kW9JIBC(5z2@C#z5nsS}eZZn!o=Au_H@cNeS&g-2o?__zdb>L3>?7 z!#MvXD5uUsVILbF0jqK>|Iy;V7ofF;hQj@Gft39PrKy#VBkO9h!oMUh40wZYnxW!0 zpKfsUyxV`qV>mA9;b9r-_`Mc`{IkV>y9WOsFb>M7$`?Q^Q6RY8y)?6ln=5VQ}sX8BKyF9 z4Tbep1y(^eI8zye2K*V}MIGEm9+|t@QaX$`LZhm4yIh}aeL}7XMA?` z|EmDh@9gUAdunrYR@Y62HHqIzI_I?Y>rc=#5hiwk~`IEyPM0<31)A3e?U|+{=qHKO=DZypM)9~5atcQn(?S_Y+M6)8$j73Ik zT7l%SxD5s8x6sW?a`n&8*DMST<-?PcAM*@cljw+?n@Kc%TdVHtk|ri5{!2@sHZzx4 z*3K(6!d-e7+l-71xZ`SOsCHmrpv@e*v{zgl{E!t2Y$LE8W|jIAt(>SW$Ooc6Q@>9uyW=%^L0NGT{<&&49_<((0Yp{u6su zNC*l&0%}_zEa~c=U03^)PEAe8A7gR}6;DT`QDl023?Cn!| zPiM9K2|@S6TH9z>;V~y3ufCrEqRBwX}O{gykzt*Xb7m-#DIhwnq%yqb}ubl2Lt$_ zW4mr@a&a+YX-Vhnr@P0;kw7WkU*!Bwu$*S2@t1Q1e;P%68&GC*I|I7b$P`-tdP(dj zCesuSB{WDg3QN_k$J$vp?DfyNF0{K>cMO#%q!$G9Nx8Zix?DR)qbC54$ ziKl!@*kEQ%RWDW21cq`DLYmw~RPvQ~DkEn%!)Ly?72RpYJ9ZmY zZI(xO1)amx4VlO<&$)VRjqy4@yUuT%ZS*7z+__@6Uo&k>SkpYos12Q-tJ77yt6}&r`FrH1k@y)pL1#&)t;1 zg)+Xvu0j!QbVw-7}>oZJ6l60U49&YZwzJ&XLS$TacJhzJ|f74X=3yY?856?+lsbUt~NN+~_25}cZf zKQ3_UGnylWd;&CVZExRjS+$gqT-4HlEi>~*H}VRSBaB;10e=C3SeZyPDV^^_hp243 z=&W>C|IYiWnF%hj>W09J6`+{AMIie~km&NhH!`DC=_axBi?Fb}64muhlqrPI5ncnb zNB+cdOHdrJi}4LpD(Di2x9=8(4GHeyrH7Tu{X}99mCD9Ic2q6dvbV6qZj%fD#AILa zW-F~B%=7|t!8^|HRa^GB{4SLQg!yqJ`)myt+?UYpPb_=RGraWJ`jl6|efHThyoQ#R z3}_^L-(zFZEH&NR;1HBgS=oJW0Rzr-xk5oF*uj6)iCUtSbF~8;Il=|_A%0tolgtc(RTwzjp zNz+&|k}H+`yEjMo!{@ip`2c>9O;6xQwWsM107gbRSxfq>cslks>$IqA-q%Q~dfV_LL(9GvT}ZmgPac=VwNjN^oF_@b5nY%H0~R1(rzjUBB~;i@kF@?r+j;ESKBR za0^y0G3RS_1ihhNe&_dlX>|QQ!M~9;Zg9Jvc}fNbP=7kfqLhA~QPb1={E>G@M7pXT zPj~qUdNM%HWAiI)xtY>(O|Ujbr8V0R8BNSVrbaJ!TtqYN<#x`W-PVkapqYVmo}kPm$1DOPGX1NXfyS10L8amhx*@1V++V`l?23bu_Kk7DgYSP!PTyA6Y%Tv>~F*me7y zO&(YJaxbies0x{y4LWEuJ7N?GO-`lWh}M2ly?3O}?S{c^m#Z=PgGE!EuaULiyU*f& zhmauDUx# zDse68#Fnpu7#Jk9_Sx}Wg&r=~Gkz0sRFjH#J@DjLAb6}i9I1~mxF=Q{K!}T-R)w}< zJ&gMm>QKT2Z(m^}R;}s_^w2I0)UZFMraLCq;`y+tt%&UP!OZaCp2_6SfxvF5?;INK z)@^G$lKu}6KgFSO!_(x8qhnz4u0PB*Cl$}-<6~>EH}6g5han)sdC91*>2&$D*3AH} zMDw3aCk{Clr{AC$56&R+AbHW(mDshw+ zbKAyP^SssHU#!*KXCIoXo6K@OoQrtk`q;d3p}4O}j@Hz)xH1*uefpU0^QFT34F3$L zAR(Aah!u+3!54B-Op*;DQ}sN+*rVFYrwMRY>|LtSe*XfVP_3 z1!tf$#HoGEW$Z3jX_hJkCw7UOG_OKOjGb64W~yUh-u6_T$a=T#Wt~Xh4s_TzO+{UHUV%Wu_IP|lpXmgqmS`@* zRmf+qQesDjhL8gWzdWtGUYZKcs=m<_F#&)IKLHa}5^i4Tag~J;M`;Z~9%!frubl{g zTQ9s!?SA?Q&-S4m{mY1_yW@#gX%?%*QmNBgR(rL6p!rKXHa>N~=(=iv_sM-%oe8}_ zZ1dJ}U+3A412KT$9WKi@L;kREXKgXa>$gW*H$NWX&!;tSM?O|%L;_Nmt?A+t&@5Dp zlX)5))H;bCF-0q>*v+N9(Xc`xv!bH(LnXF=c;drg4(*j|R?;Pa*S!`yXcn9Q@N?%tMeZMklGouqpe$`i!`f+uW zZ|E)$`y0C(ADicl?|Mv0@0M3F?=EcvTKFztb##_hmIXe3B+pWtb3t)>RTBfUxHD!| zw`?Nmxz1|JC@3Tuy3|9K(c17#G~(kz+qQ7|YWFnz!hRdD&_5!i=_)G5VD&j}e^Bo8KJ5&9jg%{HRUP=&qA=Gm zmY^9d?AY0EJ)l0a($9aITZKPIv6(UFjMLWKhzr}enHQQk)leo#MwZBJDiB8@Ne`SF%Xoff(PZ2=!?@_Su{G#TvhcwqW8O>oH)Ngp6JyT{0T0Q80ioaG95_12^qiE{?^6X;9D89*R>Fs5k2g! zX#-k?=YB1lkYbn_9aguVtWWOX=zg|kN%{Qs^YI@#|DgEzPop{6llBo2^>EL$qv@-s zs7^`aRdx;T*J$iW=qeC8HaW{tL`r`q| zXg1kLa#r;I5ctTge6LPd=t`r@RbFeMJ9u(xEwAYPem^9W{aEq@JNZ$)pbq=Zt}RyT z;ggkxBg#cyRKEf^!0~ycTI41sNj+#}_Wg(CyoEZHfDax8uXj#5PPC?POY zZEVr@47a%x9sB$ zXGaHr2?p_wLox+*{nk!esJP+eOXmvb!QvajjNv zH^K8^7r~y7_ZuoN8^qyA;J&P2hXhaWzt|*b-DeC4;b}<9-J%Q_A1IAX)2g+1w`ODb z>w9+qrsPk$vI882vdd(~t?T{TCAPSVv{PQra}{5M-jv!soPQc~5=*E0ce0j2gmrbN zTX@1RGQ>pgW3frsPlf^#3t%@nZKmke&8`#2RLPXWTi*Q0kD4SFik09gC)bQoRr;7e zX-za><#8?hmUp201FhQ45lea@+F?OuitoUcvtBm}w-V~;h=tb6aRF0^4D+^7DSpfn zzIPZpxtN(*2?sAghAl1U7N{4#-nC}u^K>rFr{{`bQ1En%wJ`&m@xa8V=1GXU`v_$H z_(x_t`nF3uNKk$?*C&wEeI>w4^$Sl_8~P3p_j7r z{)k!qT;5)NC(=56vTGeE*6KKW)#LQCZ~EnaLrm~-&CfDF;3a6&*8yxG+XE0TP@0^S z8GDr!lGelb95vDzPfU#j8Ljb@6Vb6%Z0t*OS&^K3ppmpYa- z$_tV&{Vh9`()K_AiU(m5;K2C%le=6;bj;aEh+m%dJ4Q|ZDh*aD7UnCEi_#H*__?H$ zF1TLUD8>GOnB!lGEwU{uqdi!A!=ME#N-9Llr`%5=3TV zl(AN%df6aB{-KL+n&o!ylFIh1dO1KK)pKN*Csr(de)ubIBoo-nh0P~l+&=YyNGHUL zr&E)%`dC+2WXMicMnO4^>gFrXO0UeBhijJDk2NoUIB9UzDF**A_9`$t@jCK2nOAxX zU6OV_cUW9R3P$vL-(V{`J#XR8OT2G;xEO~@--$;=bhlo&Ak66WVPKS>-P2e{mb=y_pqxi zdq%@Cd&;9?wCU0pRV0maljlRw>kr6y=!3`cTBmNhGs~N!b~XtqQx;@?2#H~I3;wll zhqWoCu^sa3T?7fo*P}EM7H09=&r_g5tLR|^#=}{@KGV013gX4+)UuJ)pTApMQTQTU zM#`0F8wn=}NN&WOwU>qD&j_zidm>>PcF+azpPD8R!a#lrpn1h93X6loLcW3DOG=ac zZQp$H3>FZYM9M3$5g87d(zWwSnIbZ4=M9!;Y3(mx?49nKyg?6-JPf#~b6myOy_%gX z*bjimxSLT&@Z0$4!J_?1NKil5pXHKL@s>mc%~(IXLn&7DgOZB+-}Tp*+qy#-#I;h1 zhndsu-AwzGpsdOIrizY^Zlmgq&XODIQI;Is5b~-HQ5|b(r5kH%75p(U%34)GuQvRcRywysViLLSt%WWn-e=oV`5^U zwl?A*$(RZVhLGv(B&oH;d{3f9h4AcphG~O~+f4p$zW`Y1UL;GcqUphw{gub+cX`x9 zAGEpT!QaA~yVIv>sH+@eaXQtrupF}ARf>sE903DjRX{!O4dHyMrKZ3@obo$6JMY^# zkTPW+%Io571QHKKGV*pp$T<{&(h9j)5cPouk$cpN*;ejJWXQL%c7I+~{Az2f0sOZO zeOb(SD?`VT40D0_2a|eDuf-{!JLjU};&eP6v6Tq$F%I^y?0T8_QhSQm&hJlq>P3sEV+&*jA) z*_G-m+zD1em!A6Sj^b;pn1)B+pE>`l&x(46=N9RmGu+rU_t{a!0M3MZ%co5gAMj3g z$u}g_Gl}n|Ex%3-_zdNX{fUurpdyyR4pxh|M^ zKF@n+Y;fO(llboLrT#uuucB9nhoq5-z)vM$A}GrBedHB{i4%6mG|vdfrrz7yW6@Bn z9krNl3ae}|-Yv@Gfa+u^JiYTuc_t}d#ox+%<>n)}!qvMN8#+GWW&g6*Pb;^U?;TTB z9obl7t#MDz;=*dpR5X|Ied>dAOIoR7U&8Lr4r=X$q-ii&PHZs?bG>3kyq$Pg-J;MxY&>; z_YFSvy?s6FJN3oST=Nm{%6HE@;pnF-He$}IX=1gWe%S<{Xy=P>n4y*HzQ4)_R;XXXZ{dkH;BdV052t>trRZo|NC8VLGqvl*2tiA`tm zrxm8T>2M$5%NjZAtZ*$3O=HrN6E>3tzs6FX#YH8#>YvV$-T8T8<#6_HkmA0dU`~em zm{)i36z;ej;{#jp3+kTqDQsd3YxCg>%A@LhmqQS&@aKos!ynL1F1s(DGeiDXw(i`B&a|4-g5#m_SQpoA>0ey>)(Sk>85BUs@lQpvpIxo$Oj1d#jMe= zav^@y6ESWN1r~qn3$HB%YsojpiWl^CA|rzyH>Qk5P9b^HsG$={{1D%~NJXL_dacZp z3rhjt{K?b(r|~9$%I0DE0m_GDr_e!FdBu5zrFg5P0Ps3or&xZhefI1u6z}r5pm(y) zmN>d*Hr{h54#o>k8)fm4;M2Oyp)|n`&hWt_bLuBI=M*N zUf$r4-U*RcpU*fSAL>j;Od#1;S1#$YmM{?sKy+84+L7fUd7R)i;J1B=0pG}qU^v*v zzWIr(v7#D2k(tpWso5Dbk1-H^=G0VMlLMD6BT1)khiTTgb&O#DTE({*scHJ9z?gBj z$cI#&rGtblzZM#fi=I?7@-4a@%XBONENJ<<>u%D0y&)FvcquJYx`6*ogO-iGoF<2Z z)jn6XFFCTi)ke{)w}w9&tP{gAtM-G%f8Ro^IP(u zhaaQ$Nud3eKfE3V9gaA7}1&OysyGC zGFLaGIFN=e=08b3Dh|o)MP#5s_zTB>my}qOT}WbwOmUQJPqVA>b!HJD?wZppX<%Su zNzf#p_^a+hny|6V3`-EAVq;Z}_(NOcYv?}P97kDgHL|*4?)*^wW1Ms=v(WHzY!6-R zCa+}ej=s5F6K@A&H+Em50c4N(&Yqr+?Q@fYdRCk#ggiDv@XwwuC$=8fU%ODezZRA4 znlIgo2)`;f@Z|JRsJ5G0N?vCvm9c$!b=a!SkZ+AZqA2*x>-Eij%o=SyQ$slXHy;%&){ZlW@}jc* z3i{Quh^^ImfEacOxC=}rR zcgxa07$j|BdyGR_p(MDTA(?}JuZ>TY)9O+st_m{mFKv&)z;UWwC^e}Sxg2Oj#vd{P zB~8|hc*k*3n#l!U`FPwkY(W#S7ySPc1ol$>Jvl`8zcpycDZ}4W5ENg6n?JV^lUAHk zc86hqcjZGSXo~^lD&Iob?EAn*BZz{pgp3Ud2)}@e&Z;i2?@^s*c9#-wChb~>l^;47 z_G<9ku90pZ$1yp~_AWa7Tnw}KF;2#^%JfP|RYoRd=cD=PWua1%|2{=HODcpFmFbx9 zZ=8uEcFxsxh!Ref9Z=O1ap0KS7|u(2e=5Hj?Fuf2QRm8mnrfb=am>b=TN90eYPyTx ztMJPB=V=V3i;ErQahJMN5|T8N7;c!P=_nn}s;|^lq;J79;EN{=a`2Crhg7^_Gh0~1 z7*T&vS&4L1cU;)j+(Kb_2`cI_pn;qBlVPN>xU!mL3?sKjk4up;x26vG%n@#`3L3#M z>rsBM^Tc$7+X20F-a^66@!@;3Yw zeK&I{Vq=`r68iWM{3ZF|dPa8~ey0ETmC&CmyZ0hADaI;af#T{vwGR)0x5CLEH2%2Z zTsCZNZkoJMPf+8QBsFWY(rEs*B#t$*?M{tEVqazAGNNvzDRt(3c!&7AkZO232TzqN z!gAJ`%8$ZtJ+AWuy7U|cYYo=DSn?g|Nl!%0ZL;feA<{>Vud{4FV#30?M3`jl)?ul5dy7<<@-?O7jm@H2 zRwyAkqWh|As!f|za#^eM-tg3ChTJJpwyV*?RAnw@sS%boD5Yl%w9oT!zFJF-@LfSX zW~l~``ol|{f1t?D&epQ~3Io3CdPw)v^;esx5s)KpjE5vwnXSmkLG?b~E_sSrybuUu4q&r%Ptx-rN_zzwklWUYG>h2<3`!7-E z&FjS#w!~J<(+e~7)50-wj>=kO4vGJY!=lZK@3Z8c zUA6nb_ zw?z}vHII#p8T$bDxYJ%8$IGI~aeZ%#17usMByDDm=#PeCB)oiY5&XIyT{4H}(N3tU zR`+pnQA9IEJM^UO3x$ggpwhzy`d(d%25Kl_|95H00=$oQh3JTVd8NJ}P54!V7EMB` zAOmSDnaT7i0hk_chsEKQ_ElUM8hXz2#mJQHJz%^|93BG$)9gnH*QXw~hkX)0hNPx2 z*0v=bs6i^#r3XB$JdK>?&q?9#t6b9XIhQsfd5ic!x>t4E;k$RF;i&Ev5eS_$)QYjv{?%?ZIQyuI6uJouv7#>TBv)b3v!v7K@iQ^rU8f&Ml4YJ0Q!{gB$G z&L)H#H|e5k41unkG{S=zPj)|TwlR=S^uoBsS;PZJK~u-O)$DHAl2JS!Q46kVC=gJH0B$l zWG+_R!P5-yV&u>JAj10MJ%xrtDl`jy<9F^A%=KAFby z{wVbvo?upSPXC%Ax9^bu0zbpsN7BY!rdM*A2UHC0dfH*#n(%+Af7_+gOUh}*{-)0P zxjJJatA3)6O>3p3lx!MFRa8>A1mqwwG_(X+JkD_R%m56ZZU*q$EFphss?;wJW-VTJ zd8f{g>Pw#J`;~Il{Ll63V96-S(ac|e|GXU%dBH4|9LTZh3q_p{G9UM>U2AcdjA}S! zt$8g-(8)<67h-_{V%j!O~^p50gAh8Y!Mv(?xW`IQnaV zd%c3^{5o^4NedV6?mLu5?nZjQTim0252jcW;m9%j*SEKeFS_A5JV`cwxa7Vn;mt3X zT0U2X6aWFZgCBF+f+Be7`_5%LCyqn+|g0l-h`?xC&gNu5#>38N*l#4bLtFqzv^L(o1dAQXAU=W~mmvu%teL zkklbCED1o^=}Tpu;LD*iAU=W}Fvafhc>jTqqE|eHSsdub^JA`3eU6M!Lq%mns=Fx~ z3G;yz50EdK6uAN;#WmOT>60B&V2NT#CLbP0_8&xJd`1^^Qh}IO13E{8Z=7uz#QlDR zm;_`ozI02fUT3gGx=W5lZD^7LH-_V*+OT*0YqY*a8YK2Tx35kxk5&=^kh$pgDv~w$ zU*O)nF3aLBglSyC!Fb{fHdi*93DTI8xiDrjZi}OnzSO?1u#W+j(_JJFLR>Bc=O<Yu#CEwiLk44s(w^T2U$T4t2CdNG42`zg^CKG-pWt3EX zcP)wFTH*v_{dnBfjna0*lV?L$bHhu!{1{+^d~&m)%W-0ncZw_cd<7ZKDvKNUE;VTO zB9#+rW?(-SC(keL;$+|P(CO?MKYHW{iY^`M@T@*o7Co* z*K@K$KP&+yz%G{2%>d76sE=G1!QAb>g91&Ldk@6Lt54kBcmW9ppZeTB*44>xtWvY}dy**)_)@2x%9pmp zW^cC0LjRMGh9f>}Ibgi&z9D+r4+;}`Z9V!)rFhhBmBhL~zj){A{mIj-rUCrz(KXVV zH7izRt(K(B+4yWR-^x;JJSxGE9l5H$&8Ap;f=o(v&(g|DjM>vA*n)$oEUr54i1%O% zJ2cHfBshdj)I)}!5;)@6_K3^1;T$Ys(^oRV!&s&rdLiI>Qzo)>y#B(|^?bbS_;~!m zEzIc3>4eA7dcF#`V7wvL^U~XT5|V6?qCzNs2!0Tfpxm)POT>OS#8m4ZN@ z^WFOHAanhXRZ3q&?ccJ9!&t-!2~0Y-V6ksh32t zB>h^AsGd*Gb(}&>sIB+mS!qF`$5EsB-EN|{t$u0x^-F#$f$_XD z-7uPjd8!p_#@^1-$o=ZPGU+gg{*8V?)3mVH5sFKx8&%PcKX6+^jIQ@lDKuYDQ2*;P z&)jsCkQWJvWHsU;MTfy-jTdJ_q7QRXHeS$g+qOQe@|mY8M@-pT;K8j6{tiy&@UcZA zlrU3zPDB)0)2h+Fw`_mOqB;mECS06SM{?OErbIJATt(82?=2&zIB)U$3zo8m!6a1D zs#u#_y0&56#eXs=A!A;3QTm@3t65!=yDDGhZa)cmJW5b#;@@K^aX&^3-^{3j;4fyt zz<@kU0Lw#hm=JTR69||8YO=J1i(28xklFS^BKiifuyk76hNdHP)SqB3tzjd3WKwds zks|Mt&Elj1UQfmI+MPPL%B|hDbw=9En!e_PIKZJ6ymu+d7fVw21s;t`H~bEBouO7C zU@nYZOXBU2==`QoQ^R^~t|7qL^=K62M4`mLU@~#$F)X?`;TeH>(m6VqLlgd=s1A!3N?|tqcU5tun^QJJP zVHwP7P5or~qBfe{t+1d*J=QMK-%3!w8*X~Z5}MoXtn)qr-$mJzyTeuLX**@-ZuYin zP#>fl4I}b+tCGr>JqBEnh_v3Lh%|J~fTPMgeO774dXC;tV;91Mlqh0$LnP9gwI^zy zlodHn+ucc>ocvsrw~g>!P*G8z z89MKCc`*dImC9?!$KCGW@BXlN%AQTu*fVY~v9SIW9mXw_3%4b?*Et$T7m<)KDOwE- zup{#_ZvLknsGldv@DG!!y0cm=cz4y6Ks`{2imMmD5|!B^%3p;iKqa3zIabRTyd=&% z`dfIFPGayJ4i;AWdpSe)n5#?{*;~nDX(kek;mx$`)kqs`>sfV+fAhkAfLq(uoWE7L zfS-dc2}`DkpnWXf61r8U{hCjB?Cl`2z02Lx0w5>!KwNZI9s!?_$hWgVr<6+)!{6ed zJIB|fz%Z!AaOHfYC}w)O$?lZs%I(_F_Db1N{bdsJOZb#Hg8-lDGAQXhF-P^2+!Fp;FB^<^r}Xw-Kgv&2;ZOmEmZy+xOlE+0f>sIV~y3LY7?0`WWI%jCj2d_3o^i) z?BKStvXT@$Sf`79%f1N??HlAcpHe_u>aIU2#wcqxgu7 zVreM(H|(h|!j40hl{JQ+ai_K@Sh9(m$b8T$^-<5Ror2k2>)jW^CmntX;+s-C$C0!Ro*{pf(^bl?SiY359x!1Z%93}WO5}BySvoq)Cmu2F zm>u=@UfLk*Sa+QDB7-kyx4PTQx1V@frl_Rc;Xuy^T00#=O(d2B^RKo<8Q$TO>9oO$ z*1GjBbW<{T+XMOjI#5ayAtw?`ErFYx<%HfKvM*P8jl4htS70;-`XDmiXB8kx7zb-7 zY2I;zmOor|l~q5;?KWH!U`p$SEgF*kAa8Fg@M^ zDKhvyau)XH?wyEwPWVZ{M{$tKs}`4UYtiecgM^OR>TK=>KtVXne5L z`=`TN1p-ZzX@324kG;<7Y0ZMf=jBU)O99LY7lBFIJc>~CJ3qdJdoCbl7^sK#t&Y!L z*FIuC`~Lr;>n+2o>e_Z;1(EJ<>F#b>G?LQN-CawN?gjy+Q@W9o?rxCoT%>?>?#Zq9 z^FHtXzWoP|gT)+c%rVAwomZYCuIC^Y^Okd`Tif19@?*#uvYG;us3|=5`T3jNdiO1@ ztPoP6PmtCc8kf>Mc@}F6FWGo{sss|-2$D1t7h%R)AC0Q|)$>^LU(|_os&Qm;ruiY~ zFD7cJY;f?Ze=Inf5LV)oyz%RXQ-kb%vV@7_tDDTfDBPQst_Wy(P#;nS3gceU5C=RDuU%+l z9^KG;H#8W$J%GQ*Edv?%;5_jy^PI-Gtec;n@k4d<@walclUIWAaxj$?T0LKqO-g=q zriEs|FW_nEaPnJC#-vp1axV_l6NMf}|RhHJi8J zxEDbM512%jcS=j{x+lN6qYCH8&tRH^#!oMzame!|5z-4!es5JT@K3cfM8gsLAKUWN zn5eoXiMf*Ud-Lm~0_CZV4I<>*ITZ_7d><~47HRI5ytWzil|jLv#k@!Prz@W)noOGL zw=9y7yc7>zi>GO8*}b@SIu@3%EM-L4lXYjP+q#X>38anP--8B4i^txpNUN0NeAKnk zUWUERJBuGB1~B+Ws?2B7StkFTrPZJeHiAk_yd0CrK&hzF*h~TDhlK^@)kze6pQ*PT zHJRs!Hnx`SL|#N2N8WQ&?38*X;&G4nbz>xh%9j=-KCq?i?TI z(u9n^YY(LM^`fi)nDqo3ZSlsX% zYFAiz*=Dx&TeFD#a_X(0JGtLn5xb8fhVH*?9z&YYw@IlGm8|F9-96Z*q5~~E$-4z) zZ;s#NEx)FBTERuNqfoR6iY}{BQ`Yt_g+~@zp)F&QvT-0^T5eujosERyxL@DpP%6~K z;7=#DRm*4GoL>y`WnyXHj4)D`4)-)$F|}wtQ)nOKObUEHF~v&@)}JlTxi?7DVdf?- zTANXZnpryN$JCP0*~HX8mx*Gl%B~XB=r;E485-%$eeSPR3#&=w#tuIBRbrS?RZ@Y` zWLhgatX69P!qNVNC>XEKPjZa2LeSO3Ej?#0fu@eEudLJcq4Y>KUd3;fK74>8hdPP= z)dX(4gZ@a&e4!hxeQ%GB3e_@#%YD9YL7sC!6L&}-!+L&F{y-nI`I+h+5e{Fsbp6d% zyrL`{c-eIpsP|m&=cdaX;J$Fn`hHAt*355Hl`vb#QUOP^)x!cp(j1R7}u*wW@jky=1=ps-P_(*D5}A`V+sTvyVQz zW$osvD99RIeQ<1;rx!=eC<>^lBpAt-$E5VTDuH_a{0;JSNG2uxG{)8FdJc`vU z#`;%f@foQ2J#m)i?CN{$|F90E!IbjoY6In&Woo?nU~N6^iqkWsvl?1Mf!Wj@HAha` z=o!TCm5e zkfiAoQ;)F@qE(YwZyKL-rRfk(^WAY1csKoNeYu|J-Gqu)H9^b9*WqFg8lZo73x2;I zWWu`;C6(#d*@=V{X2^Fo`^&vQ={9G*9xJcWPg7w zwGly$`fgwoyCHG9`?D*r(it!FS8J6t`lT`tL)r4)MG=~pl;ZNeWxmzS^Nmzhnh&9x zil<9ksZG{cFTWw+#>al}cQw*(c#^21!jQG!-2eQkG&=hlJJd&9=BY<+Yq=PkQvEU= z&k0DutO}BBTE*(cWe^$`m49%=W{wE!4DxxzB(Q76N6h#rdA~wgcEjSs zO|=FWzDp_M**+DoOOE@-N1i%7oE@E|a}r2?2{O)fCe|cy)~t|=d3*RQ@H?|a>Cg~5 z#ec>ynQETNHKto88~VxF{g}rv#XZA8T;bOAXEBWX46<|4vFYsfqcCdmQ_#rji)(K; zLzJ_VV|2Z_n8z3=hRDbfL%)Jm+0G^?_DY7qY7s zQ_MPZfFXc}M!$gMh7R6v!u9YEQ*dePO~ZvB>05C zWAnj&2=edm2D<9Z+(qHJ zCYVm&)yXB2Yg5XFUk*s**C3@L#-_ngAwv(wQeI9DdSK-}ft@)Q|41diKFvJ3WYx&6h(#6r+-ji%?Y)?}?w4f*#zd#oCZn=BG-Pjhk)bfU@2TS5-Gl%z00XE2hP+UM>%!%M(3~$IPm4%y{eB z{Ac?%eJ_OhXF}8cy34LMC3=FKKlLrnr6jX!zDnxZqjBRa4-RqwT_o`iE0lgMThkNY zco3-<#8@1~*oq4YNvT1PJ`^yteZv15@@=sG+yeBQw%iMkZd%5CXN=m#4CO^tn&FnmrPRNOKXhS@(f2S02j+F`5%gaVBS z48sCDnsORe>6DNQhRX0s)&#PTc{xUOGZc3= z`CU+l`Spp@_Uqfg<-QKPd zNE}1JXxY@Qu>4djZ*|t~mbOveotI0uZ z3WL$(zJ?KXr(44fcYp5tBwyr4)zCs&C2RyHj&X#tWH2cE7v}ZYXQcJ{}!_6cNy-4+t9Ni{c0z zwIp+CI5imz=QM(E`xZDR^5_kbw5* z>E1-}$qMQ71Tdw7Mju2TTJK-bWV-?71~J}YbGsp-IY zBbw5Ay?~b?B_)Ym05}_S65K`o(Ke3qhX}+xii*RT9Iyj2GU`l}l&|z29}D>%L0S=p z)FfVBUXwMTke)wmKati(8W1Ik%CYE+^GkBGL9B+G>$UMJIuqfWuIG&XpqsLKA_Krf~-Ppa2_AP)LK3kQ{TXc=@29(6@n$Ov+{RxJBPX9X%nHuz{@s?g{HdQ%}g0%a0isuYsg4! ziwTyOl@3x{!%OVhTKAo5v^ znNDK!+WUj;3-%A%+7mM~)IHH$V}I$II3ANCQscsjOGwgD$lMOK?0=0v@&^d<_ni#d z@<3IffGx^n*T1fLvqia%A!d_r+#fROeBXpw!lJ;j)e6}z*8EXLbkOD%rqdOOCOtH3 z82x!A`eM65xz3i5NVt;NtlEg#N|MDX^mpJ*`|6VrozueNqJ;1<l$Oq-$O{)(I$pp!BRfiLM)5z#tUrOtx@IE>Y+xQw6E;;e2ThxZjF3E%a0|z6w z!3QZc4)ObS_|q;&1kD#5QRX{IFSm_;6nkwjx4hO6{EknW62wvNM{k!#;RL5>sItE8 z3YDII^Z=5&*e~o-EP)F7Yy1z-i(>PAsN&Emg(J>Vfy4JS8_1pF`L>thx^eEDK*!L% z$7cR6?}6H4x?4WkL zW@tF07Aiz*J4n)bDRoYfxV4G+j>H6+wQF0#`3}5a*EWs55nhTkFPFxH$c0bI%)7*)o~ z5bh=NF7$UOdTl!sZ9ngcTUV4D* zP@*5q>x&6=WxWO&4m`gi`Alqwz~_fsHg}AgWE?&konWgSN}``Z+CKTu23#2vVcdJW*;o_Rzd1K;`9S(}7cd9W8i4@}dv;6UxO@RNU zgN?zy&w?LD1Y)q8=Kb@@@RolNit}g=0?|)ZzpjnHzR(#3EQL}&zS$)EQU{!@te&Kz zVjvON_cdi!2OJ0lTKn}2)!yEo56`cu=`QOf{J*ct{v`k%LO?(Opciw5P_~$Va3C^# z+W5HAU)W*Y?AIc8jWX0hf7cn5TKY$-8c_Z(&b3J2mcP?34?2$1e~h*U4}aUu;Hqa( z38WQ)Q##Qaee!`H0&0qC*g*+EFobq*oeg*e-%GwF^MUGH%YfP?NEu*DaUqpC66#snI^On)(kt4nVU6aVQ?Y z87R289^I9nd<+NQgMDD@(rq}xNDy(he#I1bh2*XAIh}p`jPO5?;E}tOB7Z;VKWU2c z$~ZxS`@Dl~QGJU-X2q#TR5p^tjf}W6+7tA9hm4a~Q>F(W;GNrhTmHcmpA(pTeU6yEvZ9xem}m&3fWd2i zu(v%Z50ipHzgFAyd%sJ5MdMEgEm<7>Z-|Ku7~TU(1ptl&tpt@PkTWjsPlhx4OU#$7 zbB;)SYu29|BOFfk?#p>Z`sL*%5Zzyo5xPu@G>&-vHfRGOuW#^xeCOD{4V^eEU@I6fRBB+-;YxdE*pjYKetnL}efwOw&iRh_(9=|p(S>cg@7 zAlAP?-s>2QB>C1z5?g?Yi3ufwD5puqTzM`@+dhWXIVPGZ+ZZ4L{XjB9|3MKo`L6~1g(|&+ZY%e@Py_jK-qmsD<&J=5GhdcT7?K2+ydv>B(#UW_L zb3Znt1x%c|rNmJt@W2?-+2=aSfrj$p$en2RdRe6QPLC1>A^RID`v)(M-G4uG{wwBHlB;(hihqMso7kle-0;Q z^3_CK0M?dKeB);_(gSB~)R+-lcH=NEKZ^2&d8{jCEhQxb(7)wy+LwjKJu{I?ACX4BosyyfNcD;l zn2;pjOeQ`!)tpZ6*w54r{1w|_@xCI^ZlYQT*fX)M=2xJ1Xp}~cR?Sf0i#RWHPP4BG zX^#y-`0%n>0F@r6{d?BiND$t(!7I==i%D*RTJn%DmOB^tv^98&S<5>JSy%(oc;sLH zN>OmK1+N;!A`BU|?-?+bQSf?F={FQjWVuapXqja2C;pnFhDl+=N$}};xVnM%I$m6M zF71F?&eTk5ouEjqfqyqLGKr)+@}KFN0a!O@G;?nw$I2B!Xz`d&+?e7>otT(N!Tm?~ z23K`cV*jzypC6h@W(5^|FN2PXVwxl%kZUs;kp~EqEXA~~OIUBQXUOvhx?M`HkB;}% z-~UAkAV~HWRB)(CTz;(;R+Dg7AF3Z;=K07$X2L%~(Jkk$f$PB^6lNLknyq+0lUDk_ zpQ9n8enHiy?b@a_jOXLk2WjBP4L=*3>{2C=N|?71gl7*Gb^ zyFjYnCH%-v09>&Q``d5ye+O%zSHR}y;>xFKOE>4B0|)a5i~hL^DL4l63>& zH1$)0N+&NiRR?%5Xj5t>^pMY?xoDmqDIpVzNu&w0d^Bj1S_fnDLVdOFg~* zo;kDXO|0yx#l?u~YWBmWADF0uCvV&4vh{)40R8qu;F*c!M|id=z{V2e7Z5O|YWe?$ z9;pf9$|Sn?xPSN`8eqJ%u5!axs+`AO#($mU60OdZM>g;oJV`@VR`dQp&{rO&5*Xdo zBtYVIuFR-a{Z0QcR#RC5B}n9Q&X_$zt?(!X-+5=a;|r7a$#K)Kjdb_N5cky;MhV4> z0ju+HeRe7FX!TbR4e30Nd)ZbSJ?;j4%{Fst5p8&lyEY{-y_0g_Fc_UVb*M;>NPCD@ z9w3XxkIgI(-eavHjDBeQj4AeS8sBy$llFVUqN97mu{Ab|u{GAKlW29WcSW+-cT%g?lFhq+ z$xo&{otdjZ)v?-0zEl_G7dU6=R(%<~JB<#a)cdBC#|h2wdgcldmW+B(oqk=5c5vy9 zQNbH2@j(#5nFVp)SR}mm5FEoI{k-q+mXS(QC0PlwXiOxC3OP>NDZfrXnncDjuh1m# z-=j;bKpg4Y9ZB!@;ll?YgAzC~7h)b`*Bu{1(kJj}zcx_ZC+`MfN+G_IUSR_l_NDFrjdv;(!7NwKeFqJ`T zp#;Ls6y!k?GQZ(tBt1z1(gQ=;Zj5I{{}Mu9=D?~>#=?Y~N~Fd=T>$eVWyi`mG~ITO z13@|QYw#plc<)wGHSai76z|s({G@iJ;GC^j|Jt09L+sQbb4ux#IR|3C^&+4LF)o~( zZnSVo8u5`Le#3&Qs{qtWb8UF~p{uUGS&cA`gwOInq^FR6*!YW!5;zg7^V9Vg4MKD3 zk!h1|a~fJPbF;4b;w%m^+Lf3XvIA3p?1i#uo7Y+rdjavi9^<8RJMiKBcd29oVR(3Wu*arz zgZapjY`+0eClZ^K6e{I&fko2n{sr!%=3E~w-A86BC0} zpbWLVB=-wSnzh)X0RJBdN{ZWz^mt>L93GCq7n}SgY;yvAW@QE5r6K!A9x_weQNF$* z>;CxSMA(dqt3|PJ^xz~**1=xyDJD&&!#cSNRAgXE-8=>x9iMC_>0eB9G^CCkKXtgm z>de=IBZ}v$=d{dEofuyUe$9e9?dFnIRZsXmeyqzM2KS=P@ef-ifM&|ctn_4*oLJ7E zclb>rSoHj_lQ|N({!9{3n6JBz|Hhbt4gWWZGc%2g?7MfT55Ig!ua@02G1y-OEd%PE zmW4$F^e?(QO6bT!fPl#cP)yYgB+t?D@CpM_-Wm$H9k$E!EC(JR+fgO_3#l4id4Z!v zDg~83Pu}-?iawoV-j^IyE-w%k8yqPBX;aeIuLwjzy;X=RiwT-^x0?JdpLzHfUGR1e zSq%fDC#3Z!@xyB!-YMy@1CAn<32jYjZZ5R0uC8o9E9;iAdcVgqE&gMUqjd1DY<`fi4&!e!>(|F-FEpUEL-5BCl(XR<2R;#spBx_T;qywI$L z@^Ec^h8k)|Q_h`sN}uz7&99Ne$TKa%e_RB>Do9(h2~6r)j1m*6r1W!L)5HKKS?p|s@5MI5Eh7I}I~q*M|FOF3*1=Ape9k*r z<>e@9YHG6mN(yPbemgs6l+@Jpz=HkJk3ceG>hmoulfl1$|0!ANUlln|M7}^^XJ=PU zmrNlhrLwf1-50Jl4z}gQJF61b;9&L3Mv@qS|Bti0!^pNCIBJpg$R{i;EEZfeGdVv$ z|8$hPp2I^&Y#Qr_VZyKn*#Nv4olvAA8>D`H{qW}6{Eqy8!^;R0#rFCX)zqIMFtAp2;AOxbkP>nBgtgaLB*H zv17CL9u$(&)N;FDqFHB?d>CSk5;RdgtLHhAJu%OiJw_X-tj2{SJ%HZX*X#6GBN+CQ ze8u2@h0K0(KBc$`a6sifOX&oU%g!?==HD}qv>D!z_Mb~sg8yG>`hcKlY%3(s(+&ny zqrTN&Vy@ffvb28{FKhfu9DXyGx_YB4pG?WkM;~%Bge2n4=$$hjQ#_`Bfy!)S6L^JI za}ENOV|wwQGf_!GKcSTkz2TC7z>&d6pkp7?bfMVZTq|jY2YZX{wF^s?YgXr z3PpIwv;~SgxHt&nji1Qa71I|-XUve|0b1e}fBF?ZJNBbLUM(0%gZT`93gb^5t z~KHcvvy+->nx~r|H^4<}L(FQnK)CfD+M%=T2N1=lXxa2_2 z`>Vb;AHY1yZd0|1j}^g_;fdoZJm;_Ij{>G zJ#|ckW%fC|po91ufmO)3rdQ5xxT4_Zf*P1R*SOHx&?-J@^A+si;Z|@rau^YY0HufY zeZ!CW87IJ;=JhzIC2)U)i8Hh-q7^Syc8r5TDQF7#x}rRI2rcH=sN z_jO=7_!Usn8vs~&pC^@Mt6il>)Xb{G4TojWF}G^9V!joFOdyti{B0y{su*NOL| zvTj7-T3E64vh@Z#v$q@R{qchFJ=2s?DVbE1mHi+P3)3+8uY0*-N_j3fXWP{qvPR}l z-ggfcg`jsdJ&RJRZqUu^dqrK-MJfP3h58!!T;F~i+yj`1(fby$`ouyC3u#lyL!<|k zS5dGj7~EUZsV(XHv%%70dWo8S-!xxI^kb!%Qsk+m1?3DQgL=_G!*Fr&jNMU7l4>Nf zDzzgk;Yukr%8aRVT$K$~uqqOTBZe5MD+yT=m(Z}5?YSzQ z13z+PNE{Ffh@peLztm zr=^^D1)vapQV?v;Z84o!Fm0u0wM^-b<{x7no@*x|hQ%xD$LA>2(xO*lTt!eLW08k{ zwopcybI!DHW@YJ|{C8g)cjO|nJ{(M~S6q>z@fioz%3hoFEU?LS8)JT*VQ5EG-j!Hy z)*kDM95T-;DMy6O6b!9f;NJVG9JP;S@(M^EnVoH5fI<+mCc=qO6;(JmdA7-R_qM4f zxv-tAlIWt*?T%3d;gC&fW#9R`6ykB`zCDtr0DYbeSIXyRW0>b0XEEo6V8$}eESXsK zQ3w6JU1BWVaeh1jrh^Fl+LQj>DEZ8*Scd5enzgF95bB*Ayx~SO z9FGs44!plu7qk~A=I80|j;&x&JAjxOH4QVFOJO?#Dk^DJ|IK04YS#B$bT#Y{AVm|d zTOk*ZA&13@8Dat7FDx0NU{%WTS;TZ&HPavTkK+k)QBW@D+#zP-p-loQ z0+A<^6(I2?8w3-{msblFk1Go|QNCtQXon9_PCX_*nx<~Mb74SWE zV@*|MHK^KcN^)}c3|!&*hMtxyw7Ybpbz_Yu^L~2M5v~ACCk==_9D6i;Y$UT$PNHl7 zATfvSkoEZ&M&udqa?27rKL?fhJ%Yl4Vf*sXcR*DfkqZj`pKw z$g~K(VL#|}pti7?jwG4N=-8FQDRO?iY!K%CnbO4-(iB`yx5Jem-Z8(qVPkDRc3wq7 z4o?-XCUam6n0frWuQ1;6!2v^5CnWjp*lYj01J+3AHQ|3EZyX5JON(IZrj0FK#sQi* zia@zCUhnFV_Xc1(-Ox17ts{)zG!Z?MI^TqC-u4cu^}l=R zHtK%{$EBtsPwW*HDJWn?gu@Fgv_ZCU&{TnS)n-@O+@mPGl~r+~5?~LRRkIs16d2RK zeqWx0iU&uAnHQj-dAo|tO7!MQ0i7K;tTIV;q<-lfbO^Vu@;PD&gDhH-!D$%(*f9f! zO`JS9r6{g&0)Yf5o!PH>)>_?xLDBfcm=tO1!q#lkYRlgMx z>)5++8X2FV)Po35TxJ=B3|#pV4~@f5fTBh$zURjtC>cyn*-rS#_I+K13Z2Yvg+20% zGSTzvof&@j-$!uOIAWunqC(xvilDSt5M+<=EA?b|*kcN!eJtOfP(EDO2<%tBg?ti$ zm6%w?Wz`I9a)xyJBmXhK#Z3^58^hpN<(PVHSVhUB5P4C2o9b6|_ z3pqhkR33B<9oMVLEKUGgxp1hz{D8e4YN&~}b=#PAW><31NhS^~1Da$r9o}^}KF_B+n7$yo^_sxH7U4Hsd}`&1<7F;3S8+cZ4`9o| z#(#h)HO>FOB7!G48>X`YsScH;SRt2QerLJ@m7B}9a}VnZsv4QyanRUvYSmhSwmsQ{ z(Cnj+s4HE^?uhG0*0=3=fb2V?Y`)!|r>b-lWOhj_4$aoTTmq~b^Y@6v1VfJ#QlvdV zDe!HN0s!c3{n;mdB%Y-{!=CSkfmGBxmIwBwC}B86#E^P-H0B@Y^noynq9!gxpnw2Y z*AJiJ?l6|_mc-NG(ECuoGPWVy?oB1A+BlTH0Ah&-(UnNnwm+j$yUFe@6sXe18HZ?T z*Qo7*rHDt@zt$TF z=*jPk;EFcX=4PQS&Y+JTA20i|T`hDMZTcR@Uc@AP&Ao5w^Skr06a9ABT~X0Y>a2(6 zm;m0cFETPasg_a-{q*BGhq#GhaWFQMPs33D$p$!S2-Gd(WYR$Bu^=z6$1u(~n!@zg z$>8q0(`$+nw0H4UB@XVrZbbalxJW<=&tRM%!x!4$3*ef=$-Yy0Q?0||CJDJn)zGX zk+a|RL$Q&#gQqHVE^C!+WlsC9}^wUxgoecb4>d zD>D$EgIYqKm$c4ZG-PBi741KvT$TRNg~yizNN<;VbK5)+P*()r1qw1A8Yd()M`mQe z(OI-KHv`G)7$60GbFjHd(Iqq2NcT`#__;7A-3`WLVZlLi36t2>6=G?dXn1enPV7QX z>Inne<+HE)YB*^k)2Fu0rDdc`7hY|+mf%CxudjD#t`b-q_F)=d5QxfH1u(`E^TQ@e zMJDp$T~`>JFH9I@^_e7ghZS6y_Y=lmcv$WO2$PA};er~bkC1D6Zj*nz|*aQ$XZn=dc6RH((K@plDxF zc=F*XtB^@haA}?F?kEzwPdV0+>X@h264PW22KQ_SjnSnS_iX9VYX?7Vp@q;7v^Ly& zD*0+Gt%wo35ZmnH>l*7d0{qqM-~t2=j1jL6bRqEIyyRnKp=Qq%X~C~0_6^x3&svx;FU4S z+jp-`dx81}&qkDaQu^;ZpOTPuKh=eD$(9lqurJRop!j5>7(U-#uwTEj;%8Y+hc4;j z!joIl)K^2^#mbC`n9lZX&xDE|!6$04KC?Z0gs%{M;7GJ=T5Q8U44%}|j|o0=3LHrd z@wJ>vP{Q4M2+2-mhnfGy@cYAx4_4!x=Frfna=yNU-@Wbqxk^o4T~1jUa(a6D`@ z^rhS0Pcn+^DZmwwi2e{KuuJct}mU_kSSYBH6wv*s3d0D6!6uF*qD z_%7sgZGXuer$o%TK(?EzUFeaR(%Of=BJr3AyQ&!g$LM|dQ62P>iOMfNmT`#=ax4I zxR?|^cU7v6uAIXdxia>Gos4V~^@d>+-^_7#aEau&#A`D7C{oMg;xs}6QMe3MdQjGj zjdqoOlHG4+KDCQHn)W=FRq+fT%11p&89)7;qmF9HtLl7tfR%WnTtz|HsE^bi`Z>g< z-&-P6DtIy)^S4QiAAP32p5HBTPq(6n zjJ9tx``a=y_#5^-m{v)VfF2(};k+oO1UGSY%&CVR>rw1mEuejxa7U{?d2-lM#RUZr zI5^o?VJu=$_0NQbME6in*L{hC0y;$cJX41oCVnn4`UpWoZ&#=!wI>EHRVez|?W-Er z(|XBEX0ZJlq5gLx{hxCe1b5-RY^<5sd+{9Yn%Lli_J%F z?m>)7{)0an$HJkboS@=4|2N_I$Ut5yxhzF#vAy*wPryupC(A#;ys5*~zM9Tn^O17s zyNGM}%@C(4IHm!YiCHMP0#8cHLgr?{3S@$T@FGy8Z0!39df_96o#<}Y$TBq9gea?e zYj`Vk#hXu7p@quRgM(dxpntBuWbgfTS1`;=#!w_H65(v>l2tleVt5bXk;cSs9X5w` zALP5Oe;2Fu*}oYzy7O8TWwP!b>omsa&DTUhZwep&&3&n!kJwqS{e;`{G*4k9(g(=%s4RxCfa-bzizb3^_&YACUsDzUc z3FGuK_Xbv=C`-QE~~1k|F*BLCp9ny{ul`Y)FsId=!c_TrMr9CNV+_I+Pj!6;e}^BNp+YQxX2Hll#}NpF5Z z9oYTMr4+#&c*2-{=iy>A;08^9m70J4eKJ<<%?w!#%KR!vRt#omP%~d&cwDpP_g-kL zoWjZKYc^FB9F1Tx3F(aNX89?S0ATe%nFyhwq21?uQ*qUt=(FWX0SygBq2b};hY75V zx@bZ8RiaK{3lSfm8(lhJ_X&aBZx7S0pfE}yNtaj6W$&cf0O7Te_sm~n!IaRTXmFFi z(L4HK2|b?{sdLKzP1~9$KTPaPnWeB0_~Dbl$k=d70rH3X_wby#&n02^WT--B8*GI} zRz;up4_V(u4sj>wZ_F7cOjgV{c1-Qrm*8{<6-(9{QYVk!<WSw8JRF zPzUh36?Vq!+Q|CbkRQp;McLdr(-_5OlNHyascX1vjecbqTVy&B8e#4*SZn6OB_H@j zsooR<4&baN8XYLD+noHzoT_LxIIpN!6TjIe7aX}g=t^YBdBBbdpUe`NZrv};kNYIP zBw?*|zs62A-Cbkv722GDVZxmLdU?_YOZU`Tb=ltIH1^Eas=N*+`RjgmlS{hh(~{p= z7`=n4Vl;mRGYw=rWN6K*KA1NnA*5UADK?hR#bzi`n4gQ9p;-c*c*v>yJg*EfXv2K{F`2ZG}}PwdOBGSVX{{&pnSi0P~f?H>ZPDw6#S9 z()xgt>Du+-BC9msrO?k;EtT6|sDN4txypf!Yi5*h{a7teaK=rK3h(uL#uPhySO=vy+6VP?O11 zNp(7!%v_6z{m~7pH#hv@DBn+z$Q=^_l@uqD6S{atpDf_Bd8C>^U04u(xXhe|L=1u5 zfDQsfqWRaZ1;^-(qzj%hXuF!<9pkBf8+U2s&ZX=k3%GV8$JZmyBMrayUJqTWLZ2Zn zIOv6nblwA5(a4f6P@jyd-AB>TG-_}Y@mtRaI@QDC_EWJkNP0%tcKs5|;Y*#f+F!ayJPdb6gmPo12a*V85Wi$;Jx0yqNKo0y(N^|1+M?bi1~tTY7|gC9NV^Xx?>() zUUHO6ad%F;s#D25BFD(6rbk>h&fj=!LEry8^_{&qsXl{P5@JgcLWK}0hR6$HOogRj zdaL1{5k4lj!)zh0nS5P8=WSm6U`Kqw2>TpBHP*3OAQPJAMCUXpBJ9f{tYNg${83t3 zI+pm2lqA9Dp&4nzZ}AVNj4jCTM57PCQf%~tVKjd}&B!q_jDpImfy=-7R$OdV57?17 z<|o35T~h|3js!X6O4GSD1k!g09f7~o8Dw)HKmt7Mb%orIM1;}|^m(ea`j?e|LelM;2y0l_|@d3XOODQuouU;^8$j(!9PORZNm{jgf zobg-lTi+y^kr7bUd28&h<8bBLI0d<^=WfDg<76Y*= zbwjsWm7eEM_+B8UE1&Fyz@83!VqZ@+ZG%e55o~ zDFYaWk&H=!&=sQxP<|O$IgSrw$KSq;3EV2r>2|Tc(ev9e15MaOh6Bo>=d-wu_o>o5 z?Uc}wOj=Ut=d9`|aVh*84NS3ex96afEN}?@e{VpY?6j@+rUnTZoS%!jGAJ!lmlE^3 zHNS2F!CjmuBQ2LfN**;oC|)pAHuzY{1{&&wot|9sdJ=9<3=hpOvT9$6$HO9A-E3Tp zK?g73`aP&??!=^+p_($8bs8~Q^YUqDVAu2+*aG*i_sTp^7IM!MJNvZd_; zHR0}9%0ws}e1HB(R+ML&dSotBXU6B_V?McQ0rQCy`y#9rSt?n2k##dKrbTIjIBI6R zh#E11*lOLe5R&fi^NBkp2A>0^iivPC*$f50EDc_>RM?$N-G-3(v)Dw%C zk;IsI!z8v4;B=gvocLw}SH+1-P+H7@UQy9f=eK}z*$(a_(Bjgf?XGwmejJP|>qoC- zD4e@-XV-5gd=94??v4NA~uyn@|Y#}edowJ62twr)nxuD z?c=_No0pO4f0Rz<+RfF0?CeJ026cFv>Poi_!&<%*vR&Z%dS`I(l7!PV#{g_7bfQn; z#H<4KiV78B{kv}R4^G(Lrx!&c_3>h*@X#8V@c)SaCZ{HZ*H4&u{QVC^rVzK(QYW6q zvFWV+2qBAmUepz@pE+-i#oWyqJVq08F3w+0b6?Y2E20I4YEejxuKhatwxvo|;nE_b ziT`}3@~fhHII?#3w0p;lz`$2cy}ZHn>H~MZNCBvszQpPch;F8@{Upvc%%C~JK zoOPT-u6RM;)eV_Df`8E%o3{1SiMoZN6t!uXseP%CSb3Ds#p&9Olm;Tn@?k>R_2=49#Xk2nT|K9h!-1BhXny){)d(O31%^IV|sIp`YFK+0@k{3)WcD_e! zd)>6q@q#^Ul96DFPQ+{AB2w+;Nl(ur&FNgT)RB~*TexQ(Ar&X1vMCEoh(1qB)C~=u zF^)@3o1mK<$!>C_X2n1uP0o$}-(!s6hzrOq1ash*K?4RJ(cXkXKrB7-9i4Y%bdBYD zgSzf{w%_>Uf?94b#z9hlv~BR|oaL#rrdb<}{c3@)i|qXP?lbPu?=>i&=tPnUkJqFP| z@9F5#?*_6WbYW1x*ALU0fGc?Fg)LWk6!AT4w7B_XYqUgquHwZcg>f9J;~7ok9U3$> z#iEMRfE4nUS(PJ^m0CB2G^`*gt*RJ+x{Ow}<%6!4E;2)OGI`dR3M?D`x(_5PUT+>Z zE%0Sq75Z5E>G!y%^!DzO;&&VNt7WmQOw8^^-zrTifzGE`kK!!AQg01X2fl`RQWe!p*UbLEG_E`ER0{|Dg(;7jTAFG? z{hUZ$Rzu~&_TMj7>81OxxJu_T!SX(RI?l>10{b@+_1-bck4Qez&l;{pKZ)SxDPeOS z=v8@|Tm)U4EyOZ1VYj^58>T_t>+P)KstM~(%$jrs_3{cb(pzoYpC?OA6mqGJ`P;}q zU}EGMNLonXU{^DGW3b6wXX=p$%g%TYTq@EWA!0h|@@TB&fFt0Ktz6fNUb+Dz*s#K< zvD8K|$>HH1s{W)omQB6W&`p1QBKbDBQf+99HuXeh^zk8vFx|5D@#&OvYEe-HoY(dp zES>RQ{wJWK`pV#%`{g$$*$IQ_NN(82F%EXzAry|$*k%lCz@VsAQgg=CP9$z?@PR2} z8jX6d;2}U}MnXAjZJ3o3W^5=n=-2}OHPqB>k{IMHiH7m`C&tjelyeUb2}UKxHeVAJ zgPB&zq(p#Y%_?I-z}`KnxQt@#3yy1TWr45pb{1alFO4kOr>N!kLRar+9NuUa3UBJd zAH$$la@z`MJU%ih=jp&ENFWMP)>2o%BYwR_QEt40z)c_wy8VEx4S0u8ZDDk2oHYyt z{DJAYo7@6rdoco^&HhOXeHYv`(mU`bW|Xm%>`kAV<>j`NlwK3ww_jqhuvqF<^|&EQ zXJXaMF%^2^fnN8CuPfpdmeF8-@6M^jssaM!12c7suz3ouuI`3!7IB_?Ga(%pHp(3r z;WW0MRfq*@Eg&@wfxrJY8PkeFguHVw1N#GeN|O6kN0sIF+e%7)b~hpPw9)xT%NseWBPk$KhF zSlO&Ttl6BFAi11A3H@nGV!BNgQ3qHKLZCYRbZL?h1i}46Vs}*9tSOcIx!TCZLu3K# zaGQLA{Z_d=fwhxU8zYy7j;m+KPin5rikk+l^9L7Y*KcTiRn2A{d*4`-ADqfrfxi8L1xK!>dXx<6l*bct7dc-<_7eBT8UN z9eUHY3&k(|kSERq1`u*fw2dw`lsN7PsR`<^&b!XQJwJPjRa}Uv$`S}M*Nn<*N*N!S z6?F(vg75UW<3DO}4R0-~_I-;f0i@|V(_M2 zs$K18E99;L;t(m7CJ?b2@+gHKX0P$E^EQW@K6*jFN^?a5m^XKUy7-V&Lhj>qG%> z$f6L!ddg!@?Zw`zb!h$E=FeJ%&!3n4EwRf(86V<2%-ZRnT)Tl+X1wZNs0g4Yq!ZzQ zHU`S^$ZB<{NX?bsQ4#ja_;<^6`bDXbsZ}%kmfFs&@bV`5>?vq4^nj8fyp7dAoMizD zm@2o-nA^rwuJ-771-NTBy~wVpUr-yc1sU&$Zlvh073Gsc!< zX9RNCkq`yl6f;nKXkV4EBk;AJt-r2)C&~Mv?EUa_u=s2r)AXaYR2XUa!h@E)en!lC zlzjEst@UahPEBJoC@m&!4Go0MLP65sk+HGwF;P)yqSS<7qD!7WKEqQ}viNX$1A*T_ z6RN(>{o$QC;$`_%p@nFe5ZSvbuc__tMGaFFSOi%$SFOeO+S3Rfvm&3aWRq?k;-pTT zTyDHU(7q2d=e55GG)yki(?U+;nUS)Nh#--7#kLUliqh$5%H)b??v~*fOYFfZ@ypRo zEXwv9gjqTslbxNNr_+hDt`i{l^EGE4j7Ou5fIgj}MT?mG3A~~21Ma%(Sd`KfTueXf zSq}3T80wP43s1(s`k`w1UfGqhwsw_V*1j;P@4#mBu{)(H3nwQhXa?uV|E-Sr!bnm5 z<@N22oP$H{^Y!_8!m%2&t21jGDzQTqprB~lLNqazA3#j)aXGLLL;NkU5=f6G*B{C! z2pF8B)8flMO+$)dxF(kp1tk8n#ImF%c>*xwjZAPb*5>dFCc|oG!bJ z!Do}Wg{I`~LIcsPJ`2^a{@yFDa&bV=tocfzt(YBQn}G(EIbg#Nkb%8=Y6@^)#ylt= zNj~}HiZb8(4I35pi-CcGr?>ay+YqqWWPl3dym#K(ac$=9tT#fJVuFjz)v}h_%V!@<(6XLZxz?sOYxIp z1uv5M=Ab*FEttWEua4@lcyu>PL2WNAz@+G>ORBUKfp$ET;_Klh zJp6*@?@jKVx-V?j331Kzpw9^eeU9TI*meFuDr#X<4GIo(e!_j|A5z(Dv_SfFiv7Eo z2%Yr>aof+4F)2_j34}n2mqUYUqC)?Nh2djxl^pbP0`t7zaX6S6B4Y7=E$qUow8L0N zCgj%-(I*Y{ew7Qq)&QypFw|9e8NhjX>y~ zDVrdnTa#(zQ=ALf)manQ zm30S5@x~9W*%yvDtQ88{gu;f<#f;=P@ln$M|KRj{Vx6qiXQWPhoUI-@?=7f36ZUwq zzeEpi6@>A4VggVNk)h$cC?$Q~uY`_tWFy-A|9BZ7gHu}@EB=^C0bWk(W<|)XX2fOP z_rOQskDRvgHrvwvU1<@Z-CPtySc0QC%cB6288*ZI%LXbSbm%;aj^^!C z)UPF@S+2Eh{hXDfP zjf`kKbWSu;*QBjX6xtM~nV?&PZ|7nbXEP}&@hPBNoWZc@0J0K6L2I8ZbVh&GLC6S@ zDL6_15Q+d0Hexc4DFNNui>s*7JD%}uA3En+?ocPlQ=)Y-N!j%Urdx>9L}tbiClpsm z_1w0UntuXqH|?zSu<)e3c&tg$Qd$>=R@NS*rI|zr&6^}Azng+s#hHmsxqm7o3_6k& z3~2qk*if{XAavVj5jKtF2~z2$T(0l*3X>zyzo%TzbPlg}IJorl_EOKFalVJM!JBn#X?VjvJ4e+yGod%{<=&lN!uUbep~6Vl=jlNPH8n_CKMv zXW>!eJL>*FpR?sQO)aig0m}c{&9&&#Rhxef5el%I>b>F8$);5mou(|L(yllBhwF@| zbj4ViB1`1UB^P5S*EVqFy-##-557Pjo*aWKmW!U$aG_urty~n|ldBF@;;qkxLPa)y zL35`dISVr8VJd&gRqgo?8vs9WEixk^D|t=yUz^GF)1(!bsV zW=-r?cnQ&e*4Ckid9aj^&CI}r>B5_~)W-1he}n`#s1L4EO%8p?X#HhZ713&8h9md^ zf^|yKOFn^dGsdqd-$X8SaC5|Ujhy>T2k5sy_aJCG^h+d zUi}JLsH)+bSt|ko#dBWg1nY;s;LOPB!(!+H)@U7;#j#*vXrhnr%-yuWtNHwVL{jvk zg|@37a=ol#`!S5fM#45-YKp17qNE>Md^46Kz~{mqHc-XE36pPOynZ!V>~|*(;%wT1DXzKNKW5#p zI5CgS1Fbj(`+H1QH|pmepu=!bLd$V)h!h)yH}){6|6|cFB7h0X*|o|)+SKHmVQjQ6 zO2|W}x@vVxk#!{f0{4@$dXVve9E`tKVh<(3D`Blm3#tV|?=5OPgF-~HAmws7=jll)Y0ZS?-D%>7 zPi2O#?8mZCQjg3?3NcYDZ6n;yDShIDK@+G0xJ4)yqI}J1Vfly<-Q@2`c`YyRN|+tT zLfBv41U_uj2e~c(bB2q7Rtn8m*yf9)kam-!V<35j|?^@OO=0p^D~niUGE|hhzDxM(}D#n z+pdsd&9yE;M4$8!0p`$cBnuXu7^8;o#;Bt8SK0x+&f4e;8|{e?e0SEvE%m z^#t@h(oYL>pyTL+EZDjoj*-0k0Mb3y%BXA*0m{*!Gb{hIKbDRQh!^(p;SgS1V_cRH zA1x==9apVRleiz=HwDVeEz^@mEu{in*00R z3XG`{RlVmt&Mj^CXWsNJ*(1RX_d4+*E4}v`nT`x?Y7pbvAY=x%_kbTTr_;KCSpWHL z^5t^7!oSOWyWadHh#&Y$wbA*vrm5o~{WSYBh%ToDdD1)Vlws2)+>zQMnRM}ghfOi8 zE=1bv`WfXWgAdE;o=>(HcJ{%IburFEQ4VwS;5$Br=0tE?E4B@>TV|uH4);+f5fdU3 zX-|ZNyjrEb3t)CNvZVMOIDGgGefLKL(+f3Ds!3|FI-^Q$JA3+wNcv2+68SaEIxn9^ zc4=O_!yE!_pj$j zo1e)pTg{sQ(Q#u`=R!73n8!4;3JF~)nS54YE`4pA% z;~)*Lz`ylUxT0fVB<1E_HqI0XlNc2jkAr07C0OQY=Fg{Y;^SQ<4edyb@^O|8AE<(Q zGpAQq!+=R^!kZ4nCb$!Nddp5?0s;afOpOPiP;EfBc4!pvvkm^jA^_RMueYW8VtXXJ zp9pQQCMlZRP{0p=OtnI(XE`;R6Onm~VZR^Q#I3cFuvTS=NU-fOAt3evj^9wg7gsU_ zOns#%jI)96!~GMyR=^c@Ol%`DLbT`ggny*%6Zv2;WFeYe9sq4*)g8aTE5sk$yl65; zVv{kI#$58a%G1q;cp(1IQpz4$o!3B4(*o1$_BSNM;ET@elF3N~5zX3O$dd<53FY}; zN27E3KX-R3%kwPEef=bv{>K8CL=MS|;Sf1Vgu=UE3kFU^j!Lk3AvsA8X5&7}1jv}= z7j&&~KM2u}=~D24-2Jcz5a~Ictp)2*rXPV)>izHdg(_%#9(q+hcnK0igu6gM-Ng4! z@o<*u(7|jLN6sY2dO{FdFo6c41tk?P{DfYQ!kv5?p}-H$^6z=@Zkqwl8$}|w6P9}6 zS&?d2-WYqTn?*Kj(GVmAjTd!Wq6uc-4ans*F3M{SvJ9 zm;e56nRC^%gw7SZfxzj?Bhh!~=)=!d zgJel0_S)s-msN;aALf!C*Fmp0W2chDK3q&m@&qSjLDcYu)*}Muw&)pySo~!H`Q?Z# zjqe$x^R!MCl2uoEc{|V_a~m@v^-cc6rBpDW#7day1uymqi9=Pk^GVxuL_e6YEFk{Y z5blmi6I*9!K_~(G$4M41zolY7XtBGxydZR-OAJDk0}hkm;!Km`hCts?<{pebRUBGb zXc|F%@TcSM#)E0eR4>l2&U-|#ky$8zs}Ry~Q&7bGUmJxaC*_dOQS8Hvrl2|U&qYS_ z4`6%uxieETD;bEy;`<_jpJ*$f^jp(ja&3s_v=}oc9{cf6Az))H?x2CRH(;a{6I1si z5>Dq&=d1p_1(DjASZwaXt8*o?hf*{ZGed266k@@#(t#rmFHL?!>XVotbP-7f(i;RsNs11dj&gR8f+~40lE~h2V-HM$A013Co zEEo7!hBBKDq=gpx(|7**(L~Jali4gHljef~_)LGi_+oC)rTeYVW;1WE-#TKO;hH1< zL%(EV@2mzSVU>mkF9byRf$8x~@12L(BV(!i_ve3$Po@*V`ftccmkC_TK|}fw(S%17 z*AIukq|6#+9kB6qlL-)~+12;3pWv@EI8eS3eD)DPX8@KA;>6`+6<8%U8K#16-M&5m z;5IK;*PZSdLGB~if8RRG(YC(}P_?wa8Uds*u6jHpcy|6pRjs%hg@JiAtba0aT0Gjp zq3(o9Bik-AsZgS=ty=4os-W0dy4KVQ2*7n|h~rc2ubPlRV>@WQg$nmAr9UgR*%Hil z{28P!XJu|t5#xB@9sr$+MM)my26AppuT>a)Ni`f!NN!Rg3NWcc<)n^Vh+a7~{*e8? z)^E#r_vpYzze0`25C}4wLH~PnT`Pr|GV!9Uiuyc14vtn;Q-UN;NJhpDTF4k54Wtd+ zF~A|LEcHraJ^Xz`TP)FpL26AYYDy`A(pb~ciAg<&3K<=EXBy&#L`}-&vm0UZ&^d3z z|JP|d384@Pq8%x4=r<}S#jMW%HhWNgQBHp$`HTF-2pg%(21`Sitx0SJzgC}^wsG#A zs!gzLUT61-^ON!F=Q`rL&X)5^=`ce=tBx!LJvf%YWC=P}r#ULB;`Lkh-}|2NY5w79 z78VF+fr8hUzMGp)ee-Rv?UOg5ae6Ez+6nj_xgm7>*&bNQlMaor78iA%40j@{CI4!% zI@FO<=vQMDb;VS`3d@q=@<%1|sMsTS9EWs3{8HLq%D$6*v>S$y*!7!r6iwNH{AZb7 zx*xLpok<@eBiNSvWew!x*CPUvjB=u5)U1(nhr_9+29L!`=Fqek0LsJ6!vl$ZZ;T(M zs`|(N6hf?X-~5Q;!kR-XROxs{B`J-CiME$_nBY|V6`?~|3k^lb5tm}n5&NKalB zpC4R7+0^B5-GTYg_I7@PbR%U!%epjv_A<@c;qll*+kAd@y=ds%v6jD({Gk9@=+8vO zoA^oJpEwa-`+Ca6F`Q1c6B;|bzv7aR6fb`0g7uX2pkf`x8*=PtvzC=K0Bo$qR+m~n z)+!RVQwyrhSokDgmCvvLVq|L81XpVW)vbms#rUF9rbXs{04OfgU=7KmG$kJCId4-p zj2lViz|^5`GinMm`(YZ6pvh_iYYLOHyAwx_eDqV{RemTULvB;P=u+P{z`dOIm~ivS zRm?Jd7oAqZE9R22>_yQe#*eENWy~u5XqPd{Ko1!_Jzyy^f##;OaVq+QU$6fAAjwL2 zuX9b_vel2oh?Kk&hyHuft$0Zcu5JShn`<00eGK@5bcjS58F#DYV8CJ$xquF;Z0vSe z0z^+I5djas3;*#j{xo8^(I*+Oa9%%|H8%nWdZ%F^It~Dfvajd){nCo`R?>xE>A3u> zaff8)1UXJ}fE4+DjT%jiN)izlOjAGDqXjD9-VO2?tv8f<9F z)v!2X&=5$TInN3~;$g9geu;D=f{crI3f>^n*4SphpkEjL?FOZNBEm4&Q0Q6#*V>j9 z+>A@5c=#nB*NpD5)KMQC3qLtO1NfnLag`MDHMn$>IezTxkhNHGX~g(f=V{WX1Hq~x zILCY6P%nY-M?6I6lAS{FPi>G{Y%kHJn||CH}PNr2K*=(4$e|GKNQqQ{%L}= zxFw)%s8sTLz_Gw?w?~rTMPf@W?0v_l-8(0_U3uXdaJl;AOS4PRf^ z;o;{$fJftL@rby4?<=@n-#X~@`wr?tvCXRydvZPBW0XJU(FB#z##}5HE#mly|En10 z*y((U)~Mk=G&I!k;5gyW(qsfw0+(jJ?qDPudE;kXfbmvy(a1epQ$b>W9|R+#{wAsz zKR^FJ%E zS`=ot<|<_Oj4F|DI`|_=0h63nphlE}%+tTe8t7qezF(f;3aE zih)7GNuNM`b(f@@pX|Z7IO6b-Rf*^Lmk7AL>mZH5rPCL-BJwpA=xRk22$|E$bv2#` zAnj`B`d)g5!ydf}Q*dJ$aP7l&)BEcM9@yshU2_4q z@UQ|MlY~+7DpjWis@)2X=#`*QG~$!WJ7>PC%4&G6S-!)u2=3^}uCmUK{n5n&C}PPz z@>?C6nb{^ZV#C>7R#b!GKG$wud1;beYyXAsDnzl6{~j2LHx?B!}LB98b9&v;mqhO9MSjfbb-_Y2GDG` zgWc1Mi5I>t!|RKGd;iqt7wu^~u>)q|ktZ?p8((ef2UJ#woS#GD<0+7;XmP2O?5WAy z)O0DO4fe0p!bVy%yCC!R{Oa7cv8yJY-^(U#`gG3m@A*P2o{lb^y?FjhR;Oa3m)X`0 zs{K~a=K87np-|)!19qj**|p~19p0_VJm>z;;Y7@?YU4r?wY#PMMPhBBem|?veoRl? zi^2{^47|1zeYo|K$&zvPb4$;dstc}MLS@ZXd;|r?44YcGqnQcD-?L}OxCg#r@Euu9 zCOtI>c*;MBPKc3GB)Z(=u2*EDH#FR+PRs1%JjE%g>xSJMgxhUYE9tBU4~EpS`gK7J4PWhEGcB!!##e(MU;GfVPD^8?p|t`vjAyo& zN@yFr@wZT{m(k|mV{GmFg25N-KLh#+i{)O4ITgp@OL~!U{N8=%MK*JSFd1L=%J7qQ zQfDdO&Ya2ZwNOSO8~7+lK)uBIWfI8a=*DmgU&dmI-~(=t+LPaxfM7en2)IKbphJw9 zQlJ(0Jt%~m^_v_5m&Y_ruO>ysBp|9!G)iXTNZP2d_<(&Zmh)e)x4+%s?DqyO2NgeH zx>~Mx-)I!~^k69~E9Anp)5a^P`RV+Czkfg?xtihk`iY;x#A)>GNY9{eE49v`vB^oR zpRq4gM%^$6x;_fbl!AZx*x;wa$Q_x%dL3F&BwsqJzfg_K@LbLC z(bmSTTf(+GQu3_IrlrQUVm?U##?UpLcJ_NM5`WlRNX@TDj{KbYz&QnM_#y#Ue+5A_ zUK=sRWFBD`$pb|vCRAei) z-n8lh?%|hd8^K4Zh(%Q1bG{V}j+O`a)%%*q9sy0Am`Xlnl zr~8oD?0(^JAIyZ@72a2klUR+e{;#Lv;nS!gmM;$|a%^OzFQD7K%UPqR9oI$>dHmgHmohJr(uAGz(b&3)QPWgn*C^Ff-q;|bw$K{3)!y@jqSi#8?*&1coj{Z3jw+T+^Q3lo-N9;f*>ko;6Fkg;E19YfQc>4g5!p#b~ul-q90sc8ruFBTp6sv z_$1yea>%|iI@+__6YZC5XpuK4%i2&brD?ee1bfpM?_)RIA!t;Ccnld?;3$_R>6r}dMYJ**+s`!`zLFDPU$gZT zdbN$F;Grlg;^{9vurtIbBoGl2)+%XP#+;w))N4v%@~fRj3i780(YgQ2Ctp)@n9S@= z%J$oq`B2=E-+w|;9+3==AK|2~=a*3-3HKx2*(Nlw0`OF<>S_P{y2^*tg= z3B6_r2f%!FE0p@9)iFiBw7k3?tkh1Yr6)i7P^?Sl7!6QLkEy9UxK$kv5fz;@8rkVZ zTXp3&y}o!RPLI`s+-Zh;UJ*=0@)y~<7*Y)_9o0|t^1<+tp^lY$Swo-D7a}9Pr4il; zt!?)|G>D~f-ry@0{B-cmZBSGrl6c=|97rEcgz60k?(0Q*osy0~!iZtNccFqZWv$=~ zlll^0UEb<3%_4VWMhmx;bNjB}iuF=gu{vry+HuuB)$=SxV02Rc$R$EaViEVz_pU%V zqUeq*xajEY&N^KtZHjO}b1H?ALf2*8} zp5O=J^gGlJo&BKV1AxXlH$HVAH+5Y>qSvo5G0~YH{ZSCAb{$52)cdn6^pDhYbwrSG z0FwF|eQ<(Qryw$@BRSIVRvV=}_|fo;X`gp$6w=O!+F&xE;LLn0j0gIy5D{EAF$D1j zl=|NNJ1-$&6em2CTJ%Xr3nKI|o32ZLxO0Q^63lW5Z3kRkQC;tlj;Kx{JjKvh3@m|- zoe_C7fwe0xGbf#6WmK>frWm)_p;C4AbH$y#JrVZ?P_TvjM5M3k<)NHy`d34*Vw7ML zTj-oN*qPpBE*lW=`#m@J*`~N$auklx2{y;|8Q(4gd5P5a-$UEp%{~UgH4|T@>@N=X zg0sV^-l;$QpHXIp8p_`0RXzg_>3qRSKOb>_1ZekC zp_0re46#}^vh3HoUFc6j9pF#f{e*k>8b+UD2_{3pjWA3Vx!NkY$xXFV0l}afKvoAn z?MdpDH|CM7$G3;#mW#13rHE!iJJb{gcTwJ}*~Z)ckp1qd_b!nqZ2YBD#h>2FT(g%O zfd-F#;Wd*CgHwhqG#ShWl3rfCE4#(U-{{Ef(`BIIJcZPHBKK6B#M)htJ z_G79}>nwJ*hZjsfK38{0Y7JCH%yyc08?Fh6#Kdff#DpP=(U28^-^)fGM}c*pY@R+C zKT<*(pfP$Fsi3r?&Z86b)2OKh5?%D?qzlp^ePwO6ZBM3=odzl3!g4ts#@k^E8Lbm6FqKhRJ2ygjr5vRG8B%F#oHywhuI`$ z@imH=%5{2Rd>F4C@kz2Uf?VjY6|{n0)aap;*YlYiWAR77__UpDN9)tCve8ijoXgQH zlqg{}-(SWS(g8U1WD83;R-&d9=xSDxYwKFE*c}SCW``tW_c3JP;Kf>8 z<%aRJ`PrxO{JWWFUlQW2}NLzA{d&IIU8S76xD^z~>zAWN|t!hFZs>abRVpXNgL zU(UXLgDpNIBzn2fIWeTe2>_iUiyN$x*P)~#dAmZsY@1^Cqo|71vmRGV)VhSwCWT@J z#EX)=S8zqfgTH=Ntz^;|j`($z-Aoi6@9!6L-TGOpot{1(M=meUzu7lXu|Qtkz<#Is zdBz1k=$gtN#hy)^FC4GB>GBhHqO7DKu2{S%Q8V$6fX!<%Z)NwlCrFa26RCw^gqLi6 zB0XuXUoBqZz%Ms{so}a8F@EV2l2ter=@qy8asu&-`h~5FSJ4v6j8DvU`?)bdSLItT zF4&rb7>0BG-%ta^2t(}=p-nz}DovT_)nr8-I|?NE4XeYwNQKz*!V?LUNng_>k~;zA z+zFE=DkiA4a`O_o(fm}NT2=w&NPuEthMCEO8;APNZrw4olTK0bB{ZE|ZCiP^A_b)* zZ!$d{hl{IuU+`hSK-l3=wi&{_EM{^qJdYKzOD36wSy^_=%4Iz<|Nf#P8@0o{Vt_*p_!QEO^ETdPOgzXez9!{EcJica19UNbd7E^5^)e{ z5E4OaWBj_q`O#O!xGE`7KA&Y%WYIwFM1zY3Up#<1Ix@pmm5BxwXSv^rfBp-g7rAS0 ztT3!&prdDC;)!b^Uor%l0Ec-bc6p##rAe4U&Xa53tn|$wPjWxEB%-9;n5-iDXW>%k zCt^VXp@^t{nE0sD4j`|}aX&R~m;A-0%fb|P{oxA7X6Ril$@9+*V4w)g)1IE?;(?m= zcj#OhJMJfcOH*kAJRY58Wz>GSy+jZ>7C!_W==$wSnlUnA>JPV8dke5#Q44T)!_~Fj zkTaN4Xo~zFZt!$S3CG?D9E;0@QgiL+o=GfIQA2Ucvo2_w?f&tvtmYc(^=yK@v$M6? zTs>W0qa|g3Yq^KBVd?dW2#lmwnlC@U7+>ZvV z;4llX@^ua^uf<)yxd=(29~-X3CKcuDW?z>4#c)2Ob748z+_GHZy23i;NQ~7<@oc~^;4M2e%Mv-1JJf^nA?k$HD^;W{3dG#+vf*`oOJ(o>EN6BH#RJD^h$#KwJo05hqlQg z3~_JD>IUV-tsZHtQu+2diT_-0-bMnG+t?xgnv;T9*6#!FzYw^E*AD43?2`x~3Eeed zlgL~Q(M48rK@xMcNvlh5|ron!b*?i9mrg8(F9ckXGp`pyK-L9jB=HO7AeiC3VU?R+J63obo{s* z2{;IcXLecnNjT+R<#?5C*ljW>hyfQnI!jNdOX|LVWqB^r(L+8+I=*oA{+?S*W?V2r z7Wka*TRb!xLx+itz9H$i{>Q~tjfO@#&^t7{pJm!^6rbVT(Yk^V;mjCvjV@Q1SqHov zwx}1X$`j4U!PV~TKTJBWpXD-XLsL_I;%!~8xpb%4;a2#6_*`pEZ`YBhEmQ=3G*U#$ zn30~2cx>`Wad2*CsmjKXC&MDLAQDMabmtMO3Q}W<#xCFGoF!qeM-Pmo=Uom8ChEss zwe58I(H~RgEzKik?ZzsW1_gaqxL(iVu?By@ZPl3mJRk+8l;DhQJqcX)P#J0ck$(2{ z86+3LZ-4h^v|X>2$^C{t9>CV&u(+0&go^867S!~Fu8j&p3^Vf>S?Oen5X~K2V$Fg z11rf`mWGI6$P|{G%NWvuTuLQo+=*0Dv$3U(g;H2~{uG|jn@BbR1|F0>gXt*+KAy&= z_!z%CIyhg-F;S2zf#)TN4UeaEER7{Uw`h*DxF&tM-QrFDn1K8hR)a>D1Q$HF3J*5a z$SqU{>=jt}87p!Sbrv=X+aVBk&(bn?+M9TzjKjyJ$7kQ0$IDwa^9ztcYV1*Br(1KWehvrIrgeBklL6p9DlA)*xZ5BmVeK#Y1shwz~We0LM7!Hta^ zSsOU>`ke@XqF|i`EQZx{{8r41MJTD_TVT6PX_kwo(IQw;RgTk_ljAQwi_aqxRz5+E zU+oP-K7B#0cKJQ(cMZapD%uiP*_-mkb;5X8r57#Tq)x*>h^I7VXCo4w3^6q7^k;JhZriWq zb_qN01SkQ;LoIY$A;{$6AZl$-+J3&3>jI^lATt)^lG*Pqq|SHP0jmZoPcJ0lhTrXd zMY2o-?U$rFP z@#8dTamCBxkN3~7`@LeCJU}XT_joen+&(#ZXT*z9Lj7JzUWN5T>HI$hmaxyh-HCm$;ird5kHl4aJYO5ixo3BES*X&wS2%;E#?`*p9`ZN# zwjPlCv3xLBX{ogLX5-V*kxq&_wX+uK;<4yO03I~VnoVdGo)ra27VW+-z!(kh?TZZ4 zmKYu*lw`z0GKDsgH;-)#SSjH!B;BDUTJ~sHn3Dx?*MEAXW`{0!yTUg_z&nl7t*UMJgBV7Z&4u>L#u+E=F z;jb>3Wu}0bN>t}QKa@D6G^In#;`(~*lfLiYhRaBt_Ldqt)|KL`&GE{;=OQZdNCd7v zCO&&Bt&{e=Rm&1X!eDjdJ-BAkkZC{;$7#vks;2iuc5Bg?EDY=_d}Wb@IXga!Z>G4j z<1Fl~9%-({J(z+^$c6Qcy3Kb7)N^pO*#D#U1&$CIwBTk%2Fp zBQN|{_0&@(^u({h51e#IM%EV$YP(7oQFJS6-l}-V^B6DMU9q3Bmuda|B|1-+2BBh- z7SeP5@v%eBdy$orDvI0O>o6t0n~*5IxaywNVnOT28iT{^>=O`4r20dEfn!iF@FD#Z z(8!O$u^E!Rsx%Y?V!L|X&R{fhNy86y$d0^pfxdabJ1dig`TlbM2NDz5 zkWRL-JZ&8-OyP~GHl9bE(rI!o8CDro;6A%2@+}ouU1E@LK1blY*wf=$+ulR8hmnm% zTz`Z^c&u-k&20U@iRk%2FKF3!lD#{lz#mB01G?nHRk(imP5INmehn6hMS#p@k;)~K zPCs-i7P?XaoFm=>oUO#d?@9?rQ(L?D8?2(1CytYLJ2oY%MAmk+(<^U&Cvz*L{N2b0 zV@Jaq-Ptz%^I>79D(CxewWkwge96+bE0OzsHCK4DCQIy>dU@7Og)!P z;8jsuCwW(c?G#|fURLB=haTLTLu$qCg;)2g{bAGZ`GZb(0fqv($92IUri<&!v%F!8 z1`a*UKqbgVe7jNMp>x=AcrwJBzzuEkUxxDkVa=bgb!dSya{m|FeDJgvGIS^U$IS~y z`Q{ss!@Z_)b$l;*01CopdQsUjwEV+z$I2DTs=zzMvv>C2q4_=(zlIq8&9l4TH;t5K zc(Jo8*q{JvM6QH3(!2gsRVheJ{`IiG`Bc`XdQOc&FP!(~K^*Uug<4p33irQ5K%wto zPtXfW*KcgVWr5rgn8OfiIYB2A;r386!LSo++YRrj_)4xD9v{?P0HseeVd$EcqYm-p zUI4j1)9@zgrblotglQuskZ?oo8$&|nn~)*ZhZx`y(6$N-lgBQq`Q;o{hml^>JOW3wLh`x?vfVvtYyvuL=YYiRDKI#t_a5RQ{xQn zT!9@Xd16ucj7fAu6&*>0-FxBh5wYGaZyVfG!^h#}D*-uTNn z+&M%=iZzV+j|q41u!sf0g+!6~&E@f7C{85#XT{f#KcF$O2C`2@W$%B#R8XVtJ0v6x z3$XA-j(B+N*FK@fIhH#@b38meWD;ISpkY)Pt>u{%cIbxJs3YOm)UI4=a#zme}84j9cXYlmY$fp9eFg<3Tm1kwEME+F!LXV_~ZN`nm3e2(`C9l z()O{$l}576=l#V_Oz*w$0hvXW3inss zk}c6LGX#6d9mz^ND-;Dh07G9$U@|n8X;_gt~Ex5bQ z>HmE{nf1+F%v`|YB5Nh#oSd9z@890f5>p?{_C0a59TUz`roQfz`__}J!RxF~T0ie-qr zT_;z2q9eK0_D&D>86~A@-hU3~mo)noe2YsH7TJXT9YiM%!&3ZaD!xJlww2@y>Wu4{_O7$;aFZN z!3+Xqjt_sYYES+VGGbGjPzhrF)S)Oc@Ag70BI2wy9pPZ3S>ZU=d4iDjfRlx(`D78& zKzrtHWol%}=~xo_F4)900+fsXwWSyWh1XC^QF~k*k7WO}?fg}=ZmRyT_bJ`83k!kw z-stU#O*vh_L{&^gOZ8xHB|dtUb9%4`#3$z>^!i4&4Cm-f6-2FTleoFNqxEOat0iZBz9$G5C&sx-CvIHG>1q_OUG5*;OmN3124vG*X?~? z8){jutKE=`lYJdG0bJWRJCURlf+4rB&OhlW8#{13$E2v9@{LS=^t;<*rzOuRk9$W) zAb?L}Gn%8lz9c{(>P?5ECq_h~s-E5bR^Xx5+fC4Cly$^Sa-92nNlA9~%NOaL1l3kR%Oldb&@D@7ek(}tB5!Sjo>ZY5ax$uZvFhMG4A83M5$g?{dw16NMFy8 zeK{LUg^v4yO=h^J0^ z_+5SOZF|nB_j};33lMW$_06x`GG9?s<91(r=ksu_x6<|yUvJi@7REL4U9U?3Gd-)nIZ;V* z=|zOesdGZYCZguPFQ#;lJVfo1e)*@(z{^?4&2#8=JW>xq7ST zwRltc#I#C;OUX%oEle-=v{t&Iom_)n%t@4_*BF9CXh6W1T7J0%GrPYezFj^cw3&ar zYgzWG=sj+XsBb>`JJ;gXvh0@DYijBl6Yt>YvTJi#-@a|XKP}Mt6>LV0Tk+HB^V~pv z_3tyTT-GGQAk8$*+dayUx6~RzlWGKP25RelKG$ns$xVICs8^V@a?|H3Ls?p$0cHP$zhoLX)&3JReRxVN8~nLK)5C;WJyEBl9=I#R95hd$X1zkf)bnq7oUZ$-B6 z>{7CHu3TIRvy$hCREjI&AlAGwHMCE9==2mB2jvpaH#Bg~Y776{9K%#dTpYo|LdL|> zQlMW%`C4!uC}H?!r?jvU=Yg|4Ps##ONQe}-{;fnX65GG~gQL#^MJ?e~KfTL~!sCmk zJ?>PEc#lRJ`$^*hYTt?oZag?dB349VhbK#!8tTi$;7HO0eM_TdVZd%dMwdf0zUL+t zFin}luB6d15Is=bFG`H5mG?rL8Dyy)h;$kgd*)%&Kv=H|Aamfx?AqIS#hvgXB%hTZ z8k!2~1}x(_S1j`oBkWKIoMb{T@g2G01E~$#zL-|*>efhfP5&lvRS)}m<--d6N;4K` zPTCck`F8vRwI2KBEeexIOGIg?nQViilgbaVgc`9YWXjzGJf1%6_DjFDr@};x7okZ7 zcj}$>h%m^e{v!{;tg>StxDhWLj^r2>`K~qIw za=5dsj-Ydtf^D>5D(1`!qU|5Xsrs}`a1KT&Td2@Z397$4uixYFXSgJChXr%krGvU0 z^nya>?|e_NEZiFe%SEHn)SO_B9li-lTWDm112TWA%KAU{-oQ$4D^SII*a`JUOHQBe z|KXtMYcOLhTU%mXy>A?xzcCV?{F!U%)Z>W~%=T&8UAc%lM z8py`<&d0TPK!n4;flt7lf#w}b>bu>|@zXOpw%STM(W4m-kOAF0UypcmG0ykPGsSAw zq4MUzYg~2Wfsn&E<+pz92{kNc&G4C#O&VDb&OEpS6i? zbV2NtmNJHsvzwkz1{fFT93LE8(ySk7{`pyv%j^^O@ccRFk@9-Uh2$qG-*-Xsa13@s z$;0v~o!c3sq|{3Cx9v?t(FW`$LutydlzmZWJ`;_!Ic!l^2PHD*qIfBb*ePC*voUdC zpbAkSQY^pL>{z{Z#*EvV1aC;oVX|c{gc*-{BVbk{>e@!t6Z1$F3FJRa6T!@6Hn^Rj z)+87Ge*`@|8Y+gc<7?k6)V(@BM*kf*8=V!n^S0paLO9~2Fz4SSVY#p9bvSPpBw*R_ zArmosHYbWkxO`iG9aJy3)9wst@;Yls$%0HK<{n#jaJ0|XMc!FGNDm}Y;&S+P+dW#> zN1sekYc;UeWi@bIdLUpX^ycUMyrm$wq3rHl6(f{utdU{FFhN#RElmLy36=1PF=rIu zzg2_Ke18%+Q1~^rd$<8-w3?eZ6aw+MmX%b(qV8)07TVD1j^U~IdUcJ=1bri1-{F=0 z@8e1(a6m&b@BM1*Xke`_!T*%?$AisV`-KPS(K&s0vJ;6+Y>w|v9(GqUAR2`klGeI? z0=BB{5&NNu)UwX(2@Fuu-2CBPQLF11>?QOOTMlo{v0faYjkuGP22f?1?LAcAgwfEV zq~x+dHG;soWo7DzHsR9wt3WySlF#fMZC1&wG{6f}dr}G=#V>XqOGA zhq6u+A}2R!SbjdOe&ky2&fy`5kx_=9f39nE6tk|bE_0%>=J=RGoY@{GVdWQzRgBCG zTvD3X;*l>6j&m!F1}iJ1p_T8JBI{r_ccC^G zg;X%pp0;xpDUUEf-T?i8nPm=e1!C3mybj6n}x;BQrO?Dw# zT?T7ocbJ>(;S*L}z%6}p`%=tf3tXnq8SS;>3pmI36wzPQD>%kwD`4?-8=2C{dc@+= z%GfbOX-I7I6>phtm?p4-Ej{Enl}TC$crE>{!@c;4V#2vO37=TR!e^o|LMdYw_rpj{ zF`dRE#LwRGqlIJK{=(V4BV)N*HD7*wHXC3}rpVe2dxSGRJL?la!JL1Q&7B;c`1&Of zi%3qk-f;z%p)9V5M#LqI5X2)kP#YgVj&qsn(yq{%pw|(g zC`HeOh}V0V;i;WX>yYg7*RhTdgkaiE@f9fkT5{|3Lc4Ro3-5$(Po^$xpLU&J=7c;n zv40RlmafQeX(8rw;La^8a};@hY;%Hv#|P+NVYY!0vF#A<4V<-*_KX`@Pu*K8-<0{- z_UCGG`WRs@$VJOB2g_VqnOY-X z+SB)RX=%T9VXwNdoAoC1FV6k8Zb_2U&c+UCjGEA96u4xY9+G^wqdNoy0r2*S%2Y-N z%6g>yZmE2?@p9k9cH^flCHqc9KO&2Cm3YV4#2MoQC0m|h>v-k3L^vp@HH19V;LyZY zAx-pZJAx489G;)TT7R;;E3+}@0!0#N>be*<(b3l37?}Z-$QcGQY|=+5>$#Y_$Vn1C zY01vHB1i_N&@U?YPqd@UteCH-FnuBlR=Nw5IPnhNUYii?7;k<>^KJ8G{!(EWrnoOI z$A-*nGGnt$ZO`rK%u9!%eJr|Wf7QT6qvO1b_N*zp-JRhsKyYA*27S|H($05A25!Y+ zc9vHlg`UwEVAgy5n>F_oH>8yW*8I z=2`fqQ>E3~QSqIYR&Wq~aU5+N-}Ex!%?Ib6C$8ni`AK4uX3GsK1n-3meWVanawIe3 ziT6jaeX9@;ce2T?r#YoY4LF=($dAe@WSkAis}*XisAV4OMz;GCQLW zh+=%J#z{am2^!M=#=B*|qhGq<8W@QukA#^ywUrzGRvQ+SE5ZjD6U^F7VJL(guSa6K zSzvbBv2S+&z&?9=FF+S}c6c2YNjbEamOcbch3zZEh5eMs|DO_;zj?*u6ZuS&C98}2 zv9i8uwp2%D)>5>3R}pKFV?V1nO4nqj;LKJ1LB#(EQOG|HAs96}BhpI8{<-ISj&d8D z^ipNVdqCaW6B?h~=n<(c!h3=_iDO}5IVkLEs)}V@GPdDGVZO&tI`#;!w)_RXvZ4K< zcoCRS>!+Z2Lo-^PxBKnstgj*SYm{D&A0eRRJ`z;!e4hT}R$FW3U_DX(`KlZ5CX1ZB zaV)w^wq@l|=%eAN0c~m>?@{kZKR=PdgeOvo2%Y?N?yXk;W(vfwFFj>uS*qp3;&MZ> zw~F`VgPgb!*DGytW~BT8<19DvIj(pe<(;jqwFpYgyt=>en|%qr~Qc;o0RC4~}gh;@&K?DE9Uvh?%VT^v6{%qCD;TufTk$2I^T1M{t^wdIpe*p5J7o(Qw_1XVwhgKL$cF8 z40$}OH}*I<`mhDLr!T`vQLI{to^zS{m^4M|?Er2iD(N@A^uPZ+p1VIycCJJx0kz`l ze6(9=#ZSCP!))v8F@Yd~L{#(jnm~ScUsa?pU{OaTP8!{Sbg?Udrqvt`0#(vO2OAr>FQ zq((|U`{aS}-h8{#OC8A`;u*D~jRUaMYHu*gU|F2*PmRoqY6ms!v1@X@k6dBaJlgPo;DU z0WK0sYuJDQCz3#qIQ9;Y50IrUF2D7l6gTA3L`0J}Asm)|te(cBi%WXCz{z~v&jAZe zB3l*HyrfA$yC{y?%1}UL(Y=GD4f@{XpY18YBE49}6Q0Lxj#jPs zP7ClA9X*as_l1F?o5wwu78SO@*+CHzm&LqgieQ0j8jAj=cU*@@j?Ct45^AKom+}F8 z&Tv@@+p+i!G65Sj9ebVMS!JPtoKPtQ~xld`sPi%VyEF#i;QhV2Qa&l7P|K@vH zT&zzLm7;AXe(BsRI|l6v`APzvO0@)8YH?Zs&YQet_f9rdgX(7!4CqfDa%9j9V;J3B7ma7@&c(XMqLy{g%9Lqa)lGB#dj98a)sjX zVUEnkfZUwCu}jrlOy0L5OjpkgB||dr$9>L%-N|EN>q&5F_owq!d53RH>jg9!l;866 z6AT4pWEKu468tQAD5??B=7Ui>X`2Fe-dohp8C;n69;C0IUy zkatpWX`GB)j%U{5|8|!Ds6Ko1qhQa@_uq+cf^Q}oU1H5-7eD$cJzt`|v^<&&hg7dE z$jqWAV7+cDSf%7vKr}BQAwT$YZ3&SS+I0ciGK08dOp~^hBtCW!dw5;HLaMHXX5o89qg z4b;%V7Mf|k8XDVGv9RghNNLGy`Qu0PB{E{aRLTF(RwJM5U zR2VtSGK-9zGYO3r!i5*kCJ`P)3#q5yYckgkCrUO}X9`O@(#$oZ&2ttogr0(fSR96P zYikLj{l<*DB?Y7tY9+dF-#a;oBrKh*5CZX^r!;?RS0=N*_@2Z0Y9Ur<2mIa6u~sza zMn36S)DBn_PCEA}IUZv;4ZkZ>XJ5F3Nvd(9@B_LslgI`Y zdRl7guQiLe$Cmykuh1xe5MoMxphJghsEN^YzRwc%!b}W92?Z07aXWIb%YE$~qzV-) zIT7aRu04q6L||RLG_eKzvkqgh>Gzuzo#1LAimphgyVaX#N*l-2wJof^2~X;v8Vl*X z9~(chV>)0tm!!E8xgxk(+r2Xi`~f}i5drqS>!rM8g0$dyvCKjmJyXlQ%4ne5 z0|8o3Vvw}<1Odl~rS$N@P|WN5d+*qEVpL59_|l^+sM0#-*yf_ z;DZ`l!L-GT-XbKlV${E^v!~a>tXBvtdJo;N16o*djwXje>D0ABq}u~${xEDh zoe4>5`bFN=&zbcbGd5u5fNm1?IJ&2zE~K>l{s6Zw;Mf%eKUwc5Q|g6sfAso}?B5

r* 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 Tb9<7R={_@Pe7AtQ#KdrC|0X4MGsDp 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%0A7%)=ZPFOU;JO=+rFrEipW^yYmM5R}s3yJDa! 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 rUsb`LSO4zH6T-5i|AhdgkciqC};bQQ-dpI!cAE literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_entity_new.png b/doc/qxentityeditor/resource/qxee_entity_new.png new file mode 100644 index 0000000000000000000000000000000000000000..cfc179ec6662958358763b88e3327c58d3ceac13 GIT binary patch literal 206328 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

xjp?I|!g}7Rhu{-?#F2KHL*^Pf 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

    S}e+@fWtmdAaXQVIn zKqYuu`+8SY&dES(^IWOV{d`hX|B{+I2VOj00Q@@VGI$wJaU~IT$N_c7FYe1Ym|yTi zl&l>ClwY?E1EMu~?36Qpcy+u{)yX;wk+Uo6gwfc#-k+#hDiH@rVv42vc25e;F)r+2 z%bv5)v&Dv9%-5K**6F=OUBUQeBgka21s*cGo`lHIWC5h7*hA05Igd}^F0Br9QFnA( z`DMsTjK>2q>`4+uE{ktziUyG`nvJ|!!&yMTv1#!$xZ6dE$TyOtc+HYM&Uoqtu9lpZ z{pzqNO8HP>$3-k9bFTV|GyHI~E$TiR3eW=Q-;y%ycc3m`R7(7+e|=&zUWc;`D zc2iX3274Wt|6@@5pD5FpHME-J(tk5DEnqeSmkfRSwC5HQq-gTN2YQ5dW;)?uI|==~ zp!5AV{+4?i`ec}M;4lLAn&@KR$;&4JL~P(9eqwD2Oax(NtS=Z1axk(`f%9L98e&Ab zFct*IFO07^DYj%!fa{Tqu}KqF3J>V{8gMU`@xEbX)+fO#w$W~zJ9acp5F>%YWp69F zJtP*(MFONkD;v7=iIo)5-w5B7^1?&bpL?h?6W6XAh_c~{fwp83b8e|LrFvV?zxt3c zawzXKz0RsPv0~ApNm@uKQtt7c?mWdlQHh$*?$XCzW!Q2my6m+sRhq$;>~%6tX{q~< zzw%)cctkJeH1t&Q#v>n*fqeJl0=a;7fBYi%j7=I2Hpjm4htV`@Xp77xd9dNiUh7?_ zK1sTGVKEnRq7Tt+zd4mzW4B~xcZt@sE3{;fV2`1}Vx&%%8K+EB30i^O8%WL4q~^mF zwSTf%SaKV%7?7%#!)s6UoYf}8+7rg|u@z^1pf=O>Y_xE#EsZs%!J9;w_uM$rn5m>{ zoC`7C{U&HyZzt9j8vZ&GA)8{2!E^^;=Y5*zQ#j zq*FnOAw(QHlnzC@Q@W*;?nXc)rBhnRe2CDH zHr~L>5&`rB=b`E94HGxp<0lbs?Tf;#!H17)jDj+Ke0fRNPNbwP-(A@t#0}dqFdj8&*SXGmeLQPVOf7h3Z6A0*T6&v=ftc; zHRSx3LMc#KSvt|^u^Wd}&rf~H)wbhlPRouT_y4e6#V#)b4W*srKd1*WlK!BB`r)qE z=3%~n9Y(jkG@7LTQ&tf7s+XvdV1D&7?Lk4fiBs~m>__zLY`W{YH&~?kewL0w-cuA# z-QG3{3|9&u_0&=4jrL2LgM{p)DJDCTo(ix!L>S^Gx4d&EW1a3MtKW+q(Ng}yO*X(; zE|uARj*ufb$p!B|n_aM?Y1qAn`Zh#N)jH)mmDvw$@oaKlW-8%c)9`lK?~v?FWj{eE zRXUxG_1-?2BHiHk)xQStwkb+EKypqRB^CEzz6htf2t#~q?~#$Oa*;qKH{`b&`6(Ep zKI9V^{60!-1|O0Y>!ZTpb&N;Ze#L`j2}j(cP76ubVeHUw_OndBrCmz9afkhg(Fy+w z-b}ZCF>$J?$VUvOg`eMq&57HSbKE~9SZ{nQAGnqt{5R5sm|4wp8&w$N_a<(3gGbqS zqlr?Mt_6N)*q6DY-exv<`*c*DKjHL}RbdfCs%OTb(u)7e6_W{~xE@bdKLU@(L;oqr z#P3Pv?|byvL|x`zx@z&^z|WMNuAz`xOs-sIW>d9VH zIVa9+pDo(e_VA9b7nN;wd_!KINpumjz#3Q zWYe(gURQ3bK3vqw^PnkJZI47 zn?7jszET$&z86wrxD;mB;S<0glQJLaG4Sh9o5-kS?X`mxyhNg?N1`6ZgnetGCW3TKXMG@##%-N;~<*e z(PC%mUnQ383${Wry2<(Fev3#Xi@R3K|FscWXY9*mS4B9pdyEY?2A@>SmIX`r)uP|V z%lLWd+CcW?vZlJK*4zV~`ePs~3#jAqSz2m)4beC^;7S-|TeKVQL%pg>g_^y_y z2kT-8_B>8V%6Q@5wY(QcF(XW^$$lY^1|F~zIcnK1N3#(Vb{U#TA(|{mHC6hEfw5X4 zg=4x~T#z*Ggsu45`VZ+YLdX_-SH{x>uK9zIpVaU|!sKV7`&`ew(qq{fNsH6DXz-lR z@8IFJj+JClQSw<)at~UCL$9M)k!);lRP-LbxJU>KH^&dy=rf(T^g8Ve=i%mT>8X$^ zvhHBRzaLtqW2iSXrmPTne$@g!J!QPGnSS8RAmBIh4_!@e1s?5apdCB+$NBv~g#gKE z3x69yd>-S^7M}&F{CRe_i!W+QYb}6C@_z98XtDl3UMM=S4$zk9HI2OMZ`5J4BJ@g4 z#8&f~+fwiGEqWe@eQ-YU7Rnxhd(MiHMyYWr+ z1tpReMKX|-3WvOz9o9@Du(=v8v zdAW(#+$=~Nx2!0~hOfy*TYXxF51i*(F!X10qigF_C%;HwXW}4|RQyd710`Tw={HK` z(GvOp_%4b*873FM67=*KAXntx3MunbHhQab{PahGH*`KFgyL(dL$XjP@(~Bos;^^W zfk;?0NST!pO5>%I-27TrHA^*x5AkTw?#0ENsC2lI9GP{IqCJi|o!5b~}_RzF#Ntp$vTOG-LOIVA2abIb< zSvtd2tw;*=ME$SAkO*8D&QD@L2|H9c3pGR0TPWp#c3=t4#|^T}&zBvBQeftS3)!-( zE?_tH*O@|>O8fDsp@%#|HzmLQv|H6XM{|@cx8J*)UvP@sUg2|JX9NM?#+U?hb=}U*!l8X_v8c@ zfSnFp5rV&I5zzA4>_eiLisH~c)+O%Aqs%8nr-ev2U+bx+!+wpQMck>W0?TRcwy`O} zPs6JpK4j7s6ln~SJ`Wb~OteA;k&B!ta$2l$az%`T$x@RtQlf1pf02aKO8!cFHsnP+ zI{Fbl9!?M+fi(ndbYpskD{a|dd-)t$^6?KpMYb44i)$B-bGP_MWJe2Rl{_D(K8Pk= z>$j%)yO@+XX6X?{DGp_n%<^jZfJNmUEG|wSz)ju@=QnZW;}j(07geigjJ)-b39rHT zN;ghg*VHopdytVVWPZ(k{&{`7JXQ8UYx&?8D52(J;upoinS#OCG$F}tVN$e05A$-t zU6{sVfa=j=#e%S6xyHkugToGJ8ucf7DQ_PwQ6JFo6&*8mJ*f;iRNa;?u^XsW*0bT! zkSP`!kkWI7ScnxasPQat@m|sAwuDLY{lvY0I>cR+VYL zf&;^B&XPNH;(p)I6!*Z$w0~O0ZetQMo;S&>v4;Eew%Wr+olo+)U9$g)H71iyPEDhH z{XNa}b*R?DVD!rFzbsFqdhFw25W?i(MP$J`Am1o-u-(35Z57=9#TLR}NIq{#>sB<# zlg{tpUZ$B$OL^O&shBDM=lL>)SS0vlN5D&KUGeEl!z)+XUp~;4;PK2qYLOV3$wI** zo@8D+yPulEL=;;Zm*LynEsfygwePcjL1{8M1YTS_?Td@w*2$QP zLRY6!?7W(ww4kbO`bF~o&G-j7QFWt*H4fHA_2h$AG3S1}YG%IShtu=mE?3+umI;nx zSYP!)zkmQ#n!UE5LS-hjFEBv?nIiSOt5NeAwz7fb%C2(E4AkfK)vTyD8|u6ana8S+6o1xj}s@cacYce6Og{pMYP-Ls&-!8rMWg9?7v)lv{vTfVxMYfzWD@GbZFB@3re=dbz zIC`M?W-!?(fAqu32aW|?HPa*2Z%tR4*iVPst6jNR9!Rv@YhTbRyS#AH`#pN*mM1@| z>BgZ6gVH^&EIzuD_b-etso5$(%fef8&u0qSh@scY=1R&(a9BS+ZnM-?jv9Tr$r2WQ4*fF@m*RXt3{} za;CIiLW+3;)KR+rUKRqL;f9IcfA$M|OCS|)sAArDD=34{f(Wxo6CQkWr7ORGmGIKs z2*sILF3uthLRjb}HZW}!jH*7Mm9~o;cCXbQgeQNyfkw6Al|^DBUAe96B}ZhMix5>C z57Z(IOytCU<$-cW)nMrS_8`?wT#{g$_kE(kN7J|Q>#0ub!&@;u4@ywE=#QcI*s*6< z{=Q9LIkWDqV)yESlkHtA{XHOvtajRzwq2;hF%Gz|_un2INMfz_xv{4)xjU>Kt@_eW zYP;O5b>Mq7t=)|8pjR5BV{p@jd*vKGJmDx=~_gJwVcna^^(N8w7ft$XQ0_U%lD1D?||XC(>SHYzjA z4NH8I`M^oZX~w97to_)_U{{}N+Wm8VfVFZe(|`mv-|$#;5hSgtNr6xODwuw^&vfhb zS^$0b5O%yuQ{9baGG(#5d1q=j-e^U237R4bmhQ<>seE>*VezRuCNEL(q&ZQ|BNSwRAHM38#R4+1(cp*he zF)C-CX<}#G)ARV2kG(4>kz}7?;wi~Ek?BWwqhd9?--vI-{8r)Kp87!CIt6pVIl1K8nS6z7dS$ifdZ*O=USs+M6iAu5 zalg{nnh~S*=UdW;4|eP zjVmb8R6(}b_8eEAzmMb!qlG^g+>m7(l+pd`Ym62&(9eO(UIuRW4Xv|K^d9l}x;wy-a?`6H7kts} zQ`Yk;oNgc7$J~BzQ0M~(u+Q@5Z$B8Jw%)oL3 z_mriZ?e}9e=u5^Z)Ru+KWqT&wkd9M8dZSY~q|PO06Zfqtq-<3sO!sB~s9wos=~B$< zD$fkNa-{WHsTB_@4Dm1=qh1f!+2J0gde>Z%deAoZ5=}De{`OW+h8yr-DUujgD0nr7 zWhKTngLAmt^2=-n4T&Z#Z1%$mIFzWMvRQj_Y3E#a3Vt=Nw^k8|kKA^taPqSli!aNH zWHcUvBpxq$RrcQ#y(reEl*weA*`rMrQEi+gy7)y5qRzwA%;WgG z_TDoh^7LUsoi@|_uZj9!u`7(`Sm+LKj3^+5msl+D>Mr8K5Xx z@|;#-x}{^eCe?C)tA$(4YYprc%|&XyobA&jWiIuP(7u0Ht0>;PI;a-L8z;n0QG5Iz zBSy^PK*E9*(Wjm&4i0AIq)CHNt{_uyn09&$_b^|wKXDLXf1z;IMWqJglzDI-y=B57 zb^bw*ju>Xb2#+7-ULf;~r64f&dIW{xx|mq*3I$7g%7#_{@+ z_YQlyL_cL*lFa>ate%LzDw1*B{nfQsDs5sx$5ZlEzm}A-;uXQI6^TOA=@Q->9?$HW z4GWFe217i)*+~UqX&^%N6$6jRwFdTsTk-4byE`F@h*BtpHahb?5Z02+S za)qgjodZu7`J_%KoC^E-LjJj>H@PVZQYBvMVK8~m6*S$UupN5|h)~;qLme$%BGZiR zNs)gh{A5!^FSh7Ki?_1nQpIpyUL>%Rd~iK<{CzPrkXU&;btyNC{0k4ud~@y#hm$?zYB~OzqM529=k0Cm*7+@|@#+G>X~O_)Yz> zVu{Z%&_0AT8-4fBCdovk>6-U^Hx0uSNgfFC- z&=S)=jRP0$Xs(Y&sF&hu1*@eK9BSuZz>vInG-RN~xIp;igKyAePnuKtq)NhCE6aEc zZ3yB8v~StKGSOc1{L{e9Xd=xnO&I5~oAaF{(`w~iiU%DF&XvVZkx|JJ@Vze@=6Uw|FhMinhK{O=IDvNU*V0$tLKH@ALI!UeXLx>-ZW^PM7a!&JI`gd! z!<*d>9D!b*Xt@`_F+**#y#GYxYN4q#U9BK}>a?|bz*_AR!@5TiO;}u0PD|+%LPi%w z6GS4fQLK%hjHfs{()ruaoc;}aas=HC@;!S}C)+`TMTK^v3E!7R1G8bNr%mQ{o(R-o22_KHcs2wvyqEdlP2uC+-7YZnq_~9lYbC2s(>y(RNJUVe(Ez zJj>CZDT*LFpYV<=ldC^ht&&`mEM_u}h3Q^a@nNf?XHgCVoV1R7?8&^N5zi~)%%0P| zd4t7k2@ASCfpyD)2|z(u04JnaMGQ}H#EUxt#Q$Bx3Z0kiE7#f@If8HU&%3BDVxP=I zPw61RaRO*1H%+oYA!*(uJHPaI6nQy}S7U^SGaK>Xasil@$}UgBIxmNqoC-rKA!#U9 z+vXdOkH)O3g8rpjF20xJ>2F!~^Fte4G)au>7IQvfiyFo)DyFnokX~RH@rX+grf|;g zzFgNfcnZ5Y9q2SvHHg>5TG3afREha5F3#R-Bhf z5Lpo2p%(NUNy@Y8wCBbV4`AQ6c6Sem9v?2ZXdi6v?Br@!8olM+I&r#gm%#QQ*o( zeE2A0t2%<>l+Rujy)rZC^Ms&r>gkq8-0oxXvAIGO5I(Jl4yPCG)M40Ca|kB3VhQTQ zL4$6VsD^k(&2e2sVu~;CvVlhmk;SOp;;$Aq9Lq`({m#>p;2-DdK-{p^>|t*F6*0w` zY7vBdow^;2gj-bBZ2vH^2r9tNYrWR$F{U>#W(``j^m5aj>Km9VwH8*ocO3XxLotMQ zynAze{^Y9kmBRXAg5%l=gR#ftnhc%ZD_W6piOjW}Gcm8=%>rXJs_Bn**PDk4DGxIF zo>5}>N?HpwuP$n2g!7a~Ih@5bk6KUP%*AAO*y09<_C{;2KI`F5twv=N!c@PCdH&1Z z#vF3TYSCkxei|G$9fB$I1>8xT#fUH9e=*E2aMF5F=XgI=tbrnOnwBA4@pGqdUfst$ z74newD7#GkYD)}kD`kDHY?nhvlW8+Hqo?>W^_`y-?tA^vsx&vm&+B(*I8~NB;hK_) zt>?jVUYx6?ribI$3+O}vRGTzv$K@4I#Nly?bhwz}5WP|%qT4~olR z;6|7Q2}Vp9fDDV>6~%wj+dVYyqf0R)ma=&wNuSYXv0z~%3x_CXw8F)(u!Z z?vIC3|N6(t4H9(51mNY9aK%Ny9(7GL+`#J9MafTY3!Rvm^oV$E98~*28F_{;Qvpmu zA_(4N_U~C=L#?5gevad;`9|EV*Sm^+7%6Lu@pH-~>uQ~^StVjRDcWK>-du|M&%qQ? z0}HVo8dZ-p?Yfs87}htmW)dURr=7QxI2~3~k)42Mx%5PFbKAoW!F(j5aa4YcDj6J^ z?@%@+>^g3(toE8u7clh2-8Jwf^X|b!je8sKxs>Wk3&qck?sQW#` zyq-VJ=SQmie|LH7c}aSNifKhOsLDhmHN{rsC!*=t0?Ku9nJ0T1E5(OCXWqNGSfyvR z;)nU!h|AR0IKEq(q^v^GEYTv4RmCh-D1YAkt^6yjIP^Q{T7{@%-(#dxVJcIY_QcRf z`~`TJo0po#(3bBF{zYLzeDTqqAW<+6dc;$#qIG8>{ZHs#W zI9IsMn`p$nd=0c+*a0xMh zE3eVx*MCAc-9*4(9FRXEXXORrartzqcAm#cO=T#~?n7k*rl3T9TLrxx_hQdIzx`4Z z?n(Q?9Jb|ckGM-)#c|_8L}r(7GFZgu`?ibK+Sq~z$>ej&u{Q4LG9TkY?oqR1v4IC- z@4{)WG!+K0A*_Qfnn%Yx(>G-HHqvd{<#V!P}-IvMJXy){-p&`ot_=eo&U)GEoleRqmqC|sHKcnO`& z`&<`J&;t6jBczd|wetJhu+2bZm1PGn%ZWv+uOr8Cg3TnKr*-k;0X*8D8vyfgO-2nd z@`)4t_U#A2s-_CS^NaGt)d1PDpGLp9hz?5!jLp|tJPT+je|_c$+KxY810^cP@z@L7 zlVmRZSnONaflzoXyQL8R4nHM&?&Rczit$;m3poM1pNKEuXzeI=cI>8dn-A2*1jN4H zs8yx%ZO|m3`tWWJ(d)a#krn<}G(G!UU$S+;$Wv>O3w^Wu*WyXOfXkqn>M0o(Z~9Vp z3fTVHCL+E+O!j_g9yfRhUT}Fa5m46NOf7a*ie_6$Z2qWvK;`d_eJz5-JM=oVn!CA$ ztaogRcjqt%{LGuAmAL(vyf@b>__t0d%n$E@0Me{m@@hwzxUdT}LG`QHCGguKulHLU zNx0Z1CfV&$bm+1E4+82RN$oGcIfDL1p}~@JlUejegxY3R%VDi*Qtd zFE(93g5tdiGW%^XURE*kpBCRN5|p{#8Kb4W*6L# zqWMB&7@WJ`Fi|OdIp8DbKtJi5t@wCbu{>7q;9z?ji6q$Ol@sHubo$JmtoK1HIP?cm z=(&BjN!Kiuq?FY9-Nk6%zc@w`8k%1K2yw3_*>Nvw{6?{Ns+h_6O9Rj}-2V&cu1ut# zT^|nK%*#KD0QH?un7`_^TOAT!lqD`{KxvcgRUVSXnb@s~xkO$*EiOOeU;GTZPU}_4 zB%*_z53qV7)~x~r+l;sKY0B=au*cLC{n zT-KDXorl^v)pRGg7Q=05ipk{fKQqocJxeqvPpELc&L+hAN zt0G1o$fb>TPNv<(W&BlZ+Ic`Sk`pxYXzC6u{ZhHu9hnBhy{3*E;*FuVy zxQmrX7hY&CJYzn3S?>=sR&O8ME}^LObLaEV{~8Ih!htNkMo6hP;xpL{|ITp=CA41n zWa#2^BfS|-69`qhkC=|;bwkpH(mU(bRy{kh5V(lH;5}Vou$nT3xNu>Pn&!OS8T~ss z`jU~9J^2+>C@AzrWbc2NwcxQIlj1{GT+&Zu4S49f-C(Pz0Q4u57~3&uiuc1d-kIzVWeRJb@E#X# z_Qf2`#+-h#+Ih(17_2I*gUE{J0Pl66G!lL}nW!q(f|G17csm7u#K1{?SF#46awGm5 z^aLgTP_XCC>{#VmI>78x$soMjJT0v|@dtyFbRnkDo5QX+_i`t*4*?mJ zMX%kz>&0|d(=LzQZA6am=SDg?-f$caF9xl6EbrugPg~Ei%F^7s?Q{)@lC=4upK_*#yhH-9V9cuD zNxpuhn)RbgL7ci+x%Due6qobfk%yy@C`0YaEHYwowM*PVpz)U{R2lU}P1Y#gB#K zps>BrDmq~m8Un&_4B`T9l*v#J_0XUuMfng>tmqkTW>!i<@qkg;z_q#sb8(ISodwyg z@HYPWiso%n9#I}~=6PJg9`P(^Ht0F;GrV)6(mI&*!Uwj0+!)bfS{^Un{Boh0^*SjM zx??2H(~!WHK6g$As-}gt1^bjP1hK}C6q}cD-d#&7QWN{!R%!+yW$4}%oJa;~@cr97!UEA6@p;;L4=7iR*)b7E+jS|Oe_ z{Qcj33DkwQWFg262Z2#Al;}ALzmX6_@@dh9Mk)aK1d=pT$yPx6DgTs6FhiJpXm_wT zz0vyT=kjz`ACkTKT4n$Z8a#8PM_*&1(Y8{m%~>$HR5|%tqTN8G&JK$xkK4-3el1&Z zk|a?vU%HTvqgznN#fo0IGlQk|5qR=@+R$L?4>%4<698&%{_68_;6nK{=11wE(5{JD zh5W2nN8N{z0qGF|?KIDPaDkj`4_oq?u+eDop;R}R;>vGmckk1xPgwc|$?u-%&ip99 zuRfK=JF9wph{7;=n95NUf2qz#a77Ih%u-BTmqvl-$(cNJ zJzLtvLr{;HT_(GD+Jz-O_l$8N@pIuQVw#a_p2Q{tGs1NTj7%gtuSgv`rU|Z%a zUXc&jmp0qBFE0UWCeG*&ceVSKDhffRR+yE3hV%Evi$#VWx;?tiFwC|AEUHC z6#S|4jKv>5w)S9c=Y-NuAj$o8y6Y6u-hcZH_AyInYV2O0w}KE%4L1*={I6>vHa?wq zyTV_TR&_rQsCHc$Ujo{bcI(?W8D4$f0tHEcGYx{}o+6Ws zXWNlG=@@Qzz{h(8cQb2U z72#~KU*5M){CPe6XfcjahRd`IbD6_`D5}eS#d(ljZ+6g_;{;H+bol4UQo%7|ussB^ z=qh2TeRaQ2-0i=I?&;hmJ6X@V(j5uYs)$NkrBZ86NZ&BGbV&n)NWbJLnYsTI zeNoJ+dn6e6OrF7VSn(P=YNHQMx6;EMP0*G}AZB%oqoF+uk9xD!p8(7(J$cSTmBFtt zFz4{B&LufxMW43w#^3U*%yc7lpaAVWS!K`O*8f&7&6G_!dJ8BiZd~%$_B%A!inaOx>yW27X9-WEA- zMRRTRm9%r5<5zfV=yM#__Y(2nJt8+wURLK+@;rvv)bO8l2Bg?^q`)~Oc_nxl%{)?qYnTn8q zn<*RbWW&W^AtjErsKWMu@^W=s31M+9oqp?*lanh5!mm-5nCFY@SPv!=+sjW35xVTg z7n2)fNkpUXPXfBH#S_a-s-mKF@IHTj!k+YpqIJKdG^3uB&tWBMdobmodANfnSOAZL z!?jM{q@x7DiZ6(JK%BtpYC3XHu&-4<)tfg0p>#Tc!+^|&~%?4bD&7Y~3nAdJ*b8_AA6h3#jnH5Xy{2Qf zbM1ZyPci8$vFRh{v>vskvY5!1Yr33JaBKh3)oNrrU&EwIw200;@$DODF`a}eAvPj8 zT{EFTr-YV0r#zO*~VRd&FAesDc z#(O`M=n`-P`WfLD+9BpMaldPwl|T(oO6?T=@QmiA1&9Vk^g#G{?fG4XL|hQF~Z74cC_ z@MCPwK~3NpaU!sBjW~6(t_7Y{8w$UCv?UWQb0(9|(3gi8w2->DwVp}`2Cm0W0k!jN zw<)W90`%cKW8rKZbYEid#)Cxe;s7yLrZXi5ThhH9)5U$><~Ws%r(}55t$?aF|jWb8KT0_qQZI zv&AL{+FAD&fJO4>vMC#2b{bIyNlyO!)iqwEN&5xXzQ8Z-LphW05li)xYy z+`pg}{`{SD<&K?jo1?S&d1-9>(Ia<%q=WC-&FUTon}fMnG~})H^^#TQL}!@9`5k^w zfh4xFFS=27BM!Riv!@a{cJf9MPtmyi<(*sCX~7*)UXS@wJ_=3p9CGsYD!_*+K-)P( z&gxlZr22cKq74xErQ6#cbgr#lCPC`-i}r6CXk_T#$%0#m(ZHYMzpTT+MktYwp%pFD zDV{Vd76F94@&1&%Eh7jaaP*!Tadaov&EETSk*U z*p$Sv9U;PRJ37V}9ommOW1(YJOe6B7tE$wC1umdjFqi&c<_Oz0{eZ`Dvc9I0-Wsg%rY;3m7hZ;zc6^uwFh>wBS zYGlgh(1rif0dSUT9=`mKsM8H_%<}9m+PwdxUsDg03M|+jFa7aeV87RIb}ilo!2L;D zZ{EHMhKTYK2G`_pd=E^4Y{GRs8*Rdv8K@u5wowBivY%Bq`yW^}3(19#QxZ%l9#i4UpBTD*ViW0o7y(Mc6? zL(5A_s~V>s8VosLZXiajXU)B+<1L9G7 zVh+p<{q(LOve7uKVUw{qXUuz}hz7hA^6m?K0WQj1!>^k>Ca&*&usnxDM%-C&-Eeac z#o2NxK@fsjEFvJ|$%qHZ@2ca*Reybz&gA>K77qs+SJxAag|#!fTlG70n#)izXB*Vc z`-OOsn?Cr!4_nqPNLchXt#~2yh!J0N_+o#`RdSeLaj^_jGTZB#eOBmSt#eVgc z(x@FM)cAcZ-uh;Ld&%dP?CVBqZ1?uBzt)Vp8-k)>K;CWbV!7~3m7b48f4iIBmAXEM zgxi&w^)2VS);Lc^n(o3X%VE*4ZE|$hdq$)ck#iu~|7s9AY)PH?gsPN@fYxOOlxRX6 zO@z4n$`R~rblREu5hQl1(RwU*+Wt2JrNNdcE@21xGWU84z(&0Qy z4P@EjF53Oc_=i$4+rcb!0%%8$W|IZKx{O2_J@8fmRj``?Q=|&jRGbjJk-`@H;!8hW z6y{{>lTV7g7;g-2CK&5>Ez&!|{&!sjGMEFd4*rNPg27XTkqO8~@FB%aH!xrB!l{%h z?RTd1!_7srfQmMB2{LiRa5uocTDtr5BR4P|8-1DJb(h*jmo?ibsI%JSwE4vC+;c15 zZ||pjD#y|K1RivdyT4M%=V`W}bAs`hWvt$Q{umL~qyNA`a1Ul@5ZAA(Kc=D^jnu)u zpPLSOc10hssI~fG@fYl=CzcSkz9lG;h^0V5su+*wzk`t3fBIeb^md!P1Q=QN0xXG> zsq@jmOT?iafMq-MTu!V1#sYnaqICyzv_CvHaOw{B|8qufpeyaalaem!U*QUxuwZus zK`KL<9a~G2X{KL6|oSprH?r#-Va2P&* zBH?ue`dj~9lhtV)7g#V9Gxoj5r6|0jW6vdm^nB-bn`eWCg7JrVdqvM>O^ClEN$+xG zRXb=}O|nCbN!+_V7Tkb3JT{8*`~GUXI2OUH{9uV=68j(5x+u{cW#!!`yu9KYqnRqRWsY%j{u1T1bZT5Z%sL&L?i?Em=`qmFqvxjy$J(-_5H)Pw*9+O@#{a zHA4Nir(q^`W@CT8%~lA-Dg?BR0OvKu%`aIqvs~a75fcm~cO?>ij3KwPeu{cQlWQ1Z zzE+r_SD&R>E~;NB9ChGBRefRBZrlT!6tRIUH~jL5={30gXO{WO*GqW)^G$1>)4KY1 zMQ^RT9XNryu|4J7PUH%5Bx#i;EkAnMq00MebSh@Yv%Z{TUwSOID@H->i+(hc_v8QX zcxHpf!haj_g@RoO(Ym&=pW7c}IYKPZ%*(NC8Z7MAwO|p@F*26(ajZ!vE>z*ehbNe% zl(E_0F|kSx#KNv6o?7Jpf?{4kU=hk(K9}h?&XUIm0yR-LUib!H^^xv9uz8Su4_^xT z!gj-)<%fZ9uXtC}|B-z6CCb5(8F0FMwP@A=aZ{in?=&F7avQpylz|;YKW5~&Eu_{r zkY5a+#G~?;jksd&Kdm=YOjwbly2nF|PJ;2VYsD7KViPCplu}1LBzLyAwNReq z)tH5y*u?AvR2LHK7n#9~GB3c$G`-bw=YYZ=Cjnc=Mu?o}afCk|rx*$n7&8gr8?xG; zngu78iD6CYT_6iw&Yui-tV|&RcBJqFS#cS}gggmKUa*f?`3ic;|K&R)erg21^XnNB z<%1XNssRJ4pAb@f16(qQE8{p98o^QG=;*u*hkvp%lvM==*8kqhaPa&91?Cu-J(GE& zA<&c6Mez`E%DPDYP@wkX(+c?E<~xe1Gp>~SrTH`Qx=-uIMpCftXRG0iUF|>AJQKxOh4H|Z|f*Vqvz8Thh zwnQ~fuCK!Pab*23!r6h5ZE^mY?&2%PZ!%mZ?lB|x4y%h!3V$k zh~33W;gYxtxQ3sd4{{0@7pw;hgVvbdcG&SNCeTS2nfyeH!CbQyfmMS~uP&CF86hED zH|*U?95BS^Nq_TYh+;Ju`S!-EX6v5j#cAmQUipRZZQ{hA3_cSEW7s<5aXzF+cP&y|+O2T+l zSTfcxgQ=~q@g^sIYH_ibNaSFZ-D1LAAdl+cdN)Ryj^H=v(>2xmq-_E! z|L zxnyq7Y1>B5ikWx=6ImD^xv|prT5N(HU)rv6J$hpysdDGq%{NA5qr$ax<&RO45vd8zRem0|=6mdqOOtX6xqEbe=k!|!m7P|gK9$n1*dVpm~orMRn%Jr_}_yV9SH z=)G^OBXiAQ%>P0e*!M*=1~qy`V(z?AhwA|)f+0SQoB9-q_Z#WGW?)b>;!KF5`y*}q zvw+DU(Muh+tCvW_FWO$7WNV!SnpbB+NVYFRMO8+^9^{1;FU=~d{LQ@8yp{<)1$S5{9C%6GE{ z=Q;gvTTe_K18;vJN%7Z3$uP2~+}ed>9*0vHd49ZAS>1+3bPD2JpQ8SW;6g||m$!H^ z7LT^kemBd7Za8*8p!Z{bs8x>M*rEtlLOhm>9?KP%^!>KnTyJD$5`J&j#aHbx*Y`t= zOLc&gJbmo?Ke+7?-lDANHT?+__)w!E_{ep3{pZ7fJeZl2KM9-P{bZ=>dVM*D{kQ#d zs_d29++rj*_h8yaUQ!3?z*$ZG@((cYATvpHWi^^revr8OZl;e(rsC}W`x{&Fqe;%Q zq?<8@9np6&5CG)HxpmgP8FXa&rwXp%sDMJO1ru!mIl%PdlfvcS zw<@SuKw#sIXdSqDa&8V5scg-rIX2$E@O5omEei$E7>hoDA>qMV9?lJDak5y0d3grl z+3X^K{&}W>_?-z(j7?TchWe1-7VUFcV`T5w8Mzz>k%lEkJb0u5=Um~^%>?$8+ldeV zI(5u>M$fwo&-^KOPo-iu5kg?TXxBF6XL}?NU1}MLzeRNWI9J~e@;i-Gt?W&^fxH_Q zfB5@F=NXoo&z-JAXxJ6(0x7VK*gSK#LV_t?8>zl+O;z=*Hls};-`A^Fr;?18;n?;} z+SIn^O63#}vO2^bFZ54cc;EN^dYHS(1rPs(HaxCPe|@89hLUCyOB>%hc<`20?H~ob zKCxS>GuEBXrf4HBb?;C;WryhP{r!V~oP4Ej&;b-OOANiRgb@AvFXDGS3Z`haKYg4z zOMn~v3ZKjKBOnbV)=kDfnFbsEYinIv9fO6@9pJNh`;l(}`S0W;n?*z(CSN`~c?B0jo&bJoN_ykI=lLq05bft%rOiR`%DG)X3t;H%?%1O}lH>nyb(UdK zg>Ad0BqWBCkQgK+rKP(Y1Qn15De2Au>5`HdkZw_=rKP(|y1RRbfq}j7eZTM6d;j6! z2ghP&JDg5!3r@PI9 zyf|d69HzMgYH_Y{JwP*ZUHk4l5Qcfc>ZiLn1WcBPg@r0GwM1Bt;Kt$_f;&0by#_sB zKxm{qm(PAVoF-}Jte#q-g9@4`WA!yRW-5cb(eZ7Ns1t9pnj>7$J)};T3Yf&ah*?`$ zIwMDFkbNGar-ouoANc%4|2o&iW0!{O?+nqk$ox*0WH zI=z!U=;vbYVTGQNn)T&n-f?PfgjE$Xmr+}2sj)JyT zjuh&6kz_1%RI9DG8ZifJO!8_3xEB2Soi#v0dAITeFwPQ`Sr5Lv6Rp2H8*nqKT&6dc zPGbX?jN}{YdPqMAw8wBRU&P)H9un=hTrK%1L+sKf{BFb~uAu(AJo2K1RS)J5Y1Soa z@M*&Vj|6C_*b-opm}3lpz_-#Ig!C9Ze{)el)* zNU7XxP{p$RE3%@85rG;8i2=LxEcV~w<@?K8xOZDCHKM=2|EbrXlQqCZg998N|Vrm+G5Y>(H%Gs(VMl>x|FapdP(CB zIYf+`49o_RVi`(!A}6=vfHMVPZTe>>%D{SFHPjpEnk>HGi}gHQ?^h^akFplwW4_xj zF5y|kp=t0Zvq`7nCl1yvid26R7@7a5ar{&a@WC0W97zEfI2+)4(elL(q}oNdv-;UL6_vlZWysUk8@RA@!84lxJz%i2t3N=Pz)CMu38Q(OMRYr z^Z_lZ7+_q6HCXil9f#~QFnfOAjp%;K=_&ibiKqGK2rEtZ<+z3H3g-K=;w&7^vqis> z1f$X|*V7-g5E+6Qz9au;!+^ussHh3MjYKbg-_Qc~EBSIH+9Ko$qf6Oxgu0G)`8p$+ z$jfUMOnwn}a-{rYk(T~O!`>b+P6=Y_OLLYaP8i(sm}bMA3*3$Hp9#4<*=XEAvw%Ww3@F`>;7tdG#Bn zRZYBQI|o9O;2efMQcR~T$7%`dNj>ZiwtxJHnJv*`DTv!QTFi&qvB7s-_#!3s1mVM0 zttGQ5qs@QtI#v2m&MehO_Y<)KastiWl+TE77amh^y=- zG3m0s3+&NF zCN(9-SVrl*1cQ3Lz`ngSUkzdApjpU$J*;~g2GNazw~iaH{tmwC&7q#_kUVM}Xk>I+ zZVVphcaz%1dMy9p8NWrJgZjKu@`6RE=Ufbm#JC>J^4xhy`8)|}GN!^g3bjddSACIA z_#6jak6dHZA>8lfdvl#45u-jxv6sUqDG2&G85uUlX_V2~rb1O@JJRI&h&b3PZ8!GX zt}ucc)s)gnPq*`!2U&j7`2){Y*g-;nYw^Ql65OoK>2xd|uv-!}9o3&{iDH*}+mUz{ zBcEB+^nkVPNpJb7W%3>HbwK9 z^6a!t*$Vja;=xTiN;F~81vhaFw8&2gcsMAXpta+io%=eN3`dnBc(NO^t4Av)Ui_b4 zM@)lG#)#y(-0WHAU@YwsS|LnghaJoX9!js&e{Sx6^1{eXldXHOhGjdR(BKXK0gV{U zQ-jvXDP3zw7azW!Hp@tRz;IUXF*1sKbyK(x{BTl#j%PHzp8htGNNO^KH0Mjs0Zxy) z3GAAFY|AaA~HHgquAvGHzvZPB>czH>9J6=~ir}WMS@Fyq^|`_4ufy_IJ+g zd(Y!C-#XJ2StvOReNC^{-0hY5BttmPqkNVMg=P|usw$2A3@*)mn^FA36(KXHwjnPa z%6E<~jS{itI%eDswqSk#LoblZY+wT z1&XYdJ99mUvbHS=n$gii7h&oK2Le9ZH~TkfNmCFq)>8={b+Z}Z_)h(S4!UvdTIP>C-GZih8PZudACoV`H~rGj1ZV#KXBSVdH)&$S?(YYiCPxjp@_nI2R}MMq&JPjG-P zmp~Kn>ei`uF4z|C^*l`Bcc6Z$lCqMY*o`Bhv2cZha_*_lzMpesJ}APGwpBBeYuC&g z>(9B`^Lk?)A2>03t($#es6os)cu&st;w+!ix_Q57PEn5eKHm)S>CM)eE;WE5zz+&< zGdW~@jWwdc>np;nG9Qd2CeWl#&I{c$^S(0nbL>vTZKNWU^FMxf z5>cc_66rIq<6V_pU90`dm@Gjjh~Tyrr_|KcVoTDK*v}Wsi^uk!9If1jU}F>iUmt$| zm<&_c5k3qz;GoZvx*qNb!6w}^&ZjWs*c#4=I))tks3}=5AY`~EnlP_CdFN?rUbX!{ zsp306jvI;(wIZP;(dVNjr;5asf?wY6(MMQKlJ$T{Y4|PqBX1$#4tC{nWM&64kuTlp z5%noqwtnv-16t5%I>M?wfA_T-UQntGX2fnV`7d2`>h@-2efd84R)$_hevPdsbjf0j zw^P`?eL5pl#{k1XL3X^*$H4C$Zga^Wmcf*pr$;UGw>=>o@tdO;fOjX#G1ldr7GNZu z(;gdsV6#Lm0}G$aYxtE9-F^Ga@qNon&NCAe{7X3S=kQ^OY0||OV^zidPEp<6nsWK= zhCOjQ8xl`sq}rOysuM$xqkbQ=n;Cb$4W)Y-Yv)LF(w@!_rgY6Fd^3<*m(`zpUi1<_ zjN1Kc!|!$;hX2I=xFD*|Nxv#ClNof8d^>pGw{dFho*zwPn}1Cz-r~jZl`5P#u6IXg zZquR$-btf^Q?B31-XEP&nRACkH~4;r15$yDAY_N5TiAZlvU;cZw?3!~b0EOi*hO0=3cl(N1Lv=Ww?H(kDgwxLYXUCFG8L$Yy`%^}qtHKX!dM9E2fqcxO_ zra_#ldn-bAXU~-yOMumOF-K#GwO*pMU<4Cisd2s={;o6{4^l zeo=Hia-XBVz(1a4i%sC4AKOVC&)0V<{au;>GR4s1Pn?_s9?Z*HEXRNU$?m4S$}!W&GCGQBPFm-nFyvxlK5ZIQ)7_pe>6`$D=#9Hx;GTD?&ZaEF2tW;P7ISTp_omY)FLEw{Xoc=zP` z;GbT*gW&(uYY(~&XGIrf@yOACV0$g`PM-Ry^ZHPld8Q4JPZy?wp86VZsNMmyq4V6X z@!Oyq=Pv^IXZ32G`D9h&gFLbfLRJCm_xqKOl7Eg{$3ig-l%Rr{h!CGp0dqOy?e?yI z!qdv_Og&1ao&kGXX8*$E$maTUIg^Wbbmh*feb@czuUWa)SdLd#f2@M8_caMI??oH& z7X{tYc0pgdN~ix6%0r84i5eT0vd>DTZHGd`s>-`f_pXfut&an8PcMLMs zs8b!gdZHvR)2|>nkvt8-i+9_KQ2552YePG)8B;^Pu!2ZJ6YQi$JbPd!9Oaz2fP9%? zZ!fUcOSaFF3ER)LEKF72O1pxnSc(=JRSvuEnoVTtPAzPW57OBK)onw^xmt?*X2*u_ zGXqx?RfR(P1LhAEH!`2NgW_Og9=XzxR6M%03v za5CN6>I3(OMjEk&tRq~P)DOfa-2v9GvfYz6{u4L<Js4>F zkwNE}BrNa)LJ{yHcNuWNJ+B}#Z^+mGLrq4#KXbJ3O!R#-^?i>GTe5SU5BB-J6bf>a zeF`$Mm&7&#sf_WA?Zp*~h2U6$f$r5f;PN)Q_)gbucRF^VJ;khP*ncL}YaAX<{xOb^ z)A&XE3i`yNdspE#L%UtA22}ZU_QI%Np<%W*x8v+WGVm3lBSA|(HBh1yIaNRfA;gt*ismsv#B2X0)0?37YeKQCgJ* zKdxBhV#dUlK_c)j#X#e{vQE)$O1!f@L0X|*nO%jQ)NP48JXlI6RBVht85Vfi$xn-* z>k+o&TCO&~teCYYn6Pw7^R_o>xTa}#F@Vojv(^YytpV~oR}uDr9y%j=8VEff(-DFW z7Fo)`%lBCa*A~9>t~y2}aEpLqv}BCyV5yR*i_R|=56izZ4tpJT;)N$^xx!h3%JT{5 zWnGuT$ZY?_(2){eiLKY@e+HyBxNC0d zO7Pjv?R8K~gSBQm2ODUIBrJ@uaQ)mo?Tu$Proap;$Oz^kBC=(7xAsHu+HS2;!S;$( z#YXCq`D@%&259@op_@}&1H#Q7vUwRaZs|E*~MG=5kHwO zP3)E5p97@oUu?hK{%te8SCCK8yc~Y)w6-H7#&$4YtIqNB8I{O>>7H{<8*}Hdf{;>D zSZ|0~S;IbWMAtJjkKH%Xx0Pa@rK=UCV_}wLh&E_(9kRpzXY2aqGC;H2>04f;z}ZI3 z^PQ&MZY;?-TKh!ORaUtLs?1jM{DFMZraMrwxRl<#C78@{>0;jqT&mg+F)I{z%K&|nN&Imlxd)x4uQqnSs4T-Q4<>O|yPlP_JB@sTbZCA-xsS zD&Q`X!|*J9C9qP4g&qJK^izP)7B*Xt3>GN}xefVNu!>bgkCPBpz4|(Gwm?GQQ=)wL zNYnF|>6s)FSBbA22k1+7gnGHGw07nYUk9`D^|1wh+Cn>fo-B>&eb4H|E_D2#z1@S_ zqd&S#cw=>ng2ML2mOrDu0(wFuYw6C^E6*68_MP+KLkXz_QW0R~2>B03Y#$i~NZ%pu zrJIY&U!G25bwI}dAy_#DgUG{r= zdzMC9S=_K{MVV9JMa1x_`1wX6U=sT)8-_=9fq_D=)0<6Gdv&}FRv=n!OcRi`0}OI- zj$1%zSZY=v6jxk3?1LrzAIGqTzG8cY_M)5jsGz$*`6n)tGO0cViG<+_vZam=jGo$7 zoE?bnwPOL-xCW;7NcQ=T&q?bH^~}4~7fB?_x*LWaIBXi%jw47ld;k{Lp>33vCNXGL{smJ?PFhhoT$6RXP`2w#EzrWMC7y~DH zY~y9)MdM=Zx}XUas(9m+9_YOI)9Im_sJi(f2$T7O9T5fb1p9#UgXfKk(=8J$N|azO zkK@R-N-Xl)j+$a2*_WVL{v!1ypKMQw`WzchL@>i54B#(lH|yBV zWjZZ+mIvh7hxj6viK`%Y9Zce;>)j&Mf95*P&12-Fz_$cREUi&HHBUV9lw0_$T=zJq zAemtucFiFK(@Vb|0v$lDC*SSBZ?G^Zc&jI04qyI$IV1npqN#;M8txjbju18&G~TvG z97#Xif^O7OXnA9-HnK`p-t7LIwr|V3@*AjdEUW;F1HFJih)l?da^~Hi&4EgCnnbsy zaBLMz<9Ip{P*@MItE*#K8rev^M~d*%&c2a+7P4R4mwOY&f0 zbm1Lxpnv*GqP&;Gw|pWmfg%OL_JSKn4#=RZm`z`zdU781Ik_*sXsRjm#4FvbVgRcK zXAZokrFCDBYRr`eR@&M~Cg#PScBj8}P^&MNdkUXymU83;2N%K#O3;#OdHEWa%IHme zByQhmcaGgUZRGzaL9_jzlB$HZtSeh|&O(?0sq-_awm`H*yAykkQx>kU)L~hZ!Dp2~ zd&o1-VVB_c)Q-?Yw-&(Gd<%Dw*~<oU(E;uBwz!)Tgu`JxAkh@>CEdsQdwk0 zq>NIBG>nI>Q5>=vG!O$$yy@MSubo7uEQT-S-vTAGrzdi^3H*h7M5V-P9c+v+qAJKZx)haGY@J=| zcKoFB!_ej1?Nc4S3L}$J=@Xi!jb!G!yUS&k8(cYqtIxR9AO7PJa64HFmW%_MB)e&r zOU67_y)DD%9TFX3pq_Wy1|it&brQ=oXBP9Cia@nix-7N`Xz${9#X%WDKpRbdC#&fJ z7z9`{Xv%qF091*7=ocLbVF4l5ik%tBNZ|(B=3yL*;Pe3xCR>b@j_G89|mjEni~*_yacCj_V^+E18-!ee{C1oMDC=R zclpoCn~vCN+bRn$xPN?b-rbC~Z5&W&*!c#bH4loIiEA(ySCG&g|K8&*+tjbq-UFOW zNFad-Q!O%$YK)x!DBtHWkKbIgf4@iE?n|r!CK|`p5YoNkeo>#xNjf4gExTg2|J!~I zKkyUjG8M$`4XVEl5Y=COtz-)T)HpM|X)A|B<}eE=o@&YWM}X|bWe2?h*c1T#-H8D3 zs48j1Jj?pqnyx`Nbo;qxrBS#~o#y%6ROfT)q{zO%_-c`C9v2qyf|(We4y{V8!enao zGFDkjduqtHDn0QT*FZ#KSTW;fS7sx-2jqyCnL{k4Mun=6M-~8K*DB~nUbD>?LMB3e z7GG%^+V+1nOFvXjA&q>_LKQ-@xUWf5^l(?#iM$Bn<3VrdBHi2VlZz|2F+U;a|ZE zI?(BvqhY4TbhxHB1liE@M?#rjjHSn~M zdV0vuJxb506M%U|qNR}KuiA=ujZi`ftGQ66xJ@f^>-LlF2o#A1MIHEWpS2N|AfnS zBSRwf#K`37OdPXMNJa3_5klT^>?B}k!4luw<5M)h*oUHzkovQ$uKf!05tE*O&c*+H zw@JIfu$%ssXJnjC+x1Q?_3LBZ#)#0n&LIi=Ql+=<%n&!iPO+y7#R_0Mtc^LIreU*Vc>IWR3LYWp;nZ4l|uO3HV@o`@1`d6u0wP zUcb^B18Rkpd8XNa_gR5Am&3&`oeIKCogWxpOl9BePRDaR2}%uRPb>Es3_W+>G+}DT zdhWiLky%UNYw31^ycP4eVxYn&)L`xg)ZwdFb4VSq)im>$6@;m)p zWnGM!^=s)f=)GY-jDYCTzKtYM!6Fcc6I6Qd2w0uP)QfDei7iXz?TfPx;WJvVJKV!j{PaLH58n3ZqVez844 zw^#^Zq$nvCbu>z<1u@KQE&2bp6Azt8hWmQuDr{uBU=0anwc8CB1o-?O{#PYnUD zGn8d7&yy8NwmWa}xz}R3cv6hgPG_Tru7^v{?9`9trp?aB@~v5`kLJKdQF!YY5r3G( zYOw7%AhToaVNI^|vLkJs3pWZe&f_Q_X_#)A)6-+~x0lrv!cQi%DYv324JvL^F6+>R_R7Qkuxu7{64-C-w zqk4qcY7Vo^R-Mx!z09fZS!AcZPt!CiNA48rt}yF48MeIHAr%fs1tGpCb);#p1!5!U z@S~vOA@5Z|xob;YY$I+KAW$}*MZvB8iK3BP_X?$vG z)-jZVfq?u&KyOu2Pt^MYn1_dY@fBIdXIO`gK_NS|jf}@inqEQ+49G*+t00}WW(KmC z6!SFuGM8x^e?$1tZLkR*_LcNl0)a*n7n#Ie&-TsEcrYElGG2Fkkk8m3dF^WP29%7G z*WY+;V%Bo$tk#5s*Tb&X98H9A8jkGb=ZADLX&Qopkk8^WUHLh+W=E7|JF9gzhSlYs znI#pJ1Itoi4-|v|0dH_y3=oj*yST2lhrrF(3>w{K0XWOW?Ppugd{g3QH|6$=Z>sI) z(IB^SLdD5HD!tAvEPt)J9xqWn+&H{^>31~`Pa*Q~U{$C zqsXf3)SU|>SL^c2)T>r$oHQv&sq_a~QaFqX0_1{YZD;97xp-)Wt>Rd-W|3S)G(Jy| zQ^g$gcX#{oC_@BZ@sXYov$FVxh|L~*#+v|IeVC_(#|F!m;{~;tRe<8G~Nd)r6r@~=b zOh)$I!BP^(`nFd)K_kw~Z$?KzQ0qs3Lj0w{a~KVqhF!NZf0D`aK2+&;&)Jb>My&d< znBu>;fM0w>zO1l7o`zbuj`UBXhwR5HErAa^{zgjE?-|RBvY!Zf90!0}7O3byD@a9i zmq-{|uzclP)DxN^rT_fBh+|vj5{HKS*nSWI_{o^^UVrOUBp37iu1)rTKLgi}9WLCA zj2J+Z3NVisiD?VwMg|bviq%HG(eZqq1#w5B66Wg|%~{eifAwtpXIZoJ6pNaxE3cu7 zKh?u=P9scMLa==*3W$ob{U7d!-+P`kXG%(6L4|=HAJ*m*{E!CMBhM%JH|&$KV(?y` z&%F!<>@m(ZR&6!#-c&rAIdM^KRFUHboCc%}jA$H%ftW>nkENR(eox}iOnyyeC^B@{ zeEUc=F`SpE{pjT2eMFnX*Q$k4o= zPy|x_N16Zr3IZtz4Ac^tQ*bCXuf_OTpFv4p**?6hb#yf{54p{?p0Gd`cJ^%y@jCME zAW(xr3Hv9by%QxIH z5e+r+Jqu&>^P4W#5v5HTx#PET`Azc{ZsC(Jwqv^1Ldd@qe*q3e?aap?T8~tWy<`o~ zT_~e{%6lE1@5`(ztMI-AMmPM5y1E&ja2;Kz^#Uy-(~96AT$=Z&MSpLm%ub%{kO*rk zU$xPV{m|l1_gT6TaOmvi9*+jperuG`k&={!W*-`CCUdZgI2jA_zR11heyRQ~CRo#J z*mbk6Z#dD)&uunq9mK{iVOr9^;zLTOiD>J2#GLW<-rl1UxYIXy8h%vE)w_6+APPQ}mNVuQD)g!*Uu6}Q$(u>U^20r8`7~gZ zd4%hURJtF2$apE~;(-8gF6Hc}ZPk)ox7&aP|CO1?REe&+t&&lid#9zA`oXt(l>M)M zoQLJZS%?G6g8qo!`#g2~YZm-w?f@+T=DQ!afCK3Z;`VAkJ_3h!JQR&Ux24gLpbY+` z4AOUIAs#vh%!-*P5BfW@-d>{DkMFW*dfE;gwLzH1BMlto9*fSed=e?=TPTbm^(a5z zvvJThx+n3Ya8a+n6x50z7$m^UfF}QB_hcSpAkBC>$Q7f04$xL8WiZ%ve5XxlEf)8` zn0|?sj8TcTFw~d(?WgiTHw*~Q_Xu%FoPIQFuS5h;@?5{9`^$u*gG(h;y>QjW)7#4t zpanSiKXk_kW72a**NgzT013`Fag`Sw?Chl0uA@@8ASEJG( z!4KwG?_1Db1l_bjI%zD*Azo{pSo#pyUm@ciH@2rZ5|w^}?5bb|CNLWb@C=}P`oSXq zAc0|kTNpgNuF!rntZ0;LhQC1- zF7@{8?rUY@LPSblP^hB9h?#+0=FF6vkdY5wjs3q^{@?w9VWOKgp_sj-T=iIw=HHVb z3gYG7Od8R879h#%HyC8}msFV0^(+v^z0&zPi-OsRN-X#j_N_e1ySD6MKm`=*0NK~u zyxc?uiDd6v7%BNz+QZw7KYnk`pg_OXG4eu?;q19Qt-5v|5xrX37JsNu&=V`kf?J4d zHpM}#WB7=5LMMLoL{n(LJo!L@;7;vy7xnWh;aB(YqHfpKeeI=?%W475C>ZvBClPIf&o z$mYLAx$pOYxKf7EK_9L7{a+f)vR#gs{s`5**U-p@xObGjQkJO9z1Pax+!+95(qS$4 zurjq6@KlS>)lnR*1u)hZpyal}xh9)G3ZlR7*cB&AHh)kAYm&(%!s{>2y2i^N6M*)z z=SzN)KKgT;AFX$N3G*5=J2$DF6}M@>P>s!37z2w1OPlB_a&(=C3RJANSXCY?^9q(z z6NH^r^S;|xy((?3z{PfQ?fK-Ew}yaDN5;J_5Pdo5jtJqic{->5C{8nzy2Wy|)MNWi z&`dh;n5_Oz16WNTFd~^KU%20FLWKnxI8RDsM-QyM4;dY)wa)q;Z@ zR^hFe>BYtqfDR}dlF(39RdxE$G7Xc?BDdPmo|zS&Yy^bcr-)+IN4MRZTu!JKmU{Zibv_C5g+F@c zEVGOy6R)p5^#K^4&`Hk!2AiT-Um9*6Mf7>z%rKu znUw*5P$}UZZ$092ehg^iNciNdroTMgisEPbdD>vr6Vz>fB%>B2vw)P;3w}yE{Wp!&$5v9_z zP~uChpe#VKCJ#9IT%SXrW?%0sAqS`wGS!|YKz#kU76iUUQ5F6$pBc>=9HZ)b%AQ7p zaPon(3S7IfA3iuSHHXm;2bZf|9PYW<`ytm&LslE4a^JZh))php3J5PKK68z{l1Z+@ z`dxEUf7^PU)1^sT0W3XGVe(Nx>S7IVr@;;qBSw#4x(n|zhZ<~ICAMah$`8K@YkQt- zg0B>)N!iy8%H(YA1_92m`A4~1k*l?iVX!2)ESm^b&Hw&9>vzoc%YFUGW#T#G&tJqG z9aXZ#AQ!?{@T+6+Bt|lJPuS$-qy!*f66JWQp7{k}U)qSGSF_ixpe(pfr~)qY1yF8B zD>-V6{S5l$uU)$7o4=+pc)U4F0H6j1LCE%*N%A#2^Md81CDPOokP5kGDLg14C}oIC z{Kig&hG$}N-YPZL6R(O&?N9xp8Y28Yg6PSnvx^ejH+F;r#q2R;xzqUj{?lIfey)+& zaUC`wEqWht1-0u`_AOh**gu&`--LgxdW`b24D_1YBg0)ZMh~Sf4i=s@;(+ zbkSl&`LS-Pl_izks^F)YW&*xziQ~I5bOs#lN=D#=*@n3aO)NOQ@mJ|g{X7;3~KXfFkXN{w3l)aM2&N4?(?d=Gr~0>AM*Eo}tyvnArwtvvOj%WwhRa5cIzsl47P ztAz$pp)CIbwf%{D(aFQesm&pcvf)Ka)}%c3c27A(T-m*yAy2pP78Ia{mSbLlT;ymB zZ_^(ZBhpd+SX8m}U9Ud>;{UyqWIb%1=m4Ael_d#<2#{(f&qWEZw0~Cf3Mi;u^2W~c zgBMr5`^K2%%Y9PX2sQGXReTkN7DuLWCUy7qmNVe~RP^3uYmj*wqd9E^;bsyb$yo1@ z2eP0F)D?%)zvX-{bKty)tyR>3pSD(n1=$h>GJwB#G0+IFWuP@G{zQ+NvXqkGKe)XB%nNWE+A3YOJfE_9I5-H*SR z98N|n7M0eWq~6qh?Dik6&{o%<`|}#^x-rm%IIfI1njz`9^=vT~wiSpbKYl^SDvc1Q zZZiW&aqRNoNR`d7_vw{4x;k}oNmN6JxHDgkGG9d%z$@CMANTrV9jZ_w(1B88g&<(l z8NUbx{<_{?9tK?9N4(;ok0WOBluuztreWVDMU|zDxZL|$w&zNuW>N?O&LtNKaWX>N zJWvkMi#)U9(Mh9UtR`af+$^v;1N>vuz|qluwmEPLWGcI$uoF~v-<~{QjZkGZaSQ6@ zS_Qo}l&|BYYvQu-@hHF$rmy~sZ~p$b4p3#gU@Dv5*+fS4PQKBmQQ&x_?Y{Z`j1H9>_X10d!<*?Y3(Le zdnit5XGZyEe@Mtx`eAiD&qbdw^7)lqUN?b2g+EoK`m@}+6V6EW1Gf^-_=w4H`h4wA z$u67y+@53^gkd=A?MVT_UujGYg}xGMe9fYWQsoN-BDD5w)sNUX`+speeB9+?lZPC8 zx`Mb65A2-|WrC#q8KimR-_}HuNT;g+#jw>;;t!$pZV@>Iw%Dd8Z(%8bb`~GJ36CV) zMrHPXdXf8ew|~L#eBrq;WcFWcNST?;arHbN&G6bJ`ryF!k9|7_4seJq&az;m3R*ZS zq8J$VoZd;3+D=Hl!Uii%U43{V36>S6f+zv3Ab`A#53h*i@tdTmH4!G8uZqD!kL(ACVJs7tK-hOu9cs6k&v)@-uH5(BD{}z#>?mjf@HgirB5~r*6pc8 zRD!&2qSObvN)D{=jXxUVkkr^dx$u?XBsS``>s96@F?ihE1LoMIckwg$67) zU5D}pRT=(7NFN1{#TUbGy**OYz9(%GCmMkt^_}7F`udcGs%eEVfJENR)oQ6GYzbZj zB3_~I*N=cS#a96^fDJ7^1sf160%<-R%vJ?W9sji`^umJGi+eR?_#D-H%WOQ_Q85XX zY2xz;$kE(7INlDBW-wpq!4-XzKj(Hy0iFEkE6aL9cuh!D7+-J{qGA>~v*le}Z7QC~ ze=rKnYsM2Wc>B4dr~xU@uzigLbc+oQpu;2rXnk>nzu0lMFk4%@0w=-%PXuuQJ^82J z-7dxAx`Cf1*>2`)=;5q!jw>3@Gy2WWiK)5jH76arOq z0)+=XIGD%A#vaE8xPHTeY-0!li0vQq&jvAQYh(gVwvvF}fnkc*fk({Rc_1C~Z04f` z0x&g?9Y6oLTv$*b_}otqr`ie5QRKnMz#u4C-~|*Ss&V^)Jg@9F5;DT^96%W_dsHiR z#7>qU4X`(=#3m-fv%L41<@0P2o4<9>7(9CmT3b1aa$KrkT%E481;PC8ch&s?B;)9h zcdU=DX>++IP5K#Y)1Yt?z_I@qNxeXtqPaCEdH~fqkPhF2#8iIZ*tm=XUHF?pbYP}< z`>=cMs!se>oRj#e!nwIIAt4v*G=|ah4)0Wj=**yqh4+l5{oU2BH2-GUWop;6WKBeE z7p)d>n-4HLXwAO3lzMBMTP4@x%;lt3-E38F_8;!0hLgV)h_* ziET+b)bUC){>}^!F*q)DNX|A;S~71n_nxiydD6oEtrlyR`$rsU1R#!%>xln+sxW{= zNJ}QlCW@FzxaPgyVuY$lyN5?cxJ>oa0eZ8)vh*G#l|;7qWB*=sz^M;B+HEVV0HRAA zQ2|QDQxZ7aZ(K%{8iKlBPwTIjBrz-?eJ!?pT6DeS)I$~X-mw|>%R$h_oa^w3G>XRN zZv5HV6Q15~Zp!+eE3ErH?G|Uvj<8Mh9~>cISuS~|P^mx%oWlEc@I$ME|E(S)qi??7 zY1>aw%FDH-FI#NnI7wc_eg2b*Rra9c8VY7zZR--hw%gqKYv8Q3;cR6b3fo{MQ7kPNfhLtKhD|z^{ z7p4=YSaO}mnV#MJo1`FtwB$(VgBh--duuUvaxMjR^OqvW?!WXRezH^MEXVF_)hYS9 ztSa5@%+sY4^ZSC#IZ6BwGu6VtF+4!^x zy6(hPy%)3?^nG%*e2AFlX=ZZ=s!=KMjjHdzdg_JiJ_f16u&+Q)kUJB@X5V_T5d{H$ zF(A#6t#rlB>p}w3Os3i=(PqEe4*JG~$i=71-U&%p7O@VXC|F(hQ zVsTfVBEx^0xs^8jMoo)xn)tVS7_RM`zI?%9{0l-=1iY4pUr|ZaoE<$u`0QPNnD~uiUT!#@=M!%90H5rF#jAQTYKH>i4 zXV+vDaLq67W{Z=S2-?E3HO52DtVx@$zWvaOOhS5}LS{jE7LM+;F=smozVZ%ZBQ( znojn8D8e58sn{T;CFf3+Ef#oX3EKD}+z;mj^>X5ub%aqWgfzCtGTn@fj~g~WTrvsP z`13czou9n_rDG`Pe#qkR-!Y!S|1-vuh@Tdwn$O7f-LtGey5~<-Wqh`H+!{Lt^{KVk zhFW@%RRx$JI`K(Fidz}cebi$eKL@Dehq9(v7c-sFttPjM)o5~b+JLqG=hSiopWmwm z@)MrKXD6V2^>korV@0^&SRgTG%9AJuVy1H#k*3C;C39Z${|(FXx70N`wAD; zc0ruDR2i)NKv;!0;a>(BDnMf`USBHG4D~Kim)R@AEjIFfrn5vmNBVt$mq^h4<8t4< znP}xJ82%|#%D>B!AWF8J4*w^n`t`KfP*2h}NRu&JJbHVkCVi94WvsXLyG#OISF7F& zd73H03#N8rwo$P8DcP8gDsMhPIM7HhH<2cAKVoZ-r=vVNdf=*<1b?XcA~9b>5FTM2 z%5LE4vi^~2Lw@Eirwkyb%t(h{ET2QQS8j-96#=%OQ4KEtaxF;#u2RMq5C-Ox*7hfhP0CS)9Ey<@C5X@qP$ptY7 z4#|0(8RjxN;`8bR`G#fiS@o^vd|YcDSOK_zqh8g@Xb!L^ogJ82w_LX}*ePaI9(TT+ zU>anste#{azQ6<;(j~MaT;K<7KKP~8alXL!eWWs0!4DhOU~)o(A1vHXG9#`M^MxyjRvlAws|w zCN3qa$q>_k?oZ}Xp?A|u^jwY=&g3=U8NPucEAG?(9bW4>XYpAe-^vRL4%0#%TunKUm`x+kArU7E`KId`!v2C%B zf!I`+P(e%mumTu!|tT^cQBGJ$-=~@2-_PfHD z15Y=1Ls+yD%(1Z{pNvS&rb}rVn%YiY@YP^7Uy0tgLRAD;+&=H?pUI~WG{8G-|7k8{ zO8EiPgeZ`d3<0&amAq2=~}11A$L5X8DR&n1Joer(u{ngzLVB`iRd2i3NyD zTP+7+StlP0r;u*3u}z-)DX-;pjqYNg`T7h_%%E zBodHvzgtiab(3uOBotQwG4Uo3j9xlSVl z`{<2Jbb(b>(5;vS8Op;$b-82}GWUTSVte3M&+j<=>GKIEA2#+{+nFQvrOWN2%Q^KA ziyiG>bnWprVFWkyO<%bb9P5Z>-Zq@t8Q+Am#=uBBW6f(chI<56u9p5@D?9-*P-;%; zOcg*NQkuCSjPg%502lQcN*2algS>2^^{0n6#SV@8#t--V3%Mlu{rBB$; z_HzPD83?H#61-;F5gz3W2qz`wj_)#RKNZ-oxcuBR*v`{|If$z5q>(_EB}WW<6yE{H(YGb+irX!(Dm z(G88l@QVPh>4Gq%CmR8Pc^yX-onB~}12|@l!Nd!H4j=I7MFfO-tW({Ih0e;Y+=q_F zvoHPlCJld$&Az2%4e(KK6ps?GW*rt3SVqumPJc1lpe-xuC3fE=Q-QNqDksYurimsc zcPK*bjd=eum_OB5JYGAN$NBsz<0M9?8lc`18(I;m{qfzCZKSrQ0Qy=6VQlC_6H)x6T1F#i-<91_*4?m!aJiaeQ@~|S z!540s!iMcNTau^ocfIBer`SozcEEvTruhRD;}-JAla zvzNI+{iPEoA)elaH`2)6?DKFny)D-7UjOb`a9x0T6!Z1hpQ-9uiTMiNunKJj!@khH z&~QgRXAQA&QJtZI%CFFGg~)gVQv#?OE$k2!ImTKXSM;bs02=y^XRh0RHiGW9(tg1 zb!S;!x73G&d2~Zuu_a;hvDy8W!z$Pr(TVbrcE;V+t%mM`=JA5igVvc9)733TvP03A z{0pAXSAA~b545C~KbTJ0PBrOm|MK^De4HSNY(SCaV~?Fg?ccZt{rnA{KKgIu_)Nuhc9n`pu2q?tIaS_zV zo*EoUc1RA4Zkr_1-wg?Hl-|tK2@zE24Tuj|D8K_rG0f}3OU@zo7$RV zFYSjpnMI$CPtI1apO7%hXC7y*a5w4lv{QkI5OR`LHb!*XD)^lOvJzI8Qb)Iml+cxW zcdG85dqPZ%bnzm2e0V}1zag0_&VnErmj%VuySCyp(D$D5%xLl}bN%pfX-k@P;OyRS@^ z8x3B$s_p4}K?oV8-Xp1Po3rR&4%R5=hwF8K?Q2Sr{?z*u2v$r~uj*GK1YWTpknlml za@zM>lbhR~&F^2py-H;~SPRVs{FIYJ2txLamTP$JZ$D^HmYr5C7T+MzzP^@PSCG=; zTzg-~;qO|9NUi1aboH?m8`OA?)fm6U0R1hT7t(U1I4x;Hy;c?ofaB>X0=h~n%(Ys8 zbw1wvEk}!(@^I=h;}SNSC6Ol)qvBQG=oeEYo~_Q*d2_~anBrcp0QE$DxV*sB*hzzW z$k%oo01G6V5redb2o4tuXV@c@UkkJ+Uq3_8 zBS5nKUUe>ao z{Mqh3Ta|v}f%`4lJwi0Ny>MRYBKAV$SmR`l%cy$1{FWJbtS{SlV=7?1p6Q=u0J3$b zN6+F*^b;gQwaj{`zTMVrQcAk1JGq}qeo-VXG<}S172Wkt_&!3!RoM4V0j|#X5~tPt zWyslt_4~si->6bVLhFwy8HQtcM_Fd(-(aK?_^kHS$9mnpo(hG|oi5;J48O&ieveJvegXvt zkvN_f#&d!6>aO^+Hqn3!wv=vU8&AOxVzksxe`dxd6c?6ZYa^lxKGZ)~$cpLcTl5;^$0$9?6lskW2*L#Ikn z->1$_EWOL#(i?xQIU9wIW(wuRRd7*2owV$d5gOk*wY7s=|8-Nuq^!YWsV%CbTcF*X zQ~uNox9Gmze~)1~$ElT)yCXhN9^ldH4z@*WmOLxXSTEI==+tP8bG9vTK?b5Rvx{9> zZ%hIsO7(Z2LJ8#N&MRz_&!0czm#GbGG&D6g**{nSZ`+ls;*GKih}_|7YYQ|ME3?}7 z)$ahql+O~}aRt1uP44#qd1U-~QnKf$OnT6_jJD#PA!#<*Ql)uEaocLE@DZQ12=f8@ zE|Gm83+JKz-v49e=2}+td9wtu9tg>;?8CbMVqMX@Zb-XbCP0wt6NIR7vy&hk>7#HP z{mh5C@ve*O-;q?f~z+gSNAkSVsJPV>}vPte~s~5)W7}v zb$r^c7KU#%_ZJQ;9+&&>JHzqRwoh0Hp*Z>L{)V6VQGL8j*So{LL@AKdYBT1v#4wD| z0SAuJ-;y$`6!4AhyT0!vrW4vj2gV(cibIvU0xzjde4>0lzh*)*q+L%gIw9k9Ktbn% z>=MKSknQo8jb20zwzr$xMcmS;*ccSxUDPWNrN|y00y}%O9%5@&*fAko&(()m;>?PL z+(ohjKq$vB4ku+_468T*#p(mF_Z$p~ZEV>p~6z>grN~a!9|GeoMcRWwCn( zTOn{#lkq0oa#P^jPP>YLNtcrm<94nGW6=K9Q@YZQJzW(+sLO47mgK(0yU&CBLVC{f zvKen98ASx!+^^MY$^`9d6*e!dpU``cN=f`pjdAa~?zb0_p)(TY^E67;bs-67lD_QS zF-dO-`+&yq!OxxD0xA;Fhvb5hM=KgR`_`P{zub2EWAkd4qV%C0k&yIX!z};b824nY z9q)h6xMFfd`;KJZ2a}e3x3j_sIMWD?YfJM(rX6*;vA5xcL1hX(G|GzJN>i+!%?t$V zo#bK!rbuq41|v+b5+c-ieWUAlu4r3CntlljUddjH2esyOQBBvT9xo4;K=3b71}{-x z^Zwr9{f>P;Yv8a3R$? z1D0=}`I@~MGF$B!k>m*~INC0c4bOxFRd|h?mKAkp6Le*4L<*-W^g9Uw>Y+G5>0)-j zFlgWT6}*sL+BM_-e5VYUI%B}YM@D3ecz)5fq#oFEP2O9!j9CW@vS(itR8p zr)p6r;TM?)7h=;T0#iilZe88*Vg4^3!SR*W@ArChWTdf1d&`&AeQYd#nf3ySn1QB) zTr+Hz+-6Yai=|@}QUe0kU%AC!`i~<3{Sf44XA7h8INVa zY91t!mVVg=_m3cxvbFLwoP#2@E3Y=g4Qb=S`(}#1sJO#cwZ{hq6nPp6CA~HswNWm1 z`Vs-L4d^TZ1s&Wd0Reht%yaRi@5l+&C#R5+U$%$olJ9WLmmQ5hs}TRgrV z&iQsPp$%i9jd#E{5FI1U5eV)4I*C_^E<0Xi0dz^T9A&70?j|^z0FR9I#{$i{Rkl=d z{nbs`a;8C{YQ3!3t(4eEy5+aeH7&AOn3-w|JIaf7`}r*$BK&;p1Gfu}UpkH7kj0XP zMQlex`@&z${s5A-zbELQz0fGfC{6;p5%1+|r7)OR9c2!Au#tj|M3n?3f^=m(J}=ya zkNZX3UggZpK`BpC4&E%6TitJgY(js9X0_%dZaxT^F_QM)svc;pUx}-+83E*HW|X3& z%5pF|W1uGpk;+e_-i-z1d=S}oDsiz8)KAUriGT02PyXae>sNJ9t;8MJ`^>p%mvz%0 zwcZKSKX^~MKL{FX%Z}#PS6UT(z@eg0c0?}@sY!7GW1wi zYUb%Em@@M>|KU$>PwRMgP($oKt;4aXU7pC>kS>Xpf=z3`G|2qfo*L|}Ll2jHUiSw^ zFJPeMw6&#^Ou=k7HHyZ;7iH<)G z@%JC>bW;?PxEYK&Ik%X_@Q_rXh2tZa%?d=2XA|d}o+ZeEYJG9M`yPV$;hIthmTN4c zbdguvWs%)(hmW18p-GpByKUPkeW?+u-RtA!h}BzJWl+Ra+2tU$zy*ao(Vx&6s=I7c z6Y|P%%>c4vy+EdS(|Q%;!XgEN!OZq=1axyl*a!zjn2%{S&=L0U%==_?3j!fxcnuG# zpIM>V&K$q1=M8QNn3}Q)ilRlgzaX?V9tiO5Ac-H=-W!(p^*#NU_04*gP{swljgG(k z3T-gzw2*kci9MHSo~{^F$k1@y9Q50$<;iz56(^Q5iBeJ@1sd{p(5ezK^5DjJwqyib zAY>*>c==O4L*GbJ>~k*bpjfZL<<>UH9UFZH<9=YKF zQ54_{fN>$V4j075{_bV1w_R?;{x=2~pnyueofOrWd3|Sb;X&Yr_!2{IB3M2U$s+F= zba^mQy<}Qnpwuy&`>GMK*xri$5exGm#?sz|zlcs9ORp1%u$F^g&}(w}4>@d^k!^9c z30KTp%C7#Bq3sl5|MCF$OAk%b1Z?VmKw3%G-@XWNfQC5Q4p1{Ww=+K8v-Zud*5EhD zw1^4#qrcUas>@x`pn#R7;(s!aUdHNGF*8Nm;P}(Inh^F^!Ft&Ld zV_9I|05)?*xb`av{x@$lW}nEAXl2(NsNO5{?Zf@ZxZuxCp3vU5zA$Cxk=?0XlS1)g z?o7Q>ZsI#(6#{VE{`Y!4GUquafd)<1fBeLZq5UBFUtL|k_EQ%Ljx^9oYJY!J8t*7> z*v=)aF%b4gq6aJ@E2i2!{%qtd6ws~I+n3mW(^F~WXBrnJn2TE8C*)7faXa5zY^Xq; zuVR|5IyMBWDK3_b;6eM3>>%@+GY^IGF(#ucM?~TmKHq`9QZzne*Ja+IeR;MJT#|Hj zsY|#P)0dIlc)+t5I99LD_QTNrg|d)jESKufqtRD=11q!}wp6BAZrX~mz0scyHpU=d z{Cj7DxV?{{kO&t86x6J{KVFB|R6H^~2oH;3vls8^*8c2G4liA|GVd6)`$s-2*w~?? z@lDo)?WtLSY3yXf5UQgSV=Gy|fdFJgAA z?4Z@*iolUQkR}j4e%}&yGh=&WnQizWC7TW z6YjR2%ahWU(mdUk9sWU2Z0YIpkMnFclM8$+CKkl~9T$o~>>2exC{pRZgo8-&@$0MW z%f_Fd<7z|tjZ9IeepGe@qs?7UWaOxO{Gm$QcR6_>Q>}LTR8vX|#Z^R#IDAbwwyk48 z;@tI`=KQLR(ae!s>VkyI0|>H%?iUb~=^sCv&qSze|r4uX;-|*!dlIuuzrt78<#Gvh{kIuoQaSOO>dkGAN;v$Ry~` z;}lKGQuC>@iL_Q}K_E>{eyLUkp~(hR`QXiHrc1P|@UihkQ+m7Lt@EAVFE>}Y>8yny zG}1c9cSMx-iiH_)UHb>j(MklohDyuc&;Rx^LJ;sI1DN3K=BXj~bZl6ErX{(l@%y;jKAH59XI%YBG0t0+r5bZskCjny-T-@jT=kuBt7dis#nHK$C zd_lfu#C#LwrFPS1E#9Td%1p9HcbWU;_di;1e2$#9XU`&?;D4WYR+{@L-)Vom>7;hV zIr{>zX&*r3TJh`a69r_-q6(i7&B+rA3Wo+y;L@RA2nR;gqZkkgD! zt}Yt!+*dDUlJKVIrZPen>>`C0Dt!)J(Pv(EI^zP*R>>ywLi9cFWc%0OGxHNs59a9L zeBCe_OX}C!$tH_p^q=nJ`qS(vOH84jZe@DXmR~$a2xEj)RG@P6Xi7Bp6x47^Lc_u) zr#l5H;Oy*1`s$Ypk5BqLBL}+tOCDtH=6oJZ%^Yuv6@YtYw+I}u5;w#uG}6BoL5ZM> z6&TAR;%9CyB$XVs<;1b69McHDdh71{Q&Hkt2fFkbPvRx6^H8KQUdv5SlbOU8FK&}U zabi-AnLlW(akz1TH+=V={1i+CQn&3;%F* zvhe%AY5I8hT56`eA1ZofxS>(WKSincn*$`4E;q=l<6aj_(1nF;h4R}xgZsOh&Pk{H zTmiQ4d{y4%My@Dd-E@fev)9A^JGlM}B`&fl`PSjCz=@{7M>rgsrj5ouenZl?i)Lir z^_2YiqV4tMFoLWx09G)tP4t*a2(4G}`t7@1#iej)^XiUSsp9#r=()Tm-6O|DVWgFa z+}w=w5I5bt8#aBPDd@Qx5>-|60zv=44Ih6Cy204HJ)U8fwwoi6&!9zuLKNE(p+)=u z8@~Z;i>|8edkB$_Mkn{5z-3wA+d1_N47>2TvHp@C2n+(j;QKXSA{b z&=)OsNcVc6m0I(%=zkSFohGaCl%a2cKmr91-9KW&N=KRzN0Bt(S;AmuoNUo)BmZCQ z)e&L$nzs74A(1mJ#%+{Ss6>f%PUsI-O_(9nKlhFa0=idpT4|W!Yg!f?Ed8H+!6{?* z4tk+p`m=OQ$7k(yObudV@1N25c3OCX{`{}4bVNCaCbWb2SzaDV;PpnC@97}VpO_Tz z+jv^~W%WzXp7iB?HtGoQ%6EY;621@@dH+!Nz>fQgL#%t#SmSC64WDO}iJeFlBz6=n zQ5HgOH-&Go)!yVr3NSYbe9UWR6<9A-tf8jAkG((9e{yh8O(Sf0pLAfibVf)*vWvWi z5rg_UGHw%TCKtWEG4;79|L-Q{AN7k-KPkE^5F^o&fT@d1J69O-v_aKEye!&fF-q=) z0}XIy0MP;HRka*83J?Kp`bk|R zS>WV(?b?65@MH>d&+ z8;~#OzVH1oAF⪼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 literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_entity_settings.png b/doc/qxentityeditor/resource/qxee_entity_settings.png new file mode 100644 index 0000000000000000000000000000000000000000..91f14659d14c3fe136117dcf31b1cb9f67d3cba8 GIT binary patch literal 56604 zcmb5W1yqz>xHc?}f*>IsI&`OW2?I!Xmo$D*yNq#iIug9z0ff1=M`-02TJ&0h%H< zCi2cAuERC*ABwxCy!3;L5vpzE3v}C;DlZ>AsE)7h5(bSCF@BM12Ekw7m_)5iyti)EhgA0)h5Ta5hBKm1`UMEMgB zUmWM?+WGU}0ea$tJ>UH( z{vtKcQN6#jE{!`C9)`S(!-7NzkXIZIk2=%+C)~m#Ia^*aym_>IA9-0I#GgDwfQ3GY3{GP!8XOIl|#^XssMe=@xX3Kp7Wyh$V>o{r=Q6NSD(|J7mh@( z(FLWm=(@Tp=SmGV;O*VzM&zNE$zCBTr6A>>hR(?eh*1W$IJYO0%ZfTnzw2@W7?YH7 z%y~_rUEz_T)L`fBemp=di_|BSjpw_|E@1{wm|_`4!fzO~!0{mSAh`N$g_{Tvl` z$}#H|x;FtxxOh-xY*R44RIs|qmH{>he@~Mh>G1`y)3klJD&T)Cbzl8NCozBpKFs`n zWRO$l11h36LJ9GNVb#v4mgdjR2(>Ax%pe~axA*-?Tw>!0SY8%`CH~ODgmJi;id@%&l!p(yC)U z9Ar(?8I+Q@o&yv~F=6C3q(!x|;-b{TZXZZ_NmpH61!=N(zOMlj$=C3vV$n^zZZl&o zth}*Fjn918IohPva)D8T>MMtZAwm+okLl=M`n@e9HM=}URn8RAQ7cd;8~wO5ht_u( zD#LU_#@&PFcez^YdYCsSf`yG4QuOYS`w}x06!UwHZR2-u63z*kF!`vfaKN?Dvd<>9 zcycNfoMQO{l_teoh5mt<=MLF7WsQBH`U=q-D=XmR^uS?$A)&C^Q3=Xgn_*2TGY@k8 z^3wg5x*6J*@Te1b?QDuqqD=d_o-QW)OU!cI#X))H_meOAD40D0%ThN?o$t^BPi<#@ z568Yw2tBhF{CI5h5;i}8B>J5gp<(u06A^^%19jPTlI zf!y)-c>Fl8vy=95W#;#G(mfI^ZZ+sGEi3Uc!hA3oJS`+lIqK@OTR$_lun<|Glmg2* zZ@b!SO%?Uxgp?WNyE7+;qrc~UkS*wvMU#L?FZzYfr!F{oaKJs1A)FtxeSL8(17!~Q z)1X|bn{tz3vdgU(>_%cZbRmU24g()q@227!xfuc(mwh_%FCzV#-csMmPI^ZRs^1wb zP@V%1L0j8cp6w@?&X?y5+?2*x@9-=Kn!)ShQ)IQW?00s@k>@{|xFEub>}$=RL7-jf z{qP$hZa;#N?1_&_Y+QY3XHmc4cTV-3C>av|6o|@fx$&vYC(lE%#W3H#Jx4tsq3FgS zHO^OxOE|=Yxo-^pxY*2&U264OOVTs_v2we+Qt}1_Le^nhqf0mNW+t#4^=AT;8n3$A zcOa@1`3LYCJ(`O{1n!h|ykHydR9|e6h=xt~)1uD1Q%WHkZyGSk@PkXq@BIfkMd!fp z+|TlW=7V+Qsk{vMS#5saHW?vZv{}8V`v{@UYo#dC4}7N}wbqXO$aYxvcE`uUyLm93<=b$o zKo|}VNs*7K&*i2s3n!_a)SEYGsJ{H&EGrnCom*m#`kk05r0l&PCu3HpZkM+3!=~CP zyTs++9@L8qox)sr+|N>Y3Xf^4#r8q$)N|oJOt)w=LxHMXA9+%1g_*U&n@#|v|<>93f+Fi8fVq4y;gO!b3tI?3ZXU@O-ff_?Pl{pyz(vd=h;(uX>> zY~FH)THk@btn);P#Q=eG&untCBwd69j@JTvvLwZ#Q?eYoMwloM_ZRNahC44Gw=B=c z&Um%I|CTKg9QbZs(EH69r_RQOnM+@w*il%TU=*$&ouRl`mL@+i@x(Dl5VNXP@`a!?Gy zs`6Vl`^}-T*TfvCJtS)WWf6;W_D&<*@^wwaHhbm_Y4@F?2iUq`&^s?dyBJf5<`SHk)i2^omhYf)7nVu21?OdO0 zzz&O6%kb87t8uvb~k?pe%yYSCsMbM?L~Bn*hH2JO5bulZQpd=1%`AMp$7 zJzHK)_z{LBG*^$qbha;`Ipd5=BO1J?clK**gw}#Frk{}sH*33VtQzmc|D17IRK#So zeBk|?AE=0LkLZ>{Z4qDV6gcDHK!{O$g4JDF^4Fw; z0e&o`7K@C1vLxC-2WS~vq9GysncZ+K)1HhkfuYB3nce{_&ffA3ziVff89It&wg{o8 zw3iP|0GV4LJB>z~K0V2h731Xf^a_t1rA8_)f-MDJy9EDS6;%_n)BTl7rP(SP(xia! z$IfEU%Z;zwc{1m!(fFJ@sef!w5>U~|Ezj4J3A>{DBM`9tS1w#5Vpr^#p1Y{3!gr{k zw^y50zes*P1oQZ!iuMM0Szz>h!A`6FB}6cO&TwM%E45J7shd4N1kMvw-oMX2S-#_> zQ?$f-++I{vDj)9r(WS#b&JZ1N%;pS~txqjf2p!#3DJn0C=`1j{-~J90vAkJANI~Ts zzH^}71uLG<4{cL3>w01QzIe%F8O|d!-+!}oyuo?WRxg@(8B6^m7hnFZogBGD_eXE9 z%fPO12D9$g<9(^Sw?K!MOiGuZOXV)B?>n{}JJlMvx8JL6d^F5t_sMR>Kl2|MGJCIX zsjcs`6d`2Oj8%P;#g$)=05>ToUM-$VTD0mz>_Q;5O8paKgP+U;z%Xkn?Lg|0(mHN? zzKge8A4w@(nj+**gOU+aiy5a$7Z=f*F;{CHwmsDk9~%wAtc3$ttaR;TLG!q{&xAlF z^ZO+(E!aa>0Y^l*?T46`w>K-UmtLJ++ozrxx0QE4$;rzi9Y*xA#!o4Ky{lY~r3A%z ztlj)xBO-CVmc9aSoPXGN0lV@SvelRpule3q_pw}`$EpgZJRhZnABFWtPprJ*s)0w& zCCRS4uxc^@sX9as-!~TF>eQ>06tuP%ljvA){FK9>P?QnGE{IdvV1VjZqP^R{bW5!B zh{1#faeC-CsHvpDBl<_t$w}Ew>+vFW%O4jQ*m4Bz50+|FZHSaLyOk?$R@{ELY~5P4 z_EojTY7E4}FYm-=gN8NPb^0VYn#YrJxSBWZfSXPp=J#w*@3+So4Y9ey;Z0jFmnab3 zOWs=~J}94|E8TazM#WY*`{I|u1PzPFRlk4io3}fIuA!eCs(%NDTknj413a;4TCM^a z#!$(t+eSH};%Kc!wb)O%i^*tyJUI0~2i(?yBS%TZK9zE|<4qREF6%=hT^yLheb(DF z25xL<`h+Cbd?*9nC?{-(D;+{Zp|`OOqZuO@$Fqabu(fmQY8rxPi^otU8IEK%V^(MM z%)ye6b`6_4Eo7ql9Vj+^G1T_@Zm|3gw}R?Ir|n-exMR|Stkn{M^zMO0{;v41@k%dN zZ#oLj^(nO`EMVDp!Xx|w0A`nXAO*<+4_wLYXPsIgMeSY-&ZJ8-24Cj^#!%ZDp7LF- zBp>V0E3AcsbcmAcOOdp%nIoH3Yin<*Mi<`SfzD*OOJ(y-+XmeDe)DHHnWsGF%IkjV zD(|+vGzJ!WFK7(oVIgdFf>i5r;2#g2a3yj{Mm!Ucsb%?62sQ)12diCCdq{eHwwa z*tf}2t2HZrn3ZXg1rd>_CL2k=eg(8SlS@{f^XcDxzV-3Gu~m08-Li@lGwr*Tj5zYf zZcP0V-Za|?!;zbH+wHgBvX*RJd%oyDIr|f$`(u<`C3uZaGG)^9+pN5}|LnV8Xnt>3 z`8&LqBAd<<(1g~CWz2ubTDHUtxY@+x=LUE$ZWQ`~U5yViBjsW(#j?leDZIck-{Du8 zAr(cN!O+=?Hf%W5Y`TSO#s!@*7oyl&qw9$rGQi8FOW(~*uM#OSni_)d$>$lo=Q=2f z%APA+t)sL9i?lUmAbr(H&!}>EoTg9NWWW2ePwHt8SKe8}!q}Bw;K$p*8@=fo2`zLu zos3LL#c1cLp>q39%~t2>Cuh6eum?hp*)aiqQNBzN51iIx1MLCxE1p5uwnIjOMfj_m zIqJ%_caRX6&`NaEw)5IM_g}+Gu9GIDzd_Bo2DcM9QOBRdjM*xrZlEjIL?b6>Iqz0b zGDN0BO=54weOm%!+uxb~_#xAGu&317QxSM~$IZ?C{<{&DYfB&*wVI#aXeK%Y;#3-T zou`7>V&;^SzhNY3h&9WpC*aVr&Iccm{G8DYcWSX!v1@Vcad`tQ-R&*t3v0S$Z?~dO zE;B8iXih2X*{MzW6~R{7o1|Axov<$X!RlQPA!&sAv$&z#7zniFD_ULZdXH=ajd&zA z{?g-gA@MSugA}6O1JS+Rj8q03o3)7G9(}PuOx;XDtjLW`Ixd&jLzJxR&s%@K!{eNJ zGZ1VbBzR=5IdDx?8GsL`Lp!v~*1&X5xxvi#(NRk{hARxX((UQN+a8K)KZ_HWY`&Wb zydmv4Yv14I*_40r6!R z@_wmws4Fnc2n-SE6McHTIDIu(6B~^jmlM~wA~Ax?ANSiik!!I1yoF&)Pf!_j#A zyIJ3VPPS>|Q>iP$`uOc_O>@)RX~nXZ!O-L6cL%wtUQzuLu%jQtGQMflE*K@H_8RPi z$r7izBgADFHi9JkzKJP`Ig2}8@z`L`r~JwJt-Ta5(%f%G%jjF%NfLF%+knpD&_UE2 zgGa2BD?j&8mRDMFXm^&%C$0dQBEINcopo51J9FZU?Ppmsk-lP67`tHg&Ul<7+bKv@ zw&{y$sI)P#;xQMk7Vx#tn#oERym2vjrqgR?7Sv70tNucEL*B2#BffmK5=-LyI8mXF z(nV1f3!ZZ4c#UO8p zA9asr@Jx)UZ;8VoR+#Wp=MP2p2hcFA%ItQo9>Ii-3#vIv9+;~beowX!wdmeL(2S%O z)p8D@wZsJ*cE>Tbc&stzvv=+$-2q&{3Q|$^z&q|`p_(V1f!0qt_sM?nRl?{$1+EYf z2g>@Y#9&P~u^(ThU>zK#cL}j>Jf<_NEMkLQRF+igX;Y|=AsMy zDOq{uXT+T27OX3}TByyf?d6OXBJJfi9ZFICKIUiBJ?^%z_CtuU8`bL;Kmo(1P5S7M z_0~%29s20|NDxh7&)_=bVw95Qri@oqE0tU`tEdDpx92NaIfl?KaH-YlaP-$Jv%zd9 z8A(!bay9~f_~j621A!XQQq&Kty%tSSmUzNyG{tq670t$_&)#brnJytdE`7?GFj+}# zZQY`rm{JbYSn}>1&(;^$CRyVfym8y!;%VC)R+2X|Sevyq-wdWE0EWGKRe8mGy}^fX zA+yuRKs8c&V(;jP3;(n}mB&dd0cxfiTsmKHbbkHcZdv;=}Sik)6;!|O&v8`)4&C93r zWiS1xVzRvAXeg3qs+I6%y)IAQ_}zKAtX&u2c*HOUi_Cdjc^M3j5`iUss2Z1>6gTOE zu31bZW|DWF!RD@hUKzA5df$fcuI%{W++FmVPg`)t>ScxXgCXpFDk+g{#f*2`$9D_Y zys8-@L?Yj6t^4wQCJe}r zhdagR2nZ5s#^JKs>t0)R7L%9@>I-M)TMZDWyC(_>3SymdoM#4=2KsK4@7X|A$JP%0 zYD(p!o=0Pnj!qLY_c!?se1fI(I9hR+*Z8tX-1m&`xySh4r$t&Y?!UaZ0tU=C)&Cn` z)XP77_?Mrkpzt5QmpoE`=*w4@s311X-Quoa8TYJWUN}XOeAhDx0nD{OrJEud!#{L7 zGw7Tvf$+Xs%}>xke+W)ei~lR-{v6rT`-_v`2NmJ@eT^sH4A-{wljb4$wJ*KVmjB9= zu|Llt)^bAy5xh@=J)2F-!?bauA@C>xsCFHO)wXq#742iLOYUoHedW{YX;E=?HWNdA zELEW9_lY7iPFZ15wG&FEeg?D%#9HWXa8VvPy00%|$a;NI%7Y~Uc5+bFmp`d@p0RuF zWkQqp#?x{0&~q)Jil@qBKnbG{PD!gamI*Wp4fIa7t&Y2-FPp_~ky%jn+$C2qh$q6y z;MxHb*=!Awf(Ce`bAi*7A8B(x?L9@`%wpHoJ-`4|dcf@*ob>}j$Ve|B6|6P&0%ZlJ zm2^+6O>R-El@A;BH`1*^6n6Pis_vsKEwG?$&* zc?~38!um^}3d+E(>C00rIT+8B?0kHMd5fJQ^h4Q68+P5UJje~i+{kmGz15j2^-^DM zdw#Z>E_Q~C($_1S`!_e4!zSqN`XPotgo5YOHM#>cv+~w!Kl$I z(wQM2mEwi=)M7Tjb)?ALy*W=m475-Nuf=tVv~7p%CWEJ~e5%R#^~lz0pe@Biys@T@ z*N)b!VS|o+{&dGZ-u0Y?hCg~d9aVmMekzxATWpD~)88aP_UyQoy1sroQE59TYfhELX$@qp;Qbt@}Y-3(XZ`z|aL~Uz!TExY86uUOr3e~sIsGkM%v znopUs`5NBwJu9zbqgw5$+lDA8dt_&izpoqT+M;9F&{XKVi( z`3-SUK+hw~c>cIu_IPXR4X3Aq6cXusdiQgWKUw}XaJ(yK9WAhddU3MA#Ia}zEh9mB z>&M99qP`VH>~my?$8i+ui?Ur7H438WOyeH3J(cS`DZ5Tli0o;YIV8KsnI1M#!@tnu zrmfppM1;IWpd#?UihIu`l+(}zT$am1!;5-sb1kC~dM&1gq_nFtVoOh@*~9V9uvc+?(HufR1M8C%)h}f$H$WJA z=yslj-9fsw*a1AE%QLg2jW(cNHSpbo$GPT~9jWj{ZSv4J37=-YDA~p*G{aG=EAz}Y z_0Nc)Y2l4UDM`5{4R20xxO~{g%8a~&e>hL2Jm5&8>|%>1p)Kh-aTX@1XLe@Qf-aP- z*C{$C;3Ou=yK*SPTBEH8!Y$@|2TUCLAQM5;px)d43M^JZL$)Q)a~#&+4t#}PIr=8T zUB@~~nLKC&)(>4|YY%U95C!;2C4i?EkcXKi5&|7pP&?f8itv`k+96}lr}m8$))p07 z$sEhrsa^IRLNf}%q)$Qlof*KJ=?@@DjS&UunG`^x&U9X}Lacp$IXgy?gQ;N4L^XLL$#`)0UT=|HvXCuB4TX-ugW0V z7tzUAzXt&OqrP4Dif7?qm(?FN~Qo*wBV92^8$ z^YF%SdbKr}L_C@P$Vv1FWu+=2D~y+cVa468Ad1ETasy_S({3cDuGTd}M6nB2H9VZ@r+U0AJQVT7ftQ~dedYVkt3wKvBzfMVJqazSeUm}w$_=p*&siLYO2mVP{2r}PY2 zLh=Ku$e}Eka(BmQ^X2~r1QQX&%l9A*D6eN4C!NBO(} zXd$&l8!9&y9XohgkMcM`cu&5y#3g^aHIH*eKRh{EkvObvqTDF@$UYt<+Fdi$Hg;4F z4$s-JUQz-kEOcKMkxu74?y+PwKrZ+3K{~B;hE$h(lKG3?$NAOh_rhhIoU0ZZW8pA$ z91UR#K?>5)Xk^yBeJx;C=!(LfQUH0LVrz`PGsEHXDB?K@VVbVidX+@8BOuRljpIY5 zEF?C3sz3m=2C^^!Z0)fpti537O?Iv-_xgwI{(Vn&bFw$)6H2u2>|Np!r;|pG9UY4#5WD4z1E}ji zS&p#JsUhU>U7Uvz+Bs1T$I9I}8in(iEX)4d*UKb^%6_9nFi>xaSSdex<04&d zy!nX7vC$dHV$fO4slp?_8g9+0#fL2_d+`-lycdjXAr=5xwhj5tI3C7a%ko-?YWo}O zkOW#{!%YEs>*%nORhLp;%=V{6r{wrg2eM5BJug=sHAul@E}ZI;GlTTBuLzUGS#dl} zQ7FK0_2+1A$U&X?9cRK0HNvEtaXQJZ<6KzTtCG+`CVbE`P>wB|t{2$WW&Hy>OtVsD zsY#1tDa)jt?bv$B$l9Zk#eYXehu=-~k=32IB^q+uRL#n{HsSwNQNc+}5}~-vVWkx5 zN0Lg9g|W zf&zTF2_Ub|GV&k8khLu>=n>tO2mx}a8yk-rN(4|lce>wTaDH2CnZ#i$yt zgy(&I*oSyR0IfmBusZliEa5b#MNl!KAwY1*_I+hqJ$LUjro{YN`9F1Fj;xXu$EuRe zWIn5w^*dRohF<2;ga0ZWoc!&9IGr^eysxI`%jv#J4}S3 z416(bDnFZ|MAd1_A6&mOFm@C#(!hT!dfh`^w2vo!x?apOF^7Cef{ucCOV0}pQ__$n zs2%&O--xtDrm+Y)?eIH_@xd)22PB-i>{ZBzm!R8!u4$Sz3fkCs|CwCu^2meEC}d?Q_Ii53 z4naNa5Sm?nr$Aumggz*kJ2VsATlK1)Xu(RURfGnw&-6u9gk`M;75rcP;pm$X>WJBl z6p69v5bGcSucE{lxwUk3p931^p$j^M=3oM*AR1ctnh(^kU3rTX^GSN#3}gdC^1_H% z{7fK&UHF|62Ga_}6v-HXBI~N7XL;*FZ-2d~d9<0R#{V3tk8v23Q+BL_YKf*Hkkja1 zF356(4V~II0b16^0h~P7Oho+`C|n~i+48CBx7H4{o{b)D<5p4dkhN~EXDMRFm5a4R z)--{Wt+BR*;;Ex|dd>swjE?2Vyc*Uz{)U*n<7B+D3cgq(b(YxnFjoQH%Q42-xK~ac zsv4$L=_0in0%02qqy{ks4lhm z)``Tx7RPs593_K!@S8X*!Hm~@`$yr-#Y4X` zUWzbeHk}q@vd^Vjn_8-D)Tt8&eZg&c1oSSa(A>mrH5IKkQ zV~vG`K+ozd$kxhyWY243hf+k8#Gt_>=}E(yrLCj6bK zOiX%eRCFgFXGArq;1{vUfTNT)oYS#wo#bGhWWXt!yEuufsHd>Ro zuOBk0tch;2k?VLd?0ul-^*eF0ywe_DBl{xXXZXd7eDIT?4yO*CSzbitdfb9}jQmEU zi%h{>VHzW&+X^dyB);!f4$ec!qOGb*JKQLNW0yrVJUwlh4EOH%kcpFDIgG~C*N?IJ4J4AmcT}$Yt6x5!|blZF?n2x3YJZQyg`NH)thx zYV|gvp1f%D!C!#9M`^c+dcL#25gdW=f3Q;b5uDWwDPYk59yAlv|AF29kN<(c|1e~3 z&prJIP|^imATq#2aD(GqUMO=)wJOt~e;h*is%Vqjp!gV7HF4qpE$U-#5AtoLrHPei z2y7jRWEgutF@cGGvPD+?tj2e(QdI3HLRQ513FeB1PCJ)TvWD}$I0Bg@bDTO(N zb~d3QmR|Og4o`Vc>YLZNG3HSbnHFM!ZlDfi!XV`NC!lPOyjfqJu(g(Ape^(E-Z@S zSK?zVfHTCCB&8x6a;-)KII~XnmRqujz-!UKG7;^WkLI8+CO2dSH2mNX$=g+XeG=NT zM!PVjR;T!~3kW=%#j!QoigVI4FzTunsBTU5jey_{lAZ*Yc#Kh#=un_ER}R~~*{|4_ zs!roXz4#~3Zul;!m*3*-(boKPzL%&YDkp<0aV6Y-I+*JiJl-^Z>0UJH!NvL$@-(cP zNd zaQdg-*2l&?VQpKC1;3(28$Eg9>FS#CCFg)vk*Qox+-d6i%YZ=)} zNBaVVE=)`a8(#FJIY$&`T#^f0P%<9PDW0fJEKjcnTaxP%VZb-*H;e|d%R`2-TMM7( z4U{5*x&-f0PHFxl>l(c3hINt|RBDq`P>u;TTeRhERm<3$X3aWZ?&V9Pno_RJ29|l- z`WmBe>vfXxhX(qEL>g3#V+dKc5(1QW=^d}XrWPsdMEWty*3EY#>(EF=5a;kTx2y!s zWVhVzBq{e5W)j3UG{Z19Q`zG-+|8(6Ebiuq110J4>u4vqNJ_SF=o$i$T+i93)Q zw@x6hK+#w#YP{OfhlS`LWq^79PiZ#kWGzO%4uoqs={jTk$+q)rW8N46+&rHmLDUrFQ zr8plye)Nr6v^m}wo~^Ntq@l|*?%uyx|6!?;xUkc)rdoa4ef5`{(wO+y(EcCndbb2j zSn3o6$Xo0S04JcqJ@OVwU1&l7kniN(p$dOE{Qpc*BU<4&_xUbCL2uK)#rjtNz4xDn z`Ts2c-z@t7haUUiT=xu2>i7=|XucE0Q2;yWgw={K*TkqQ?T({teePptAbD~d34$<3 z6dRqR_-AQh4`A?xV5#Il%AoZhs;}UceQU=SWC!U7V{7ePN}Z}AuzQ2@2xz(u8Q0yf zy2j)|vS~i_ohnN0OEp8F1|y>>XjMO7V^6~C4#Uc!T23>Y@LeiCuKPxgqk9Jj(|Arz zPZxylJl%MjS0{xI_BD><*a3Z)i`#cVvyT8~h1rV(KJO)SEb{((isETfXWmRDtVZ;t zES#(%B4edR6ao!B#^ni_dr*!iL@*g!b%4aO%*z!Wt<%pJbQwNDrM}u7r?0II;KTbZzc%zA1usgx zP>KE1^d2w^WF(@oBT)xMWfw3VVP0D-q7f8E5}2UXVKuqG?2P!sJ?HLm|LI7gxH%~k zU_U^Wh7HhZM>M8!Pd4Y232}hwZORC(4e4-%q3=+#`&UTVU?j&E|B%VQI@n;J{ut7G zlG~(A(Cd%PN5*85057N}CU?T5npz$)lRy<|Cw<~ym|=bAtu#je4uvDU=?kr8i{7BI z;+&j@cUmOmZ+cM*V&{%po#hni3$n_C`IDIr8Yu630)XWzscgUSL)>It=Ie$`k%+_3 z3>sz7;GeS?iYWikzmeaYOMF*fw)oL06@l8w>?VISX-V9VnZ`FO`M>#?M$90L1j$xp2H;Bmof290|bFvD`$ zDnm)kg{5avTd(j^Y~#!-UO~%b_&B+j<(LD8G>Vd#pm2y7M2Mez2mk5S2w+x6e%V{ zzmL7)RFGh;2uuV9p;r?!)q2ShVcpU84wr3{l;Nb!!Z1V<;4*YnX2G$LA;0MFLJT!z z)p&NKr1LFRPl1~}eWe6oYcm6VY!rZK;3z=56_Fj&+A$3sVgkNgL^hO`W zXx*-V-M6xi_iEtC>|H8IVrSY|`FbVcJCl;TS=K~doJPoI6(uKdduXUDWYm?62}Rrb3@l@&5wEKiZ>W8yQL8Sp zDVHqn{T$^Uv&m7Awg7{1BeR?1q+A7mED$Mr02i}#FC4}#<}#4gii$X8oJubHq-TQ8 zTCI{F)i_pX9 zR)e^RAOVDSosb692qOa2p}Z0D)chy-srKcV%@tmicZ%&(Ynpo_O;mP1kc1WX6Z{1q zI!%^Vp62h?Io>VYGr#AX zns~37g%SU=KXP{eckK9|3b$XZNZkJc5)3!uu%pQM{_t!&pFEHM<7sYiaoBDEQWr|P z-WB1R{8v396n2}u03Ro7vQTgOmbs^H3PtO&j+$jP5*H3(!j6DbX2Iv3xVs0WUO642 z*cX;kDsdwtnAfW+02Kbj2zTG1!x*8!c7wO72W)%|%xc*4?*O(Oh0@F$NBgL)~>4_71~9alJbrQWRKoNzBHEi4-oq|OS&MPlT@ zZV{%$NjkX&)jG5@{l|?I(}!6^=9}&rNWA53{#M~B5^6aN=g-_cUr!ooN!#Rzjs>@L zY!NBl{J{)DCNCwW%~>MVWTN9z_{U%OD$_ipt07LZ-x^byQ1a&2uT-n=9GO{VlJpH{ zYJbD=EZa*tDlkM^b{hQw3&6dC0LAIeC^0ibK!7HQa5v+v$V}dEAEg2M5lX$OX#()0 z@E0zCg+X^ew^z1*c8&oP6SKCD2L`IAW3n#`&EHRNT2*O1>$to`A$Ev_l5Nj3 zDzaT@m0Y-g?bBi}QH86O@L0hze)%YczjZL7c3AdK8Fu&6e5-O_VBTB3oaN;Y^YPOM zC6Q7LQ(XJw|A8DfNaR@bFR|YK&23NFLwz3Y_9fqI#T?4P@x?OPvVv~eDUY2lP|@jS z(#7#NWhY3swv+P7{d2aH2U-l1s)dn#N9iCkdZ@rS408cWBc`Vx^agV zqEU8iT_eFwN@!YG)&&D*)u|xvZ&~P4Cp-Rz(pqvkIPAzmc)DZ!$Q&Fu zSrV1~O_8$AH9&5V6;?PEmMxfn^HpTrJs1g*kw!54LU*B!HjQt`RM~zLCV#fb)lOxK z$Q{Bef+8(ahCyp#km_+8gVV&+H2<4}jBm#4CtL}t%<5{|x&azw2g(%x{r$v~zi<)8 zIKl$)oay@dCkp%eA6&x!K$X>ie*j0*a?!>A&*8wl6AL6Uaew{4m}ve7PO0xn9BzlUPyU8!5*B#BwoOXH_d4_`1_qa zX3a@N0d|o+0rE8Hh0_6C*Rz&E{|*sJe|)o>7iF#gh(D@f1U&UD|F$0*&MurS9J?+E zo+p6*OfE>F9wf5hR2yLeZ|x>2B+XnvoFa|onMa)i+|vr$)@+xt>%wk~p7kpA1Ss0k z-p866x(DbO;yXp9rAqj7Y(s3oVr&|?heI=N4Gu-np2%xUlwZhT9C%O>tZJaTdG9s> zjB4RdrFR+i_{Ej)0k9GLC_hvz0^mnwllFj5Dg^K{l5E`(){r?~aVl228OqV4A*U2d z7I4_E3(=YpU4u#mny>M0!pQh2w@pAbhl=L{aWj7TTqx>{yGb{8f-{P7hg~PTj*TW)cPTHC3LHMrnk!hJOpx*=(9e=X4Sisvq4*Yx*ZE9;M&lV|!RD0G zD^(IEU@&k)7kq1>A%{Ex(B^%Lav#Q9W5w>=<%LoYgc3N3HnqyOofA8OF+BT2p{Yf+v3*1`%DmctD z|NDw_yELB|P8Wnf2juNk-Al~CLkkl%;2dL31yWCTmr&o7jSZ{8fR`Qq4xr0(w9vCX zEa&>OjQ>;8`5zg^y@gE*O;L&R$yok#)r*1T3dED~^&a0(Va$a{Ed?oS{9HQ>TbFMg zvO6n1ocN>=5dQ09d|14~OnL6Q-V08o9)qf2r%@?OR7siN_SDX~;=NuhAwhJC{Sy|< z`QVrRg4=^rpBVF9KZJ$3Eq_2ml$5Ye$;rWLbmrz!={1WAmNIk=?OE33TkEI$=K<7n zX;zblP7Ql~&cplN4Vvx-1!P)cj;#nT49)e;aEOJolSbo+o_aWm4$_k%Q64Sj@AWw1 zq)A&lzb_NUHeiUau+el04>g+!xjlVAA>EY^rMTX319j*m^5%Ee4St2s>cbPQzVu%J zih1!Sk#(BtHktwts_RgW|D-cYlHrhAD&j&hWEK3RSH&Tr9l5>a2C&yy)V8T3(HVG? zG;3qZAK{>(x#GmGq)I#~5Yzb0`Oguf))sisU^L`?D|G;Y^3~rXMtX4v*Ey*g&aRGY znXzI0W6ZDt>4ogg3|Og9Dk7&y%^^|{b>l$Sgf>E?p;mdu2;#>U%KJx#fnKQ>v$`2b z%!@#6x3y=`UYL0aQ*^;>kla4oLld(IbzPmvZ;aw4(G6w9ITIBu%~l}&v(NFaMc2F^JWag8GAoVOoSha$nN%8g( zCOtsX`A;Q@HPk){P3!gAdDw_=nM4!9tJ0YD;T#SfNm}F%;HI)l(UvF?f%|&JMZeha zJDQPqSaQ^SEHxaaGfYGPji*P40kpYFrJc$NfJ(ftxSotR&k-&&8VxW#o62+kmc}pF z8KmlI@H{W-pq@YPg;83v^=H$2dAQbEEXa&N_ScG2`C7>DC4Z#bvGgmlFwM{xB={bN zG7Aq{L{44@w|$gAG#0oSO(o}@Ulqk_mLu*xBWNzUP0TP(L20{|Ysa-_{Ss*Oi0!S2 z6p*-5r6$t!rT5*jXD69gNoABqNP&)s6CxI>2X2}nKQy4KjujzLkw)$@I%lNf%*4}m z#ow2^(}Optj@aXh+wY4!(yMBAU9JQK2Jb)$FSFskO5ZH{-!B$-Otd{MsvSy6zLYPkofkCh>+Kvl z7|n}EywnHeDGU)McJlR(O;3Nk2kvC?fA!vK`Z$-|IQ))pwO5Y`R35Pu*D|8)2?7Qg zJweG@Wtm?|5Q~t##=4V!FN)GU zJ!b*2STwEO&r-vfSrqADdV#H3pB)eRU+aUbeuRWEb0A618T63PE}lCTTpDW(h$VZm zS!0yS!RLUqMN{wJ7~=f8b#I;{$p#3>sj8KRx`*Wvr2VO#>eoHhIxzx*Xr)Ij9q41~ ztVV%~tXr$geT=WI4JlX=csHCCp`&Kew8o5Q5 z;5a1+X#&wBtR5NRU5a36$kjC=>jHxz9BYl}fV^s?9Vyglq2ae)=cSH*74j@}Rt6)O zzS{`fOsTLURz!mX4$z@Z%6H8tuyZLp$yM2Gys$Eu^VY0Z#Cw6?y{V-IoBsGJEGQJYy9ZseNyNK`J5bS@AS`=r(t;qq$BJ0%qLqS-lVn~Mzo ztiZ%{$yK{?+9pmRq8@A1CLt^52G|J}8u+lK=|ESEd4gz7(M-0UkY=j*rnkYt8hH(R z{~v2_0afL?b`7hP3W#)rqLg&!BBYc?x;v!1LqtJZ8l=0s8>Bm=YtgZ27M=gIaPM>W zd*c1i`M%FFa4(y>kAse5`4_uUjJ{l zs-}+|uyi9J^;&@xckBuQoAnu<^3M5ESgAM;EaUAw#!nHr$ED!W<;V(JFTeQMW8)RG zd&XgTTj$)>iI*8RyyNFr@TI_R-!7s81dmZ4uyVGeu@9@Ol&S(^Qh3LNF^d{|OxTLw z&=6Q?7SG|FEQd$(K_HZ?;+qRxqGq0oanOM;=BlP2g=xI%$$ZaWjnjo}IAs3AIO4d8mO}cKr@H58qKUxXJB{vRb*xqHrv}Y~Ido@f?(t{<KoP#TTaFx9ayzf(a1)5jjRsfdR~I(mGJMq?AH>gQZD_D&;!rqacc zX?+wGj@&i4N%-o(f-YAiuK8QQFT4`{lMfO{dG!DQY?!B&(nQ;?WPs%QRBmYN4{Bt0 z^zM)TYB1rSI=BC4O$71YZT!hX^=!Z8YO_gt&^-_n4hzT0p~P^eR7<+wi_7BNHMM6t z=KZ_gc`_wz6Nq;hpKOP{bJb>rKjwrX&05i!M<+f=XlWG{SU@7nFc%J)ELOy7vYexf zQfU^?0WQ!_{d7&7n)S}O&ie!!1T7bN%K9LR?@-lj^_xmolVDh`f~3#+bt}7^(MtQI z2y7KSQPBdLh+9tc4lvE)p}&m#+l|gegdg$a$*briCV-0|cI<1GKseu3mAH+3+56CM zLwlHlwGDz{J(O9w(e~&ClY#uR6s^U`uuq$Qz2;rV_O3WLx3xcxZvj!k>WmvaIR1YPCAtEI(lu2ZuaAS ziS;zZBAW2jrHbH}q1^0+VbuWNlUF!u>W>OI(ml|>t9uEm&}GFpFu zK)DJt1=P%1H502>nYa3&@qP0n^~3{wl}Wu$Pe;3WCExhSq32Afs)r3UaOg6ogJgqK5Z*zp2c%nC?4Lp`K6i^@jxOJ<`5!#QtO|_5sW)&WN)it1lto>Xmj6pqZ z;KiilbUe{I?(E#_-k2?m&}+$wHD8N(Lud8Ki>@6^Rndr9ih#7*pLl5cnlVH%v$Gjz zVL02duwqH>sN2~E1wW_j2oM2my>v61EJ97o6)v+(6ANMUZu-SIDu&2m< zs%${_V$#x6uH7+N1uzV_h?J#Q4b<6g*TLFkRD}t8yBQ81VTEgD;~ER7-Aczc4o)_* z=|T$A+!l`5Jp@20P1VjGUb)6Y;djwhFSE+zK|=|F0apD+>FQYo=YO=oJ$iEeQz+;m}WkfMl{FV4t`1I@dghm~pzhVf7^o)%wL z_Yh`j(|40AVJJ>O2b)?nC8B}$8Ixe$^G%F1xcrmH_c#38QBsO1?~AkMJC7)gr=ubQ zZ%8v(Nx!3XWHILy5Di$-PI_m(8z*}gjpi%LC#m-8GoUX?clVC19vD5F33Iq9^Qx5s zJN4L1KZB|R&bG6~Y=ax#&@J9pN6%WFdJjGFJ(9e3_DV=pZv4~3>M|XBKD*}1fKLm? zzvu#B&m?o#tE*1dsz>wmvyQ-? zl-2`Oc+zSqNmPiCt(SL(qr?2JSnmF0Fc%WN6szp|c?_ zN%79ll_*W;hO)rf&`ATHaL;%3JKXpCPZvlW&_;&RpKM zzGYTz&b}V(9KzVljqHW%);F-41E)Kl2lB{*iN9P^s=#_mX%w@ik7vQ2rMUA{!IPE# zM0fAcK}IQih~$a~Y?ko)4dzQCBIP!zPk|m1OfhV$lCl1@m|lole;RCi=Y(a4v{ud!^iz(uUq|gujoX^s6 z>mAS6n!MProew$5!RKUlJb?B6bD)i*f$d4GH zZ#UD6X;&*6fRDA7@IZ~w`*QC<>2#9W#IE9%dif1eCl~3A zU!G(*zy4H}C>M?#pS+5Ti-x^wWt?MjpN*>AU?Necz&u@m>9FrI6E32ah+WPa^_0<* zb#?BNI_Y)A)GFs!Kte!(l2T4$4*SM7J-|#$kc(0;i-=TmB1&E2RdS?O0g^wO_7azd zF`LLPNhN1q>Gw;3csY}$*M-3 zGCPMXB(SnlX^v4rdHiR=+hFbr+TJrpoa~|HM^zhY=e41bV9kD^?*aPBR0i5tvc7=cg??xegP1>`WtoJ4v#Q^IN}_X&nWr*keE9e&zmwRk zm6AV^J|(Z{2-mJYh)a?T9L$Lw<)0!c2HBM&&f!4ocAkjpHh#}hZ%4O0hvI+A)jlAV z*f4#Jl~US3e<4_hRVx)@<3C^=&hg1~HhAKEn8D;j#Zbq-9-!Ih2-B`P5;Zp)UpeZN zeX;+2pk4~P$Ng;iwQ?azD%PzXOQtw_uR@^gebz$fR)})~g|z0J{e#$@8?LbZ7hmP+ zs)9`tO(NU(B~4#ng2cz2(oHZSJgdPGIYK=4a7e{p<@o)ohqL`gUo7d%eIcl91hm`_ zYb(tnTK4TEQN}bo)#XqX0_w#nDS1>M%k1b2Up*788YC~9d!PMyIp4eY^8vC$ zy>?JnJs5+llY%6jv;7cMwsJgQvIAuTW|ihhOX2`#N*}E zhj7Nt<^>5(YY!RovcB}fnVH%OEXQu$tfoj_R_3SdG2p8a%%$}44abO#XN!CN<`tAP zuUadIa=|5`v!jjyq+VJ#>n@e>>z4JBO%0%)mkf~&NX6@&21Kr&L2fIKjAuu?_{QRF z=$*pDJ=7JGtsD6b-}s`0ugqHkw}xCoSGVdM|1C`0fvmVvrn! zya7)l>1}4NB9_6SaML_&HOKAm$uHAHxPqp%rZ?(eEDXK;Z0{f#Rye%gFcO^%Um7Cv z8a*g5cyzX~exrM%D$<518E-?H{s@5asDHrtogMi3yMg`XreaT#P{YopHosCJ+z2FU z8H3p+(M0LGZE7E1oE#oi;l99uq%GF5uk5&V*h|l=HQlk>Wmc9bV|m^nJ*$ID%F{7p zE=MWOeDXZ^`ibhj03;X*&LQnJO`iWb?p4Ee7cuZ!qO$ql-$4MjqmMkAWLY?!#wbv$ z6yL}IFEqNb)Om^r@oLRhTTSY(Iuf#*x0n?-W_5M%0^xOSD?o(QbliS(rhp5pgo~Ack`bWF_+^(m;}ew$G%Xw9#cN ztG1(J{!g_-yv4{_%4%>Y)Fdw$NLA_{Y~tg!vXQF$^a(H0|AIu>9rTznbW#@K$BXlM zGO68R{r=QmRLA18z^J)yOCEjl>H`!segDUU8*F#Dyp5FJzixCt_KxVQoNqFe<}6XV zh8({)OYxiz;5rZ0-f!_@<@eLMMQNBtYVZ=z_|=l?6r!U7e60=tep~tq^=5%1dq&JW8Yc*!{mohx zWoV`r8Jcu015$R)V3TdW41YAIScUWNBIPzc((zfGyzp7HuaIb;G=1)X8`7H?>9QFw zaMXXf_541aZpv2%SVLs}5n8h!mkg-zGt?VdX}Phv85dN2gJ7Mfow=fYPP91p>nM$>zQZ?d(Hhg125>9YIgj3e= z%`G=h-;c&Po|6k%aassjkiphnyvE+FUw@-$lu?bw;*_s^5e$wEi7E;GZG-UC}? z6wTxDI>OHv?+FeCp2YGU(LX0@U5w*HA>I}M`&ljDtgxKSy$7U$_jaquIVr>~gy6MJ z91d5(sF}rl+Tn*TJl8NoUzOx`(5vfpuIEQTkr_5qOVgy%`&DXfGxf>NN_nJ>$O1(F zp_KekCOPf(K!{A1lmJio7LBxZ4qT;kTA+&8w5ybGVNcB~pQSU%;2Hd}c_Fp8yDSOL z?|3dZMm7a_)bzqzO8WkQfL$pqGZO=$tixejY5oU6*xi(0UBmMUarLKWaP*n-JXMO1P8=pu z>K?NJ4tV{1OZ2l;Mpik=d}<7@-B1@FZ4w#&nE@P!o5^mDaPW==w&SyJkv?<>K+Hmm z5E{tf>L$v?_6M!do(+(s0KyuiPG%153hZnn$e0`((^>DwVpWs2ZKIUY&C!O3Fr%XA zuEvAA63#+&7UpVWL2m3ud;6Ugb-SY9i~0~`5-04kwSLk;9A$RZC&uQD2J|@2fm)-e0ZGgSyeJ(nKFxN#yG(qCx5XF3R=x(6iYM;`K zv_d>x4Pasc9#;%}^v$I(W zSIW@5b3?cP&L~-ZRd>r=`wI}r7bn@GBByU`GIR@k@XBhuBd`AScTEE~$|K8W4fw+k>`pfvhmeg0e=fw?5*6qM<_ucla!tK`n^#dXPOX{q( zjaSZH7I+O;fq2~1pB>~J7k}g{#@TnjOQH9uu)+=5nj^cW*O*6r`-}y)Okv?Iebfp3jO@upeoyRIZ6qkolRjvHcNm~MHyY7PKLtqRm1n}6 z8wr%#4E(n8b-e?^v(4JkS1fc8lEs_G`h(Vm`8SOWqq*r~2S*Za2fax^G?&{xd8x>} zy0__k4A)Mo+^qYGZbyjT{E0{wk^6T>b!VC%Bbp02Bv08w#F&P0zPK8V)sw6dccgc{ zZ+h72elWHb+a;0!XThg{6WdpqfJL{%EMWW{@S15cGv3{vH5X&}93SIT@sf5CkhH{g zKpE$}2-=RC7gNF*KYQ~x1l|5L$5X429f~5s-%fA(9}zit%9;*rMQZhqlH!hm zb<;7mwRPM(P?Gsw;j;)r4sAjb}M$mKjl4FKHHFLyZvcb zYTQOQb=Ekrsh+U#LbLZ>%I#@A$k}y>j~7b$s%NR_m))Tcc(+`N-^-D7U2^D`9p;m= zs5#>XTc+{i0rTpHGC8*8dB*_ zuX#Stu_t;p$)TH!*ICG+fBUnXQNI&Kkv(ygBxhq|L%*#Kp~mx2z*&v;6X6hkOY(F^ zC<8&elyc#)#0%yx3r83uu1V(U+hP7_n3w>0wKG#mv$QJeIEznHE25>9Z4Q-gJt0~wm|_>vj+DvSmQ5*#K}U7x_-`@%ghy_jP5 zrDBE<5bPr|J>biU>M=$bGlWh) z1c>cP@6Njbr9~T>=KWNj_>7HxK_YQ}(AL)Wre}#V$@#6!?;rY$ zbIS7DEPpW-;onHGQy?XYi>6ggJ6dIhvC3pWYQ6xb$Jl`%DEdg zdGaHnfxA0cxjC&1#i)Ron(jAmG-42e?R^Qv7#DZ4ECDoOl7iFs=@2@|^w+<9iD)%5 z$nFbd-D%ns)2HEwVx@nX#mxj3^ zUabaCLo`Ilo}TBz`npkKgjQx|=KCjO7r&|THQH&;u)#>6rn^_$|2=@7oG7Qy({AKG zJqKM%nE zsJ3_hWRcuf88Ys&JV#OMC1GcljF%qjj5b$J42(sNB)r`jx~2W|4*zQGj3*&%%~yQU zytoB=lCXy+As<50_EXHuUj&tZ#4bYyz}6BmgYCth7q4&AFSy{L#*6cOi#PIQiHOe{ z?cm$UzLOF#jdsUrp)^9pq7WBc9^y)Ss4~D#0F~2JV?rBsZQT4RH#vvudAYXECE?X4 zZ$T7>1hfSF2Z(+__D0WJse$zRwsOb4pAD3E^1ucLfX`x$N(`?nFhkcGLZHo3MLK7t z7sn+WnZtgk^$gf?49oow(T$T@Td-qvAv@EX^_lP>r^uD_#Om}jc@aEL zFNDjYTJb3St=+c%#H}ll7lqz+AKT#)c4odtTsaZunsFe85(}Q?yIJpqqUnnsfL?HZ zZe{Vn9ZWqv8wjGXoST-EQ>Pad?0j>Lg0R3<=32ozBV8IG12A=_&&$D zC0~rP99PR(5G@$ng21q|H;RTBvo>_zh8gge4MyG|F}{9ulIktEdUt@{e`A4cH1y-^ z(yV1ioJ`D57Q?6O*UOW!r`yDNLA2Xk*6)TAUE_dg3hwP?2hQ!Z%2d{s0`i*0gj^Nw z?HB(u!595{3T-6%&Z)B>6XVaE;1+WmRI{&iUq>F(d~WK-QDqx2@)VD7G|z{JY!?VR zpqm1!lTM|1vOOVPleAbR`Pa9iMCFC*ipZ`2sn?x4z-ypBc8xHAMmNVPEmzIQq%!z={<*CpOnQY8QoyV`h*L>>@PVmt6!H%8j zawa=3j;wJDj?ea*^v;lqB%Tvyj2t8ph>IPr>%t7N_)S;H;_jK>!C=4o!Att~`d@d1 zu0w?__0av6t<;bI6UW+#Jh=S~1F;n_5KHC5Nc=#_5_2gP<=8;N0!Wq)gO(GsTL7A0 zD(bGwZp}Ry#VAi)UX}+ac?Gz!bS2Qcc1}e;{YJLMe?_+R-O-n@pkF&-S_#}Ca-i66 ztFo9q$k)DkL#qw2*GKLMmi-lIF`nlj$R>@<{@^v?5!iTkWg4R^Y z`egUuu@&VM@7NFtc2V#f=KNCSncwmI7A(9?CP@pDr`rfN3Er_wd$cvaDtV;*B6(Q;iM$Xuja>R=*A}|D?M{>{mBJZ%!6G_@$$6$q~IqgO*h7n9hUUjy1Nz ztOGCW9JbT4YMeivo$dWZa&yW*ZhDs;LekpDu-i)G<{0{ZzcHpA#{rQ3J7}+uA{Ije z7vu`+o4PYdup-eSdZ{$4ih$fqSXO592sa@+f7^-ukrHcqeS;hb%an-0ffC`QKL=nt zc>=^j)7{~ME*5i7VnL^={qH}yTo0@%NeAFK;-~zv_>=>8A~WX)#yCT5)^{IK`tCma zi2i(xczeKni5#=EdTOO>GV&H6NDK->FOSBzOU%dBqM`#?-Inw;9LC3MOm}CPsHcI* z9&Mu&#vFH0a^-fQt77uw!S<4;4}$UAD@N#kTUzS(1_ z@tN*vMUX=_B}aJSD&I;}V0!VmWUyl&4Ph{;+FK_i;7yHbG%3{j=gTvW+3=~+z>lvY zebKtBAw$b+&GpF5SDo`Fcb619m~AyDM*Z5(I7a=ksl8bdVsj$x=J|UN$Xi|AREQUe z*~Z>XC35wGYd%5KT|BtMBAE_0E26kFv$a(a{t+xSky(KIY|gIKT6fur88$ybsZcEw zpc+s2<^y+UtM&g*CQg;;!fCnfvP~-1u|^lf18I!_7fJq`Py9Z~VAin0&P-wpJtdF7 zaEXtP*Pn1<#v&mhQ7YZamCv>Y5++elP;$=Y5jT#s$79|%l`%0e$PBGXdU{DnND%AG z;*)~U#)~(ZeDKZ0#NAjNJU#j7>o&f9vL)zi4KxJrV?N|qnP0>R)}V9_@m-l0 zsFXg!)v$i}^y$+Q`>hYODkUw>*{(!_u4SeCPhd#uKQTqh{`9`|$E39GqpM!*B(y@2 zNWMMJ9{-J;tG^dZNE(Z0*))pX_){jefX?PHU2*H&*Oq|}e9j~1TJMpC^dw97P*+DL zaJuw#D_}S=M1q1Kk;LaDCp$mSa_R#K`K*{1()sqh4srm1cpN(FzH6r4hTD8D!e);9 zn{kz5$44GpGiPICgODF8Dk?&X4RCXe%R~egmdZrP+t=f- zy-OLaZn~Fv4~7S0Zb|n1lUDto*6@6mlld)Xrw#ok zWlYTb#U=4d;m5_GuCu7<@z}8hz@x63FcdXj>(O#sVJjtoknT>8wZcw2B=2tf`G}nF zzGyv#T`)Nee;xN$EAP>C9GSxhE*CAM z2m(VbkBcs*2s9`jImIazC-}%{xiRtC3`{8;9R`kEW`DZ>R5{BMw?^%PKWe-G2>JFY z^|yLmyCVI2$1MKrEAAxIrbdJ+>4+yG-H4s(Id7u)4kff$-yykDv&I3j z^-G7&Z*wbJN6Fs|fBI1Wk-53zVO3&wniSxzO-o>h#%+isaJ^XWXThskW|m9@5qeZd zrfh`R5;a2|k3I7q!)+7`fZ3FDnO`+}#MZKSG(XM&cJF@)eutrYpsdgaWOy+~=PWd# zF7Dy8rFiGNb4d_~loFuEHK7%&t{Fkj8w1*3-p{&cmw=1~muf>bW1^+%>PG_u*awi2 zo}I6_TEjy6BJrYerT0NMd-_fZ{XoZp#qjPSiobw|iO%y2es_2~ui4Mj026**rEatS zEAv1y?lV{OIa}6^tKXYfeXh@lMt|d_u)+U=&&_(DM?Cw+`;yyCR`bh`J=c^{#p-WY zvUoqJvc|Fen;){87YIuL0S*Qhm`y9O`2y+ONRxJj<&Ug(Vs(k<=Gu z5;(p6NL(Mo@;3(Y$)T&Ps=jk8E#7dhR0;BYhB%?PVLYhGykAp20Sy`}ura;BFjp=?7Dwte<=!{-FG`w6MjN=e@dC7D`=($>0r{XY~#OJ|%MBj`8 z=cJtT(WrLmD>95~j{}U=kU-_awlqaEp^p=)5{vamrUebF1osd&kfBcSM$>+e8AKUY z{s%~<1M5Dl|N3}_mANojH#BS?GcY69%q^o*9*Nr7-Xu0ITR*T;oGY3>Un_ zm%NFe4ZEAH&b|HxzIf>Cc`Ps4HzaltGRx?EhRbCC0e(Aer-e;9pLy$hn1JS`37DQ} zzM3aeniTYzLuvJZ0%_wfUSfC z<+2PJxUz02h0W+b04CTxelC5xDTlJ-j_v`zH9j`Kr94&ueOmImz(0Z>J$GMoP&8VE z&@h$d$-*kNt#lIjXsxnm5=zC2TXPZA@3y&0_%1dgd$YX*~j6c z2n%o89I7KBPn`|CK%kyp+anILDw zApo>Do=v4mA0f~UY|>jPpnWAkbHstO5X>O$+p2Xab|{g|$b zIh2+CnnOn1LHes^1cfVkG{IVy>e!1kM08>Rgj1E$0=qT#m29*YFJHu42~3tpTOULpom7K&TYtHoF2(WVzklYGm=s8QZJBIwc~O4F zXzZGl6#wq~Z?H%rLtSvUCs$Uft~H%QaHxn0nC|6l3)HLW_g0M@XSXBg40Tx_g6vgv)CvWY;JF@ zJaMZ%s^IgL3P-tG78ij5hV@L0j45_{5f4X4GbL;$RE@V1&UfeQIm~ZXE$>-Bm>hA0 zYgGw|;py~6B|MlhE3PdX%`A1?o%zaUHgVu=_yqb_3h=t&2-T<*!i)bD4Gk?H?_?}j zp3~#9B0%2on%V~na4>nVwWniaaO;;;JiXSM$M!3R4F4nyfw12h^)Vx?th`V`{xjSECV=F<3upx>Lyu*zfjcJC4~AjBQGFPFbRzs{AC`~_{E}jiDBhN+ui-hw z>tXQ4#CRYsF`j%Z(N;&;egyIaVZ7h3_6>4(hLPDllosBXtv*O1DXIa)JdT@0$n`H+ zm2?Qq>fpSXWWlK2V01u_XT+>pZI&<_`~7v{tKkOd*wS5XFmeg&uNl>Tv5xK)NY^KK zTt{#neT3fkZRZHRN-BCwceZj|rRWD%X~Qr#?RHepPKf?lT;@b~nJfOon@NTbQ}(Lu zx_}Ew!{SNFLeQy*faOhPs=PZ1blnID9G~7|_PrO)IxM@!R^-#T8aQ6<-dB6h2dzkl z2W@C84bVaP3u(d2Sikp%->DzXahvs(#3>`0V?A`NoD#?9sp_-DW7C_mBmvx+-|2ob zPelrv3TE$(F0sO-#Rpq1uWN&t8wX1)O5?TR>yjwq9aMa)r!9VEeLDe${46%j?y&@x zsVDaSn5D>qSw?MFR*&^c#hK_g2+QxaN|u>@71NK)3MJa;Ab><--RIci?Cg!(lX7dH7nJJ(7s(iE$)( zvAEMu6P7Hzo%Jv>=vobz)n{n8gPMGNrG^2%ndfQ=n@sphSYHI~iauYt{B(D-YuW1< zIR<9)vt$vc##rUol9F#L&IOO!QB3EGuV-`a{H^{tDYNyShcF-*V5A-t2_jy_~c3n_v7(SM%W{jDg6&3 z`C*0>4Gb9wj1qIITtQjI2kxqdzQD(x<3^o6(~DI6EZ90soCE>CedR z(_qW}PHBn2I){3aUoLOo!j_?o7m-%BCp68kc^Xlz9^GG#;RK|vE-2kRG+fkDW#kUs zQMVfW_2!XlvJ~zcSO-d+{!gzd0^l_@1JcAb1>mQp(Iz`z?WC$xF7LeuwQvtQ+@E$8 zU}J*q4NS5!WlsY}5+Kl3=Daqa#dR^8Ms0J0St-91=pQ>ikKBT1KBvav?uAVRYA z@tzf+AVG3kw6XRx&mnYkbv+(FcQ4-S0?b~&<39hlsmA}fM^Zi6tKA?0MH1a_@ZDPX zIm3@*y&Txr9+3RXVDUKyCbpoA{d(^S=xZX`Rhn?F(=y%zAvGkKtwU!d;wTwqIQ|}(2p2b<_%iE0r-p^3>VtP(3FeW z4xMr!^MM*5z&^h)<7rc8@H<8GchU_xCePm9UTOW$>~ljZIUqjWW`tU_kVGa+fu1M6 zy`EH~wwV^pa?!X=HKix4@<$3ej4ilYxCF`OB8s?< z)<_*erz{EtFJMfHUpQs~i>yrj_>{t~&{|dLc$!Tr+kTA%kc2$XcC|EF*m`?961_Bx zz_1gdIV5Idx3G_Da?r;+e~CWLtY<+Ukf1I3aq5qBjzwWKw-*LtBNhTZxWpwriJf^}>v$$*ePPFBH(9V_%=GzYoR zrEC75#L37>=Ve&-X)_7M^+dshCowV0%MD_DSv(>peiC2-??s&bU^ zl+)N&J<*|NgGbnp@$~BkXqH;)ds43&>ZIMNq7Hpe zlZuWek2s;Ps=?XZWX_5KjBMA;&dPyLhnQ)zfhy8l4J#h%&tM){_x!^gkckUoj~kG- zn0_^l>_9+ZT3)|pW<-R%Y*LU;BH#poH8>W&SroczXz4s9 zkXx`==uq1W&@au0KYktXa^Z$#Q#g=>NI!hX>u-yd-PUlewInRdeJ_E4%`Gc=dS*oC zuxXI$IbtHbExf<$Jn+8Q9k@2<_Kc&PLyy7bhms=5f1JowhXsqT?h_I~i+ zM8;zIy*qGiCg8R1eLb?i0enC%!$8l2uyivtd!xs@a1+*D%3=c!WgEG%u&rY!7g#v& z+pB?0*5Yl;H$Dsq9Fc4OXiHp-qL-2+2SkYd5gi?Tsy_>p3bVbVO`mm+|9lxIP62(0 z89=vpLmwI?qp^6*7tN3K<3jpMo4&sH{0S3HJ!QL~L#r%*Fu~PumBcn@rtS3f>xG-e z-kZ$oj&n=%4v6Knm8!J$J z>WOD~E*`T$kdxPI+Z|z>JvSuGc`yaj{OL?&Y}ya@^Eb%tmA!->9!I_f8SrcC#7vn9 zKw#hGw?6~>Xgy)plbZc}?GgPrn?IcLBfcg-{;G}h*}T&Uo?$Ogp0V6|ShqQa&+2@ig_U7jOudmn1yh>t1=mAZ)iWIL;Mm+1S{KGR9`(7yq_KNd+XF zyPBlSq@HI+RG{HgiUm&Fw=!(qF~jZ~XIE{#J+C+J^(+Ca_yj-yNjeN8ezjS6J{4!=<7wAhS5ra5#mlbQ%Fnhj?$7Gy|35WFxx5-U z{dUG|Xt@n-p~ZVC(E2u#zCgo;%kx-`kL3I~%CycNGDhw0wtbzvR_#~F)!Xsg9UoN@wSe#Z%*}qm| zVsWQZyS?8~A5EZ(XSaL%CfRvUg_t{=xNEu6E0AlWsCCEiP}BJv6_-H?u9Y};y#rUK zLaBO;{QbA&Kxw46H00^MWwir`h{)7n&1Y@&0FUSo2{NPdUnIzmfasa818y~t)j?eL zW&pFtw5tzoB~hQh^B=}i4}AMGS0hreG0_>4D}4-+>qWUO->5Zi^A)pshgLtBD)y_~ zV@pkjo_X77VkA=U!Vlb*ke#H|deMSFbGQHo2PVzlKH?lN9zA4mf3~j-_6cj?RW!O? zk4c_qT|`zd*>HW-2cgtYt6_XFn6ogLJv=pqCobdAc`*;V z)|+wtWx}ZVSympLeSM9RuUG1eFFKasHt8Hwoo{BV?moVc~RcZf+v^JV3{} z*y>ZW96&Qw9Nn$A)*BT<9bxdjX=V87oPt(}ZB$X$bqFz&j|27`Kbi*Vk7A<972*s= z1yVLAHolYGaXcARePxCp{fF?9BR(!(}(Ij z)=<&v$^HKggJrp_%uRzunmV&{`ODcSN&PxSd*7x_zK985-&GUudk2L%#sgshBk*A6O-D20O8V@$)Xo z#>+7b3!gw@Z%X!#st=a)J*ZS~X(F}mT;{oP71rn(R5C^}aYqdZA{t#Ux>d;enst47 zvAG$fbTZfhG4lZ0ui0u}_#9eU1r*jB`vxwQms_?k-&Uo=L5n$#td33=U06P&MWHos z8kBbjhBZ8Xzv3eQ?)3%yB{D<9a~y!*PKHlf2>glkET`qDwou@tky&8DpN`B~^CUK| z8MiwB@$^e=_{KA(;-C6tGVAW2y4v$q%6otFty^g_?QJttjX z?YHPVEiHMmKRU-w23xM?UMuE!3X2`29>v=v&MeTo_t2ir4LFakLy4c0UPpA!t&qj& zVsnv%4rVK`RLM2_e{%Ki9dC6I%g)&{kfk|=NG1P-s7MVqQVy5vAViBr$Mc{;L;H-L z`>ib#6LIrt!}Ha-$5J_1;Hehj#`F%Q^7TC>x=gopFZC1+&HZQM^ zy}sh+A23=@7EVoCL<^P$V<&f=!p))wOc$<5_mvc&(L5D{Gc^l5-$jFSgA>)UqGPC5 zru#K&YYDvwZ$%mG!px9p>{8aN)${=Nw4x|))0JmLYCs&wclvt&NcGRB-G?d((e*k- z9+MF!4uw&)esFrVbw#)j&um=mhG{GxJvUC=DJgQ!$n(tTC;acFMDc+0<}N22%?-`{ z6Pk2FCvLGD`kCj4x%t(*$GF%pzqWZq8V;%DHprZuQne{-57^k&aY zMj3VLRw68q>M&E7qD zYup1dROkkwr@=AQjm@T6X|RJ;^Lm9CL)SLE^)lglBpq++g6y*CW@vNzT4poX^+DU+ z5zFev+RO9a?Y#b$ab-ON#pOE+*{n=9S?w|%_97~kpbn_9&bW(RiHRrQ(Gen}NYlpX z+goHwW#^&RHEudpM&u2OZQapu+k!I!A0U3I0?$W4!Z^neM5|pUS-u8Stzj535U_bE z;@8);>s?S+oh1r!TB1CHWUC0NG#SJhNocBY>zt;^L5nq#STr@$-gk&}qfBfJ#CN^$ zz~DaKiEzFwI;oMavgFr9E$4XO?~cxCC70N2SaL&DPp4KT!cH36>M&YgfH+WZ zSCpBUcUlkJK<6dr3?tCyYP}4vcbNN*R|miDYfcWjmr{9j`lOa^_jO7qkEGEbZ5uQnb-TYGlvm8pibIPCQG~UG z?Xd`gLZJ3vEs0(7;+z zV?udk+NWmCFDI_IUR{~SpTt7w(`-c;3$y4LlR~xw7L^(%xN2%*l3JrIX9GTI)&#o!USR_taVp<(}!AR4Wk=^m=7}TVH zBKK}%G`;cdNbaK_wSYom^CaDz`0kbUE~mC4r)XJB3>-!-DRpIk9aB=QosP}}QjcTw zXS?9g)a2Wz5?ZdPc5bfT4Y{1P=d?u4x^!9KTmBM*E$04YelJOm?RQ2fg!!He<;I~T z;t4_7&B|x`b1)$k(Y+ALEM?)mi{oH5-zp!+u6DG6n#6rQAgac2cXJTvnz8QPjFZ|d zkKrqdhObF@Y@}b;NdyJKx4SWAjD>dr@dZZY1UZy%upgSH)u19GLVC(D)s+@P{7z!2 z<2;5~FmjOnw9UV``;w|W(B`!Y=>goT^KAu$EB;)5zP<{t4uBE3u;RGk?)K`9bBt!* zni;{6Y8gD>FnESTtCEsb9k63c{BaZs;^X6EilinL&yIR=ap5Wnf1qav)MHG{%xQKg zVQb6S`yJmuNRS`_yw_SZau{J##2li}9I&7`Y~>Vb>MntK(EZ7i?+k)D8=4|P%m(_! zK`e$GtN1KNIg1(;)8R=mG1(io>zx9iLXR=S6uSCi6?uaLI;Wz!+|phc={vGYt}lxJ zrLX32r+lb00Gr0T6#+LTB}HJ~xhLBGMEZpDvwbxI;JVn?Gc`3mD75mBK)r7-$sGYP zfOm(XUO{zr+}Ddp{4LG5+szOS7&lEO?rVbpk&=>fUi3y%Xnqm5dVndzo0@&!L>v(Y z90Gva_-$ocU$%*%_KUxoF4B4g_#P0@ND2Q0GL3MbdR@(e^SnOH{Qx@b38MF9DJI7r z-|wX}?P_`Ri%Ta`B* z5E`UD=0e0QLxR+a97nl=z%dpKaKEo72D6SC;T&!;r{na2-%w<0Q7LLs0is2*SRT@C zmW7`K|ERDVTES#{sGMA@I8j zXLf)WeAl$S9@r(jXyNut1Ddyben{6ZP0E9vTTBzLeB61lD<)pvf@}hVz{|&3%hLkS zhwPID-IN)UFxTMHXwfsfwu_sSti{>u9oofvjH*LasHb-(&}o2nCL#&C5IuV9MZ@~t z=j^lB)@tnm(X4BmOtkLDF)CvRm>{uJSP5>n6{XTpgkcv`&^m3_{4K7pv#I#91|ZjYc}t4b;6MVh@zy`#k(wc^giTI7cgOHJwD-vSPVz_0vQ z2wBagXn1(-2R|=b;vVW)cfT`6L{A|5sR6vjHt74^j`&YBrpbRBOBhDSs0B|rWM1b_ zOx_}p9^Vi~_q*VCU7qG`m1#6la%Sz+!2c0(Czb2S&r1^e|FQR;@o;_p{K}7T(y?4WmA)-a^br>xSGe$Sc815~<-*uhq-2ZdV zeXiH*e)xZ4o3Z!Ivi90*t?&C=K8hzEsq!)kY^QC8ifNRLcV)doWavb8ZGCEajIo`v z%Z=T5|CL)j*_@FtZ5qDj{F`BFRsj29#)^C_g-Sl}4;{vA45mq3Xk-1g&MoO}a-sI{ zG_(Z>&&<>e3{ooryoy4nX z3es)Y#mix5=Bj@qKx5=JR%KqtSi6K3y*w|G4Vs`KeFe6wY>+XY2kuQsImt z3q5Ur>7Lid7x$s0Q6HB6SsUNaLcxUyMSFUn%is>=KZ~#I&Hrh&@ta784rH(z4(3Yc}i4 z%0&Bs%bnqz@})Q_|0jZ`qzuQ_jrtPr{GZ+r*)q3y$N=%#D*yGBYcjKePL@1%ebXT)7_nXeMykSHun-zb)PUS)~?sn1+0!7zJ~# zx(i+B$BJBMFHW&yd?>LPmwFxj(%9Gw>yz*J4Pw_RLX|p^Tb7fF&5fPeGq}=C$8K;Z zqpZaq29cnVR~PuVzymCE8p^lp9-8~14&&8=)6 z4pZ=BKhw&i6@cYeABwicw}pqn{*qSyEvPFzgZ`rjl>j{xVkhuI&-^%K|6Bg>(i>mb0u!}E z6;79xsAWH<730z!=_R!JUX8>Py}1^Z2dA1vGe`qQ`r__;##0Di=utB6chd?UC?D_v z@SzTAuB{>-`~9#LiFGyFLx?`V)r>9d=s8Bz8O#2nI((21gZgOa)8H$#jm_yfHxJ2b z7h=+<3k@L9KnXce0gms&^;SrU&^DEr2i@Hhn}=Tvw+?Y~Xyx*#d(ohK5(jJ%^&PU@EgWxviP%}acj7uSD+r5`Z>8v`6bytnjTL zC^gh)#)sV{o93g#Z<<7((Dg5L=IJ&&!2R;v9()~^pu?V#if5*Du91E|nO5{$#hfWm z8~`E`<87=xZjmw6;LM>qjs=STekS`&Z+?5jFJ}Dz9hG@oGf%bczetqtyPN2ppmjCs zH3>n&?>Y~T`^TxoS8ne&aKExBV)AVNajoUOEOFk^}4%p z+T9~dGMoF=y=bLN{EwA|g-2j%jTBg1t^8URnPmoS`;wUV4k@0{=PX8>w-iu0(1;Bk zt`L%w7g=|6z4=ynzrF>wz?-@F+C=FhMEqG<^4H-#(LYs=!8iVY;~#HGg1jYLKhoX) zSNvf<9{=UR$S=_NYx*RuYYvC29OIv^JgBgP(Z zpKlxtnBo(emU+}%_r68Ac(Zg!+7$?sRa=-;kH4w%A2=jmVBHN=AWBtxK6S0~E2}gX z+&Wijbz4;YLkv)5PWNsYD_P?EKnz+K3D)U6hxdPuT(__rd zO~te#K=`8MhBiCd8f-v$Qq3Q9E#0Kfe;CIoR5dED^xNQzGhfi2_s%l!OSLNLeu>b$ z4TC7G)(@TkfFn%N|AZsf<#{jz+d`V@Ow*bfK9pw8ntlMeI@0WFaQFo3%cr*^t|tN< z$G=-_YY_aCppZsp`nh^YjtdB*f=`rm!$_ek4e zbme#4X2FU)5Gm$NZ8wTaEghG&$_FaFI((;7t7#ZK9NGT-BFy~XNj;ln$$U=S>R^uI z6p8+yiAfkOWUKAD)w_VXERu*Ni26(gpj%wZ2MZIHd};Sb0}d8*>sRACIU;0mh0M8+ zC#K2&d$;?IMx)s862CcGYp7X&u8^U28uniFSshHMWO;)`mluzsSh8k7e)Sw}K6?w( zqjl~%gU0P8)G?~8e0ohMM_8anM9}3=zk~cLli&L&{j#R_q4!VNN0sha&2HHqJ}AF2 znA&A%hf}Or*f|(7mJ|AvPu)W^L;8Fdwb{5hfJkAk9ySIr0gm{Y|Z(B9t8O?js zB`V82Y(Z9@*JC-~+wEPUoqXWr?B>^`^Go8mf9BZImbpa0Wa&KQcGeT!-aJ@q!cIvm z?#sFQmI~$;yL3k4V98-2vVQN*A&%GLx@#F{r_4QW)BKFm4xpwD75py9xJ))6S?vSu zkpU+>=^tgfnF;wFX8#MU5`EYseTG#eKi&RKY79u)aa=ZqI2*~x?Y>Airi3kj+=zGT@*~0$; zsQw8A4I_wnq+Sh2b2>TuDv(@ObYjNPD97Vw$qc4nNt*4j{Vge-{wvwS(%~d0-lVYO zd(2<9>I*y}>M!GSIZ4prJ2Tm&XaLXVkAISvp2aV_UN24-LsYCTTQ5oGf2HR$`2w+U zb93u~DYVD_?@$a)pqxilj8}CCLdsQOvZ1hZOa{%? zb(MazE%{$;vOm9uepRRyh^jqEV#j_Yo;UM1e!wCPpM6sj&*Q22Le%1&))(VEoBvJn zA8&3lr1qsl_|e>428>Vvq!ylmn5&o<9VaINp8@Omj{K%4JD&v*chZn$WUQJ298yICku^(Mj9%S*20wfqWV$Zh=9>e)WU=ou7C3A{EHbBdjUR8Q&H*(+8HIQa>8a{DX$7AO1jzq{Cea1pk>9TP1BEE zA^bVgpxY+G%)oBrkbat3; z8Ar)%Uq>V3ncG1f0iRjiOxi0N5%#br@f+lk>>%!cl$Ove{10t-4b`uw80XT7@_CIr zLoKClD}%d^rm(S?@E-qo9ASXnx z3lC2_+tVw-10Vi9#B!5&vZ?x?h~)wF%jqtmo2ThB$|Nn7mlue{kdAWx0o0z5wrk09 zy&IZp_;cB?-YPHc%lR=kOTHAItG_y&mihZF^e)V`5N)2iv{@yH`aU~yW`EJov?LsW z#;p*wu3zdieb?wv`pQbqhw#+RX7`enkfOtQd>e(eMa2G{6#l^6Be>=Kos>1^AJoXE zw@YY`xmeHi9|IMqbP0Pbx46?Iwb4ASIh?YG7pt@s8Nfi1aL&2WECsq1DKfvu%9B?< zy^V~d>X2Cl_NG7uJ0L!z^v86)8-!cM+U%T@OxGj&?AguVyGJ({%^UXRd;hs zWyJ^XQRS|GU(oRWXNIASiJkN}f>Ax7;TFX#M;-5RK+$@fFY9WXs46o#a4bicSdQEJ z_EQhh`|M*0@(EkG!Hz87I2c;uBPnpuD^`*A%0ZW@-apQYZN{u&V&aors)^VW#=i6` z5kk)-KB|tsoDA=i^Z<-0s{%%HY`n-9)0~gy2F2UC`VL?&V4?b(ptUhT&6JKDGYv}%9Zsl zNjg9e*qzXbe~{lOX&+Y8UAz>XcV0#i{99D6ql3F;bB#8Y?dxP6Bg&vSKGV>WS$k=( z_M=l-3mX2yF|SYc+f_@P@Dd0a%9V+`rQjd%kmr_>;l6HbBL(wpsC?nO5^B956t?`Z zzT`(tznuR1eKA6I!bW}X_uYK#Gps9CqAKlX^v^E+THXbI=Sh0k$dS>H(9)1DIsN#! zl*NHir-dZpWl@L9h;tY6u;ontOn2QQO|J3KzsGyy@^Wu# z_+*uh#qx=>auM?66zVCosi94V>Nt(LCUyQ@#yy4y|<9H+h`xJcPY)3IO^Dr zn?uXj3b9%6k8M9aW=cQCN*N+ch8}ou`g_l@=gerMV6R?${QeqCF)Bl;aPQs&DJfKp z!mbu;d@-!QcJ1v zX@MLL+#2E%eEio@WvMZDt06b}n|YPYV-!zV+TGZR^N%_VN_ z_w*0%yqmigi{81?6T)J+qf<6oZReovh^g%hhomuH{q5aPhUPNyeXg1E0i@3vZiuFC z71ONE^a|?<0_}tZ|FY~yPVq5$NvQ2GL%Tm4<=xnsxM*<4UN@yIVt<0#2MM_{vFCD^ za}377dQ8_-y~o*`l-!};pkDM0zK@$O)~2B2TxJz!dvhQFGnYll-%4Uh#~uQHFn&iw|)Kh_U{RXiN)pd1fRf9BvQjic?IK&Ub~DaOX; zy9`~OxiQJjZ&(js^N6T0LS%En-^4OO4l7VMqs^V%c7*5q2fyXqe1LigrKU8Lv$M&Wp$;*a7nTXLX$@`JXP0*83TU}8hiKKA5R#jP%?hC z94pRA@Lp?};ReZjtM+?tDtm1!GU|XjsldF=hZ3H_YXyf%wf0q2DXQ#~nGyD>O|l2{ zd*8H&-hD4;3hJgeSj10+^ff>eV?KX^*bky$a<%DVniMkx4`-8{kNgkm##A_>5@y`f z+7nz*a$f!WcdIl@gqhZ^Q|o#g&~S{Vse^9*?Cy3d%PSBvJL$2iEm31kEZ7}-*le=* z3s3(rb_wOV`?8=?P7ZLLNSUR6LGiEQRTLEL$gyDT_xyl%wZpqyIYZ{Eh&W_MlAwm6^^>SZ+;@ATKsx7~Yz!iL6=`Q~kwRt}82v@I=loHoKMM#?(D32-nE;A$)^s25< z5-=FQ^85a~a!YVYDX0C7F(Zfdc5IyLC>2`NJs_bXYfb_#X9 zF-Y0RamPc-+N0FBC?+Lshs&qH>Q`tt`Hzwp$Dfzllgf$~>Uy(Q3&!skI*U+c@gHk~ zH7w+Uk81+7#p_KdefJBFkZSqnt5B^9m-?iDU49l8R*ubfL)GM9H9w=?dgIfZCuqa7 z`E(ahZQOOCWafKbA5T40eqTXR*-`yHqR%SfE&I5Y>Guj}`slPmCHPK9_I=jdJZ~pxU^6vb_!oW_A)(~w0aBsevE`hs$k zGV|Y@Yxd{Jjz?6>!fBO9FR+z!>DUS=gzT{j^SE7lfGkHCa+b}Xk6pj<+eLWDi1SYtS@nIoPOunY8HCQz58rh6 z?0%Yi%~QA&k?D#ZjKlT`Rp1ncP*&EWV-CzIjx=t`v)}2%UV%S0$rs;_qEWNCBV@s( zYrzx(T6j_hY2=SQ{ZzOmdH0oDjG9UZhr4+fT6-&&#=dk1l(T&zufLH#dQwZ=N?XwoV!qnT%ydU$ zfw3gwuqbG;q6%%d^>c3E>x3jLWu&a+Qx@T4%W5VpDhK=YQb%gYGSo^V)O)&Nj#>`4 zD}K?tL>%KZ#t!eb0JR!MEO-TgFIw-YA5zDc8r z*Nz+%lzuOHyJnb2m&y*Op~k;&buk1kru!v#3tE#ZbdWW8H3GUg1%*RAUVi4UDxh{~ zBL28G<4D%pVXpRIXgHQ$)Pm$H;~2Q{aG63fJNda~;4hmHc41v|efun3m#c2`j!;x2 z6Sxd@fiq@31y;P7c`MP)edGPy(@#`9l((Je<6--? z^?gYg@Y7G?V*F*%fVcTn(;Kun*n&+pL(gwS+o)co8(i(cShIc#iR{P{S}v;Vb>$y( z6&!P|d+3CAa66V)rWDC?#}q6TGE3;GKx*tOPtQj*o0I9Eu0uwbEc5MWgqE0Tk9wsy z7APiMZ!(oj=5}?i9*tj&5<4=U@$L{PR zl9Cw2YoW?Y5^k}E5A%o_k3@Dnw9U-Sx)=9F@KM}OX$8;YRqtH|&4%%EthX!4O^!F8 zXe88`{9Ip=a1!Htb%^`8ZM)_N&RMNB>_`|6r3hg%b&|HWk)W>iY~<99Tva6W1+eOe zWTcspftCZN>-bL<1r}OPGSBM0*AmB@@sT{yap_ArSNaELy5S@rOhTOdDQtZ2thY!J z+JR^rNFCVT)^1@H#WW(C!Bsyv_$vLI z_NUmFc>mX@ch%(y=&-OW`(u-bC=4^!fm(b{^-@j zwj$)2oZMOR_aE{20e9>koa(~fPLbvdCh<~GkZ7OEpRac2x6ss#iQ&P0x{921aLZ+q z5P1EAge?VW?mKkz=FORU^Br^bDC}(rV>NCwcJl^(Bd)!Mu_nudgoI=T?r2?oe|otc zC@QO$n3xCyEKLEQUE4g1E_^m|_Bq)Mxk0SB>98C1Z*>)Hz@;IMh` zy(n;)dvW3H9Ir@(k-1nnUE7{01|39Ks{MHm`?@SEawi;{PgX(o!h>mT5AGW9vGlLE ze5Jt?lBOP454CLxH?I?QonNvY2hv*85KuVuB1ZglS#y* z(}_mg5sQcDkKjAkdnimMgl>#1W@kl299+H#Ig!C1T6D}_9|_Vs>nuO+{;~n|(51cV zY5^$imX5x@g8Kd^b@f>oq{#55>2RLf##2x`fBufet3Qp$$$YZedhoCR(2tYhw9$Bl3%Ln_zz6n@ux+Vv-PKUmLP~O6qga z&g$e<(t{AnFD+FKs4_5ZI-(q2j#90_Ns82sI020yxy|}HPqIoSQHgo5yhDeR*qN8g zcZ`g3_}#>HdJ!hV z6^oAzaS}BK7m3}1$H7g58A@fNKRz=~(~>MSua}D&`Y2X+glYflPm+wX+ecNVj}LL4 z*}-yxluQpsgO4rdeA_PG8f1WI6fE9XRKbZ~MEO zCLcaq2Vqcyq(4a|%$RX6z0IPMlu5vaMY=5zX3eH|q}s$uyTUq7(m3ELqg0yqqMhCCjP2|>j^Wj`j zT8xo6mVAXlbkQdkPisg+RO0N?(=fM4N-8TW&EwLnrH|fUq)6FvKdW#zV?Z(}5taEy zV)9EP;)y173C%gnF68j*9gZm&!Zr=q%g%{Brb5_HK2Hmwii}x?V5Y1p`uf1RAMX+_ z(glAA%GplNSO?o`4N<4-Oz|THo(gKKc=o*_;)a)$UJhiK9vct?5i@oc$RkBxQ=u0r zz9U6@sCjA#3}zA%rM+4*Bov#}pq{7?cIXF{adhmHF3tKy>9aS5DZ_)Tm8;;%0 zCyiEA$}%Nq*YWoxr(WgME!9#~4D+StzAEeES zFX3-nmE{>eVhIPlSLP}L5I5ZWRPTq(S&*Y1s|$$go)UhmM8aFAso=PI{9Kvm6dXHd z?QbF06lyI#gDJ%{_AhDRC^p(WeBy#2T=P?SVX&WSpXGs5QSYt>O@d#Nr>56R@4x|a zyJtsYsP7FYq}4IK0ok5BR>Ic~A6A7Ah^~h-p^()Sh8>^KbsM%hS4<=_wzo;AoO9EC zi4nC3ESD3~`-!eenzysYwEzALZ2nTfj>mo5!oujLz6%gThGYAHT>^vq8Y!i!(lAic zEA~yf9#OZ9BG?^joRK@03g7?apbiXxj8eFH9XE5*chmLc{Mo>#YK!fSdAg|lA)8?E zK>EzCE`xyW7dv{FsR$((=mr!YgwEZYC!!Vg1C@2qJ+b6~2S)I6`u*R?U@fF4jxOsp{8!oDsr&CN95cz9rrtw2ab z@?Y46Ls{gQsqvxG%ooq9Dl_S?JxMjU_@h$kek4lWpL6{A+;{B@E#ohzr_gf%Cy-Z8 z$~2t5n(EzsRb|TR{2TqyD=I%4eWmn>Nla6WMG^pwG02*9=D~l+dG<&?T0|bJI9|X~ z6{6N*AUCnd&Yvf!92FJE|LA76`4UzS+Yx=U$#ch~llh^rgzR0@n) z$&TtXb;eeSLN&qeAmvArPpair&p5&G@ONr)*PnrD?RD6MXMCEgIU2#O6CNCb-4Ndv zl{s!G8(Hk~qvs_%P|kJ>h}`4in?pQe5f(eY%PQ_8Oo)vFh6<()!v$r;KI-3r+j*Gu zZ`nVLJqx!^c)Crke!}dKE{kj_&kzbxrt)WdljlW!Ch`dRptB9iTfFxI_1&}ObLGz8 z3w-L`qw~P)44ag_+kZAVTWte$z#IR0%_pILkWUJ{dspT^?S9muMrp3ob*}3reaNQ3 zCf~EQ{&}`D1;&^hW$Z)^yxJA?2d{Lix_K;6#K|t>y)_=Fq^}>3KTPB6#&f-4dr;qh zT9D@B0-Wg{UUzY?C4@TJMbsEjU-ESLDML}p^l>tBJ<4qGmYq|DWs|&~$YhBaolX5+ zCeiMtI5C#qe*S*2m5Wcb?<@0gHxu(Tw>j_O<+QtcHfJB0Tz3J&I79~o%crE=@jn_) z_izAx;c}Y69FO}e^NGJc>0TZC=+nKp(;NEBzZnT~Dua7N(5L&x}AT*5=R!% zSjLNIpOxPcS7%eGwNqIDww48*o7MX$SB*#+-J zCE;-(+GZ40j^{<{&EtMfpzW;($Gn^E2nk%+J0eV0^h}68poJ^+&v??CFYQG(?4x|w zOIDog=7XODLHu)#gmnt#z>cUM90*+}hJ#S~>DjW>U`1$S5mTHc1dg@-pOW0;%*#2CXxD6SA+j-~AY=gb|40)5Pt z?@99lfu2xO3O*godgl0&+ITeh8(dg=2p16W2-Zxg2ed0w3;%iPIy-z zMg@!`&YrQ1sz&?(>3T_qa}y?<5)(~*11ukPS6fal7<7@upOGL#V!QOw-@F>~DK4`f zb2ilsgkL)|NdBA2>0eE=mAfozc1OE;S_`V@g@rCaGQ^|FQd7f!?;KX!=7A_l=wSRm zJwwxyB#hOf+uz-lS0@wgzFVAoXJBgKt&XY(mSj#UUR?WdDxWFywMk`*@{)snEr>Fs zC|mS49mq= zCt8WAsqj;ZSu)rI-}#ojtaZ>i8K8eet2Opzg54Q^$kW-n-phE))pj`L6(xSz$^o_T z(V%8h1p^Xt82#C1wsY?JMoel69atd^73P&&1C$=LWiK z*2WdJ++Sy=%oup}AN;}jk))o!1XU87d-1~ef)A5$t{ zHNTY5=cg+jT22Mkc*puaCw*(uyFN~pZ;PgK>Par>e3lKG6$tieAGc3JR!Z~kr;7MZxL=_ohg>q>T(V(x*=vZ$4*i zd`mcF5L#`q*SMtRyQgTyG0&-80%TwbUx0_A?=EmF&c*0%%SQu`dHwzWYAyYHxMsLr z!2mH}>m@r`m6b!lgYi23GD%58xtiEGL^uzdKl=xqEyD!2Y}9V`p%+Qt#+R#Ic&n4? zzmThvJe_m^L~7}b5Z~yQW~r)^y`{Kt_T13i9qEW*M31$1oDd0cmVAD>f29`xm2tip z{)cZQJWV_4d1l4!FVk|@uw<7l-J@$0ELj?T3Ny0fS#xD%6IfbfQ!zxapywK%w2rwSvYM_PbKZ!V%@2d`!QpW}ERLN0V$}n6#dfbA-(6 zj{lPJqUoUK=+p;3++MpE8&~hP|K#tM<`yy>VwKgQIn8Cu@=awgIwS_>ss$+T2&=P` zg^vdA!Ct@gL|(MFpQ-ny{i~UAfd_5PcOiy_*l)+14_KB>asr4)>>?uip%#+4!34C( z{>LdnN|k?U+`qjE6puK&i*F^mCHH6rtu234iU!wjd3_XULxN8F30*mC_$XiW7pmqnX2IW_`i z1cxQd+S47iHSLoam;(k(h3Oy>Bh_qzbXCUNSpY=4!>&9KVa_SZo8834?4 zYWTB84CJw}vWku?H|-8730n8uj$oEVu`jkq3{PN7hXGhA=s5B8&z-!cCXwD>@9YCu zy4;Ni=2q6$7$EG#<@}LVym9XC?(Ur6s7gi@+uytA#6`;oTf?GgYpyGUjRdQz2*>b0)UR~@2blcqd zW{K-02Hs}rUZ}tJ>MMPQ>QR2zfc1@}0>bh)p8H>I%l~!d`v;W#zZd=w#P&yArV63)J^w0Vw>{(IyOxpBpeE!2a z99hleIeYEXt{+}OlTAVK@%&SBmJU003 z!dtIx|C%3&k9y}%dD{^*Ovq~{V}peFwXq7l$r7a({$~cMR{um(U|`;RAGI>zqC}_5 zBwI4S)^5LGLg?GP7oFs)#aA-EAf5-dU2}q&U-W&{3U?$8tV&8PWG1^gt-GP5WdtT8PI%i@hFys zr(W~28Jq-R2ql9cU!49>JIToNY$yplp2QyYqp6Efvuh|UC6Scb&gCb8A^MG0e6EmP zt86F(uYdx*G!f?byh7_xesn|&UUH7aWEpHfqPBmUY+`>LXTo(r|-lDcm&*CHA$GyG@>)Ab}ICZi*3mOv#Dd}Q!i6jLKiD<4UpHCv~ohsi*R;2gUBObBk z7ihp9vuS`FbTB2Ay3;yz&uS^SWnv{w%9PS)ay3?iVjFfD?fv#MG-r*BOE8D0*+rLj zHTycFwD#tj9?wZq*YicfxbjbJbwhQ)#H=Hhz2Thrh=I>hQ6rq))|iiF{R1|o2xUsj zh@(lRwxm5A-R?GgixIxg&qg7AgWAsJ=qXFWkGq??tLTm6C=*dUtnsuxN;$YJ9=xf= zV&s>1L;g55i9r>JV5Qsbp1tc9??Tlz=CD4!$Kl3yjs(Cqn7V+?g`g?p&0@ow)pn&)rp;jA3=?X4|WxCdil}D1hKwt0e zy?(jD#v&-xjRt?^P*pOaz|0&=;NmhGm-eWu*W%0A@AEd3uX;=mEbA%>xUQbsq833l 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 literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_export_cpp.png b/doc/qxentityeditor/resource/qxee_export_cpp.png new file mode 100644 index 0000000000000000000000000000000000000000..96c56cf90d3cd7bbcb151db7eaec716b26d73170 GIT binary patch literal 53430 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
    p&<@e2`pc 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 HzR&*xVV3c| literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_export_cpp_model_view.png b/doc/qxentityeditor/resource/qxee_export_cpp_model_view.png new file mode 100644 index 0000000000000000000000000000000000000000..9b8c3a732fb54eec415e7372b8de8fc8cba82bcd GIT binary patch literal 45145 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 literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_export_cpp_model_view_output.png b/doc/qxentityeditor/resource/qxee_export_cpp_model_view_output.png new file mode 100644 index 0000000000000000000000000000000000000000..86e21e2f64f586d449630722489256a9efb95735 GIT binary patch literal 12815 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 VNhLF%48Q^)1zA;@QYqu0{{s4>zOVoQ literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_export_cpp_services.png b/doc/qxentityeditor/resource/qxee_export_cpp_services.png new file mode 100644 index 0000000000000000000000000000000000000000..6898e798d68d4539e48473ba4a25bfb2d7c39cc9 GIT binary patch literal 45595 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

    (jjjn<$VP z9uU)~aCURz!~r`G{|VrRyZ!zF4*&a|^cNERryv3dyPum=!zNfUVqnW5q;3z_EOtOC zKLN}F+&yLZ@z?jbBA6nlgHPai21%BJE*h87fKVgn@%z7gN}0d#?q7m?acR%B8Mdt5son3dbIrSOj${|KY&{{(yhprQ`D_PxL#Qaae6woK*B*c8)GiLs zk~G+JE+RI}fU3I9n$wvIJWcodk_wb`Znw(dg97Is6t8dJY zp_|Bc1ig&Jr2|g&+0LZPLiE|3=S5@m@852F_5_=3!bEnut-E29PW8;wE|6j!a;nL? zi|sT!g^RC+X{|Q%mnPc#!=e_|JNhZuu}yt%lBV3acYXo}(jBn%epomJmtD!1UC!5x zaI5V;F+f-4exsqT7+{`OHYHzwWeW2Y{c_nibLOG{K*r|hg2%;kp4?XUQr`K3qkM$K zjtNzrCgH?<)y4i|`is$xs$A88!DaI#d3H0G^Z9q<@f}y2yfr7M^~+moCd+BiX2GX? zHj&xS!s{vh36nx+p8|H%2lU`a4SEY8FpHsHlurH*w}I{9*mwwCE*#iE68IVN{JY&a zq$Pg04w!Y5-XGg2?n@-&qbq*`Dqf+$0KF2s z9bWZ<=Y`_UvEqWuau7cGKytk;@9_ev%bq-&QL++9Q+4bx?w((YTAW!SE&Zde1Uo!E#opSR2-m9{c{W17OS5NCtO;QR6UoNMa#1tD*!s)weX6xlf!52yM zEP#c2%!YXq-%;2E+`0r8Sa=T+sCWdblP;t}c;7}Lp>b7;W$*r#a~T@eV0Q#h)8UNe zu4j6>w;lN?cuW|3QD2t6hYGqwD{xRV`c>1JL)vpgK|#hz#`wd6%L2Jmw=|JJa;H5$ z-d*4r zQH?>(T$mt(gIt|xx8)BPZsk!OUEwb*EG+u)*s*v*5G>b1|GK4}FH7GNwxotMdDBGI z6N*N1U_1+3QUumw!w>AwJ z(ZaypuEaEm$CjBiI=1qogQ5F+*|?ATNuGq=w3x^vo{m{X>qOyOzXEcmQnwr~j6Yn;bI@?f z$~c**^!t)goH;lfPQC82dhYAY0l^nN=sI;c`Q@BdzV}e>8-OsyV+3G9df%^W^qZf9 zPh3S&iw%I!?~jX-dyq`?If~&{iIFR<+D8;WIi^3ibM^3}<@DBI@;-QG!LEpHt?G~G z+CY7D`E2ywofoQDBUBH}okgCu!Ep6%U3BN`b%8-ifsxQYbW6ClDi^;1b?*VF1oR3> zxp!}r#bN1rQe1#{S8Dh-DS*o}V{brkC0I9oQe3OqLtqEl*4DZIL$aX#HzNNR7Ykhe zpWgewTigHq2Lk_lDM>Mj;Wu*cEFczGF~qS&&>QKdOwW#qYW7?0fV6&Ch=`>I2t@tQ zAfcRC>2Hw0jv?raI|w~P^00uEh@huC&&9FCO@QEl%Nsx<|AQ3spW4aphbdqSa~(kT zz%VnjRRmlPl9d;DpYmw;a zCP*6Y3m*Haw@%{XL~_h-jqSW66wb|%r;s8a{Wld0fPd92fx&}OuQ5x^Qh*NdbmH!g zWg>O%HgW#0NMB-B&q5euqOmJ=IJ2S_Iw;?KMEhPa%N8Z~y`i3LT!9(hM+`AzG9f<86!K5ch>eokXqf*xZ^ewk| z2b0x|_v`swu0LY=;JC8=?URs7$J!uzuCKgfp+G-gwfX;Mnr>Z*j5i=|0c?|uyw3c8 zgrgv}wj>cKwa2k?Iq^d@uI~BA+KWG`T^-cCEFe(G=(3`NumeK--b;pKYe1scKzB>w zY$C4ygXK_A7_!%Ud-kmv5bgk)-{x=0SbEz3AgCz40isKH|HoX>N*xpj8yeAc+X@ zRfaP62YbJsLMtDu0;w2KGy|qXCa^Yd1w5IQ4&O~|ob=g>=A(*^kGknCB8-2N023qW z3lcG-72cG~=w*DYE~+6Y#ag12rm`hvW#83JA1_ts+u^M?OCqUy$|A5m1AxY!#~5{# z#DXM2A%xB$+#Gq$?SI8Oy#+6u!&=hD5eX(CL0Tu^HM`QaJ#%{@Opp>Mrh~RW2`%R_ z2K2+gPnq@L9aPVMR5A1is-vC%S8HD#7iG7ujVJ~vNC>Y|0wU5%*8q|V(%q zN()F!hf>lEFv1WL(%m&fGjzkyd~1B)z0cWa?|shiJKx7Y%nu%Z&sq=9y4Q8z*LB^i z?q?QNEqRlrm0eN1Kw4uBI?zBV$Dzged0O&kTIcUo#ffo0m&O=kv%>g%)EY zS$ZQ7go$blBe&V>S~Dkn5n=q9Q2B#s0t2Srtd=D@SQl!(DCs$$^S1)<#!uU7T#GyF zW3nn*39Y@HYl-ScCzS&^C@62@eaAZye?Nglvf$}%xn2VkB)wEM3CMkr7IU$OZg4gB zv!5j^u3e99`JEWwL=@Su^%zE8M|*Vqu2)gp_%?(kg6vimt6AZi^b8yIC>UqeCV3-k zR`7e#awNnU^Usn4f0GN6Ye-*ybpQ_W)p(%2>i!TTj(O&{k_w?Id@bp(nyo`FE}F6$;-Eprl^E zoFS#YS}Fuxpl=ai5A>VXgyj>?Ti;2S<;kSnqk)Y2iWU}5LzHLyK6~@s8s@yQPDZ)Jt;(22xZKUU9*gyZj{fgTDH%TZP3)9=f?_(RF{pW5a6TY4w*`?>&}$5x~l_QxR>N0+69703!@d42j*~*8awNWgNX$@dlO1DNw7|maSLQov%qB#0X z7u6d!8F@#cO|R7G1eA}3d9;2CN2;tbjyBns%hVF{a}6&b(>v4NQAV&kT>iIkel7%n zOj^6w?aEaEYQaZaBfMI&2GPig&10>u(N$4AtyLfpI>Llv!>6VPn zQ{`04vSpT+roMjjfsvMR0x?`^q3ao!s7l+HfF{q+)uBAs*y?`zf#7LonU0+Bg6c6H zOYdD{O#r)#%N}$Sg{R)5xbW`@=b`BAjUi54T21`HGgaw*gNXWmzmw#}w@QoVo?+Mg zuEg|YJ>_#@gZVt9yhuxijpmLn`?m?aFVxtWtU{j^coQm4IPKmny1z8HgX-2;-ur%) zm+ams3oF%M{`#bdj!As~yHcHk8Fi)ku!p%}Upm<~c?m4-rc^$86%@3&*Q6087da?Z zGWQ|e5>?sv9`0LiJPqylc%?zRQe1^d8Wf1XCu+A})T%r5Je#+2a~$P5=)wsn+DTo> zMyDC%U5V|2yS$M)3~~Df%eIai z+&jLb3Vw$6#!sg`(=Hi5C4gQb_cLSGTCWVxrTymYn-It_q%dcWO#Ml`ZEEi*!pjVk zS5%O;LjnsuXriDUrWSn4(xOl^G%3{Knj6dA#;a-R*em$C$kcp@9kVA#;i8j#9QfMG zG9m8ms^4X`#?nbovYw#%vWn(=0x`jPs4&G=*zPd%;159sch#=ppFadbCbQ-(3cqVH z+I~Rg(M6-8lBm~1?(?-dB?$~yjjsqH&4M&y-jp}F4=EvtGEy_p8PwVJ)4rn}l`%+4 zR(MzKvZRX^GH>3F$zG^7FFokEwnG4m#mQY1I_acny)f}1k_A?>uI}Tm!msBM=*y<+ zJbOD3@GfWv1svXou|K;)3@IMbw+Ut2CA=*Uh~GAu7@b$4Q9*k7l|45?-Z9 zU6-|(;;gmES-Ius%GMcqo;prl1KkWgMsYXWJoH`EwH^ zL7AL}fi3q7N$uZ-g0|=vucJP2;pD;>oy^7-Zy>@{S+9kaJ`2*qU0$KvB%pu}#tA@S}nOh8`sr!(E-`oJe*vw-hN_|D(hgVgA@ zW-TTJ7jt%I@H@{c;gYp4jJUJkC8bFVMKfMRF{+b$$!>=xUDI{)q}0f&l1-W}VB}fQ z=P8;-ijJ{fky`D}%^<(gQue@{xyqp(P!mq8W&Ak7$B8W>@iX!!-Ei+Tp znQ@A|NJoc1KP##k>n(5S?z7--X2YLYpj-mBamkAD(AFEhxow}xUkzvmoV%r_c-|Mg z!{5)w+Uiq)B03#QVmMHwD9S$ye-)frJbX_m>spdUOQwBcOklBN=|@lU8HLJg`(9ZE zWhqVLibQbr#74p0@Gi0$n1jO-XlHVjw|xWUJ8P5@jw5t!9hTYxg_6GHc{0>5kXeB@ z-nr7)rh$`o%7T1^elkpbHJZrpalH|y}sh5C|4aY0+u`GSWBcbMM=*fZe_^zXY ziJw1VIZE{LhWF0PLMhlz6scej#f+b>z1sWsmvY}}O1h-)?|o`_OHfq9BY!bO-#(fv zVKz}i<+N$_p1i%kQJQfo;5uhiz=C`9zK?i;1dEeCdd}Yrit3zoW*O$cU!_W8aCG~n zVAJ@qKY|pfRJgLx@-M9iEuYX{t{1tDAvGE#h$T~BrhN1Y)1Dvw)o7ds7e?;nzw1=D zS(HEDa;=Lj0xwh7Ti#%otB3#iQV7YVs(hePE@(5c3jN#}c&FyGnys9(sonehfr-Sy zyuV0^^sMGxMK{HC1YFM)aE2YwlgR?h_RDw@X2sdwAzmUKtsIZC&?&r@$;ud^% zlYfXZBOaVC>DEh92uW#IqNEFNtUN2tD&qcC1j{BK_|B?&0^nc>IopjgQq}u8l&y|R zn#D>}l@o55GA#n+wil9o!W;?tHD-*8Vm3AE2|d51q$CAVd3ruFa?t>B?jCoQ#Cgeq z1ePeZ_A?%!S4+i*m$4+q>{HI+;Y```7Ko&^pdS$ERKgxBPo;}Fy`+7m?XCXn zIp3`KSA_a6EIp#aK^iDpI{J3dOF*&-cJT0sH@RS^kxiF{X1L93C*}9#_m)&|1aNrS zoGrX3jpKC^Ry!36U#P<+o&X-`^*d}q&X>oSSmDdUkxt4E@HU^mD{6J(aHmbTh$^N^ zS&9HHHI|T#M9;52;}fKbbr5To`?p zF>!9$87tM3<~0tNmz(`m#fu3%Fp(-IqN98<(e}{Ox@pB?n3qzhKSQJ<(=fI*jaVuE z+bc9W-MeGpT8Uk8)X_Y|0rAv;#Kk!`S`rPB(sti!GVPk|CexV4kIsbBjVivUeAbJ8 zv?LsPTB~uAhR2aGJ-LnVESyue@jT@#r8H(Axg=4{RC|662HLSqtVerGg0@jyxO=k? zz@F{XQGD+;_gCmq?u=04Gm-6R{WtfbD`jNvCO=)-!$fo=ru)#L7fl@n`s-SUSA)mb zXOSIt94ELq#mdBKtQ_2PTwB5RN%iiFP>(}K0Nx$oyGHD-?e{3W@}0MN_b(6_Yw)7l1M73CxwRYvx(gc&+JHg`+h2b@Lou(#|HF(2n0k!0}e7+j;IXeJWod*&@;!7Ug z^Tzf8$LebJ#Ju|@OwYX)%=BKETo-()M&5cOsovqI_C%q}(Sd7sOIcDwdb7cPR(*G$ z2RE(_twfjt;#SqQNGT_*Ql~`T`4?%={1vrqN3N*ss1)6eK6x^Ms)8~}+1mmRYAJbJ zZ#R(9b(F*tn84sd(T6Y($-df|++V9jhUCY{TvS*#Gr83s zc?wJ*KvA%73(_lSwhQUayiKR)t&qjJ!A2Hu(q%lLpLO5ZyOLjdP`JOLcJ2;*^u*|) z7ZJ8tI&~Q$Pftr>w!%)mfgUKn?{FEEl8dn<55YZ<8Y@5fvz66C3&@Ds-oXBN54hcE z0P?T=&Z6-jIb8Y8`HZjOI`5PObm8OtWB@uLC!*dLGXPEg*Cvwxs>*ull8|f$PZl4z zngh5f>a;?h?1RttI`rZ_b$`hQAJs-0`!O0DDp}4LmAojGd2aTieywYujoh~4(-~*- zMt?({1A+En@->|3PdEk~v+=O3#ePxO*wwZP3-j9kcU78|%tIw}zelgKUxWaFF! zOCcXSm&-)}LYDBO!N+NhgkK8@b{X=hlbDV4qEtM5FcloS>%FFt)GP4uiGuU=%)KVc zKV=y)j%^S00`8rd{2)7Gi|7?}(x;Yv5EmTgM$Q%DxJLS2!ZDS7$3YaIs^`Ktl#RT?-k(OJZ?p6a&FT)KgfvA8cEm1-#k0B3;2l!zTS&^JN43AMXYejOD>cs@v>5<}@9`7P9kh@+~On z();n=BDWF0Zm8-?>$&rOTX zSG0aT)(x99?~&(862@1?fM=hu!2LE&DvM$OC4RQ_rJit;o$Ti^WMmJP!^8@zO3^4@663;Zwe+ZP4jR_D50lS36<%#$2n_JtjLM`AYwk z^NX1<4NJJlt?qrtJJmf`5`t}Vl|oJ`PhaQR!LOR`m#E~~MqLomX#v*tPGxXraQV9B zrb^458p<^bJA4Ge0%>(Om^yg-2p@`tm|F=yH3tcBm8-|sSH(@N=`IvKEr1h`AQ6c% z4RRYMj13f_3%Fs*7nqHkt1enHvE8LZ$VVT$CfU)g$I#-dv^L{8hAz$RuKvUR>S%(e zV`9XNyRuBqX+26dn?&%B_2;7X1drtQ#B+OlBVtDsCU2(*`FV28ufqzOoH(UxU&r*L zz4mOFs%F@}?1(_v7Vah7s+1F1$cfAQxld#vH~G{wv!w4hEBP%$hWKFa6bb8TzY0uxw_yO*!=gj+yY&w^ft zXAj=`RNnl}0Nsg>y_w8ALPjoUij$iuG>>?LH4c~u(T;iZB^e1Fyh$NTBPMFeyh0C4 zt&k5Ra;1G^Rdn~@qqGwF1;2}uI*Furo27lpp~^+t^vMmGRp5ELi@!?|j!&J~*K2J6 zO#@@?_!IB5^M_mz#C}w<;r(T5y6bO`iUf7YtEv$+O=@t+!NMo&wwspMG|dG^1IKMm zu6ic*z1h63GgCu#P+JY4n?dWmlBS+uY55DCW6T0G^Z0JBXwU4Ve8lE)G|C+D z`kY)&tDX0gB?ov|(1-cV?zx0i%3~TFgoL_p6Pp%`#XjnnR2XOmB z!SPOI&qrRyja>_fjvU9qr@^*g)3g%T9cI_fUjE25($W&n5##SMdhh)nt+)UAMpesr zCQ;NTnS^bArGYulPh7Qs=ms}DJe&~2$R6>G?5v=EeFC06aK2soOHK-?4f5vO+HhDn z|HsNcL$CMmO$X2n)1^(nw-&I^h~ItkTU_wp)>HpgbHu-QO8U1u2`3#;Lkxq4iShh? z;gQdWAGLjAZnX-lDd=nitk;#>Y3LUZ1e5Q|Uk@c_Yk&9UIbS%C;}PWkU}gz>1O!Or zcX0QTe*H$_HzFt*J~n4E1Az5?iiFz=Y*q4DSUqoS$(1YfE$S{e4|_`{WFbpR>wrAx z{ivbfWr=*VKO?z+Lc(Odj;H^}fqe5W<@GPnz0y;=TEk_AaYocb=+}mBe}5q2Xj1Qb zu#lBvrm+pBxkMXj$s!zf1oIc(XW9J*bEn}oN0A&Y?>GbD|V|v;)jrocyM1cq^_>_Up3eWZz~r^9$s|EyFS8f(%vkfAHy9 zQfM&e`+4mg$kFz3L|AC@3yK1IA`25U`8w!Bt{ojQBf!A*K%nQ7{f0e-i($KT;I~gN zBRxzK@0i>r3oL|n$On(?PGcocHT_$Aal^*NufZc(kOF8NNbp+UC$5lm`@;1D`O86I zGZf^DTtek8dNtkCBN5^lXPha;`qU)&eh;GERynSzVz7hGA$AAwJK;|%O>OX)=#Mcu$O_NZvjX%MX19akztSONUaD@+C-sCg`u`_y5 zedUhi3-$(Epv*&A%j7czZ?4f0-_V^mk2z%E)lS2X(@ExC;SVtQjLhv}_I(R(Qaa(m zR81J!sn^%+l4kM_Zg;xaIy_HVokDWC7$DtdCZd-KiC($87}K&stD2Xk(9_pA#;{>a zi@(QWlBAuq-fG-GZ_YfLwrExHrUl$!(I3?29qrY9ZWc~|E;vp~0w$`~v0BIGt-u7! z`{oqaTAmMy7?&V-1qB2Rv^VJ=aJ%bn4X#iXg!3I!f(|9Ne0y-`N?4J!-L;i{a)bhA zDROhbBV#wK%AiiXNTORk^7nETCbm{4u&6Npean1HgOyN*_0=w28YZQb{DJ~rR(V~J zxgP6%0HR5y>@&$6>A=rOi?{BgMAvZ`@&7^m{V$Y29oNfJTU-3%J-~c^uwmEepV(05 zPi*+VWx&PXbw@l3&BZFWJBTigZw|eB0ReZ=j|D{pbx=eo=eN>aH1Z=t$Nafo!SB9k zU0G5!g^d9{rj@;YZ$6sjl4^@Uy7hC%jodww#c@HFa&)}O?la8RkI&;|+a?{}4&<9d zfN52^ohDD;zuTQGY*%}<*yFXxoz*INB^y{1V`e`B&7A+ve9m7SNN4+`p7wMppxKVu zVZx1!J`R8p0#;xO^fz>o*8*#vRuNw&)>WQ|=m>hXJ!lUf5{uTDi>Pt*exIO!UvnR) zJZ+~u`==rQK4WD#OWWd_MO(@m&?pna@b292ADks|I+Txr@TztexaITrUYYK@@PPW=xGe8cU8gtvcefJC|xX5 z9Mv*hx5;zJ*UTD^sjMHE$BBQ4gfRZfwHtnsSL^%J1&9SUulIU?`0&9M`8Mu)mq)p_ z2RUz8=mXt*H4O28YIjRM1NP${Ev?cojln!xWro2<UP^81>-v8 zT9Sx~7S$U#2~v12Bll0Rm034HnJ5vKffsg1b70-(&6EtFMOqSs*=wc`dhd)r0afuD zbLH~Sch@YYhRB_WAd`^(wfSaXiYeTXl2Gj03M0ODFRn<#xqQ2}I{<0WEWr_hUOU}M zPT-)Jf7sVooPy}`#|ZWYy2crQpeN$jJ9dfSmGYY;jbKJj+FsuO`npyFDh1})1Ll}| z)M+lIPoAzLg`Qomd>rpOi1kK^Ojujf;^3G%`r^Ate7?-Q06eKIty$j> z1YN}y-6a*CsS*h*4|$2>EW-zZVS)rzSxWXTZc&Q8Tw?scYLY+kRy=s|gF~TsTRA=M z(NqjE7~*c#vm{>u6zfX}XK((Z)jV|%N4D&iU8ac7@|UHhCg=uggeke>fU6KKyEZ>LEL+^ur+h@BNi z6cD zf(aG9R3rP@Ek`ot z&raHxr#5aY-5TuZiMw$;2L)S+%Tv&XseXH~lkOzS(X3ZR@r#C)M7M0qzd zKGpPl>{_+d3s>-jPd$z~nQ%hl@EmuF1?gNld0lnMs?b_xS{odRJ4P9g2AOaf9irOx zczT^ujZ^V_7!2#LLPjJ|qwgn#Dt>XT^MbgFb(yXp1}@>eJ5u={AkNI?G{!cp_L;o5 z%ol%8mTFcUX+N#;%Qtr2X1%OxaTHz31at8Dyo|ZevJ! z4P5Wo#V#JQh1D?~!7E(Jp9}}pnReDoC$e}hrWkxc)g&#HVz6=)Q(iRj5?05gHa9!9 zW#ZGA@QFE;gRgIdpO~A~Nv$lGizaW4+;IwAs@x4MbITsEZ~iKc{+X~EE2n_GIE+i7 zT(%bPp#H#k`8c&N?^|^$Er!yX=LU`ns*KKXYR_VqR=uZ0v+Wm!&np(!-dtgMz}?~5 z<%_yEszSEHaO}yb2{G?O=nuu7COhk0Px7=YzYzMd-r@6J^b6mI5%4&0b_@Pt|r9H9W5|Xig zIi~RO!wQboVS+5^R9MspJdXvQCCF2i*>w}Svd^9hlWLltNBE|+j!NxLrXpt;nua8U zT1tYSvl_oU8P9S;w3n7d57qxu6$ZGq>@;!l#i}fUU5lMpJOIuZso(g$%uq2&YgrJk zoIv$QiqP~oNh4@7UQNA9Q80e#&W=HMZIu*9$D=WZg#85-a~=L!8#%M%n72ZtC*K7F zT*!Te2^9}EF;opBtOiR4F->8W$$JK*DjPtyHcU{vdRuoEL5LHZ(<^HXD7j8W9bD)5 zMRB53rEhWJ@$@}SZ;pDxTbp_=E)?nR9l&9Ns_0cjnqlh_4OO(Y4Z$E`7jAUz3_kkiWCRB_!XLy-o7PjJN9%f6zsNFa@meP>84>Vxz}veI+k~NVLnCo zTT5`m>=*^4{n>t*!D~Vu7RJ{;dC621Ob~FnKD|7@c+WA#bMh6X_$D={!<+QrVYPq!lf?adLiF!- zDgP>u^8F7PdGro!Iz(3}nNJ?ap7{aE4H((~sHKU+VGsIk(gVWHnEjQ!A`+ly;c6#I ziw8E1f9J=z-dm|Cf*d~By8%;ux&Yb86asdicOBw?w^J>|t=rhFcX?2dmr9epe2#S; zs6@ZMBGd;FDFnF9HO<;klQ7Dm+)YtLD2rO)G9jw~~D)Xa+Luy^jEAac&~c z4xpT&q5CL!AJ6LClr)tqrthQSl=~8q$paKt(H+O@;$vW{t7hIqI2Q2Ls8`RZ>f~tB zJJ=SeaHCHTU6=`=oh!ovzk5)a8=;o7`Eel zc3^Wt&gaK@CCymIqO;B=X#NuiKSrCOMIys~hMaHc$Y)SqW~blGd(zug-wzeKjj$8* z+G`4$vqRNx?y{(~M@lZoeP9GS?s7t8-c?)IwHx1*?tWaqzs`2ewPd1<4`n@D#GT=_ zNw?ZH$7Le?R*H6hqumBUgg$`XK3U7B4oc2a30Kv{xt-kI_01ed`ySS~Rag+Udy*8? z7fGcaCjJeLZB%k$?~0m&8;HBC5i5Oq9aPo{<06eq8|9#fOdXseIosrB7}_mIN1MMq zDeestQ-@u_(Vhpc-=VT#1Dhv;5t3ci_8_-qQwqwKaLExvqUM!GgJNZPb=lzGai1#^ zmfW$aXKJ8&Gbgop)F@0U%x8=87=4k}s^>VnT;tg7*V;tCGTvAfFw}JODdB@_i{d9P zNAJg1UWb!+e9gg)%O>(?a~J&@MxrXVw6I5K&NIHu0v1FtXltmMx~>LfJF9(*ZgNnD z0J;x(Nlk0f54dgd1f$0IC8(_?H0;K)<`afqtoek%V*NzCfWrDE#~{+((iWFXvJ)}-IT3nY%!@49NKp5h$7;{ z{lQ}h-5-*o-?T38m7Q)N>iCz6U*>dIAo$(grITR0&N*QeVvbmEbL~mA0k;DBk3Ets zvFxRN3ZL!l-%gdz8-I!N1bbD5*1dqyDDS=(G_E%gpn z3q|N$de`o;Kcod3QT%q2fq^Jn{5A*wmqSC+R4)llOpK2w4qfm{KU!=Wu(44}?FFB~ zU;JolCj+MOd$L@w@zD)I<00%$AwLjpnh!9w)gyzX2esC}e{5n4!lJvHKMp&+tpbeq zvF%Om4-3zFEB;Da*_`Qrps+7!s6~%85~^2A#p-lWGt8Z}ILfVAMRjAmV!IZFI^hGh z@3=_!h@=kd%Q$R`rq_EV&onXiEVje>ZgT7$+Q~jr79upBM_5m5m z>mKFh@DWst&rKco;mac_5ijF?7QVODa~*t_Hm}!vQ**kH9IoXQZhPC35_h1}W8R=E z5aEs*$MmOAe@`iX9j+UzS6)nP;hG11qojqY^_pGgTo?6n!`Qsqt^cs0Tm~~Js(_r8 z)hOx6)LbHaXh@g%w^&Ihj>%qy9|ix1t7*iy!NDu@mP0HX9^O})~l;A6-d3Ibfa<1V$Hc5YX!}j@5;!i zysOv+V?;Q_fSPgw_IeI#gmOJuKQX*Vxq6mV`{v~>8WfZ#-Nnreoi0}FB=GL&!TEG2 z36NUHest`7uQ+%#72XLtayxITf9S`CMWu~zge60Ovz2TC)vmoIZOyP(tH(1Elx6by z3UAL-w}Q*}Lwjr&TY|m@xIRpZc&Q*Wz`u!fp4fGrXzT!<4r*9bnX6!`F#&vPZAx-+ z6Pl_Ofcm5Eo%;7_Q)7|~852E6+0^t@qjcDo>B{=_rlu3^!10ZIBNMuL0vk?me~0G*y-%&+I-iem44aYJWk$M6Vx*V9E? z_@H%j=+RyuA{4V<>x|ij=hpD&+Knk+{zb>$nsDIo(UU#Gc0t|3;6XOWNx&CmLF9|I zl)@FIE4J0xXvq^^AvQu$VE+Ldm_q*pQvKbTC%x?K0S;6$`-Qclaq1YQATjk_rbak% z@mfYu-iR%q=tkoIQMgP+OE1;id{SGi;r%rU&58y$D<-?0wwpN76R|e>RcyWG5zsLI zj0E`}_!Ye|VHpU$T_iaA`2RZ{^FQLnOm8BrnOU7`3UpprP_3j@y@bQ|2*kcG)ZWjq zM?l1?H)-%Gn#pP9Q> zcZ6a6u9dInGlxt)9rZz;jMW_{BQ8!-4Si-B=IeT@_vPy_T1B6@Siw7RzleZ>10eL8 zi(1Nfl=sPc>S0i-cCFXZuMi69cjWHCELe0tL*(;@k7BDYqVBYVfi^xy-r^|8RZClA zxT&dmh_%RV?IP*08)M>O_)II4ja{zT6 z+cBkwgpA1bK+%Ubp69!l>n>(%jcVRnu4EieZPR-1{iLnlBVsxmFal5Keqf;2&U~Aa z*DPrVw!cV*)Hk$MVP#g8u9U*v_)N_tSI+vBWVqF|K8 zL5F)N&rO@jA1Z7{px8_=T(du{XEjoIXIbYe^POZr+Ka=M)sZ5jm_z|-4;qn@l4Y88 zTBF_LX^DrQEXEDsAXm{Ww^Sx8D98T@c>aFF8O z>vAhIl;~0V7`7@^0lb=e-_Xm;7Q~rNKGOcRGyasC;N5%j#Nl9VY#>`HRnTei3^P?! zP+(BwQ9jn;%&|_OHKBvi#+)GF)hu;8K+(KH~-Y^?5vBif>qIShb|vChi_(G z_%KQ^7i)U*i>&}LvOb+3_A?E#?cj{j`{$k;la+04&r9s*Lir8dO{cp~!$n0jVB4=z zwBpq(D^|^7TC*)rmW*mU5%;b zVc@Bk+p3u$RVn~HH3xxv!$>17_am%@*q{_Rhyy>e?UV}J*O<3-c-jdc) zIB#CqPM8Pv0_mJa7`(FOy>e{Gdmsq&Z8s>b!s_o>wuZ7=mJP|l~jAyF0 zn<=GTj<>^o*hpYB!NY)yV&XFZCnja3gj3labdUDF?q{(YncNAt6WlMbtNR+l8MA%j zY9Qt~Da7sWeL6{0;pMe&biN$EA(uoc=DFP%lt84IT8SC?P*?KGX0m#JYxl0v_?_jW z3iPJ;b0VhAD2GZi_~wA;qm87`j`61N3=w7v?*lfzmp5-7?x#eF(a;_vjZU@dw%Wem zjZgLQXpAwccG^9+J6)&nnTZn9fd5(pPyXFwN#%4PEW;epkB_aRK!j>ueg^-8|ciO%PiyteOqZ zQw#rev15SlNud?rv121k@csmDeeDW?TSbvs%cH_o0k zV3A_krULiLs{xP(H-&{_pf5qQp1~E6DtB??)_{@=08zy_$XCq9BQ;$5Q+vsIUXZk@?z7M6Nc>Gj()8fLLi?Lr#d3JxUpEC`&VcMaAcmdwt|l908eD2-v& z3V!E>d|M7)pV3lNdCA5e>#6f)YGLp5VLiLEy@T~z@JUrnkdpQ3X!B|zLUB-EU|BG7 zr%9hU&c||A|*?=1t2^eh^nYDX`x z(?%jO5!a2Rz&?6I&cd7QuhIj3hlYmeX^RYNJh~Eiv0G3PeqrsgC2_u1p)MmIyHV}r zpkY$Gpdwpg$3f~Dvx7<}7EkSfPN^jI&!sxCn=5E5bauQ?s|6-tf3`moB2n^OUHe;O znfxE5?AVoYo!%?3mo4V2CKq97n$KtM!#Q>t{NN=J}hf>h~BCv*{M(t8U<>AeOB1QC#4BoKNh z^dba8hj;kh|6TXJ>$~^;?sYBJtVt#_^E~s++2`!Nk6{{W3Iur6cpwmnK=F;NCJ1z= z69l?VaQ`;&NlJ#24zRi9qNyMaDj$5Z0qoqhey#c%1geV0zj%)W>_7PUM&AVlB5c3; zyVd1XXaNFw&?(Bke&=bjGmG!@Wc>a7`7OmzqPHZE-}0o{X(kZ!hQ>?~7{6RZk?qw7 z%eGT0X44co+}=X5L(q30O&H{trwn|jDbC&g`e^tIfw>TG8dj-jk&HkEU$y?xa-z(b zs|7CJb1Eh|3$~uP5BOwRPwy{4$Hm;Ouz_t$J@exi-F()A8Nz(g_%ujfD_<<;4M(0nWZ%lF0-SFrDr zkiGsm^fI|Jb18Z+%19877CA+GkI8A#`L-pTwfrqCVnCOnnWkHg`&b4H{DKWB47C~a z>VbI-2$h9i9TrGlUY+kIhXOk_AS~M%}=FDa?WG&gjDKyW*owAZ=Z13U-Szxx9+D#HU=*$eel97 zy9EM04TtX`&S8FAu;SukzpFL>tMBxe>)X-l*Zo5m{ot0AeR*5je7T&7AIi^I6AFf+ zbuRw)Hxdm-o3x&#rK5~sX6Q|t@lt)?fevrRwRx}WUoZt7XsXfa0S%*!WW%eMHkoh> ztaIkYK|8MsoB_Mb)4ZRji;dR-u3c`mv$HdQx$7ZUEVxzEO;glIk{>MXb@pq0C0IF` zC)(JA-L{A35l0da_>-lN{ow%`xp%ct0j8BWgH2xTJpuCyO8`%Yn$NpY4S~H_lSmbP z*1R7jnsse^hU4KbH@9e7x7cS@{k8a+lDpp@$W_ktQ8PhH0qk#IuD-{Ks8b)rJ;V{) z!1lmKRbVBx%nZh9CvjHD>i4b!SlHRFLt>cz-C6kcdi2#gFe`;@X~t?lxRp~VfQO{m zSDcdZgq|MmY2)hiD=njz8=OjZ3Ypa0l7Y?nOuLbGnGEP~mo@C4C7K~+7f2=4==?i5 zz3gY4qtlH4_^-*d9)Ir67cKJ>ACfN6bNWcmRN<>RII?~{gQ-541wT@T9DMcYp60sf zx5oPHMu1`Fd*iJqjp8|6t$z!}anadFV?4biA$yM;2YQ4MhrMbnP|54wq@didCcw-} zZOEobd|db$L?rP6MTwC1f}D?vjqaVj+FK|S z$&11Bl8TnqG)rZ<6oggLWM369nFE@k@Ybg+S`qv^vgX!FH3ciWV@tDH7a2#r`+sv* zjdJrJjPm$glGngmEr~HZUxmU!+}Dysr}%;5Kei>0pdd7n?N_NFHa zl5oo%53uiYf8K*r{ZWp7lk!Nz-A`X5BI7qI4EU(z_pY=BUZ1ZdAGb=c0--s@|Lpu+ z$T6cPWi^v;N97{L>IZj1fus+zIxHMb--tdd8xxYaE^9r#lst`S^*(Q@s_NT=3i}`6 zT7SLT#ieo5ce(6knm@mq&uo2FznE`nB^2@md6_G!{L5!51o|b%3ert<@sUL=x$}32 zFG|;c?-=igyKu0f8a=V>_F(2-tTJKcbnBz;;;WtoAQq(*Q<``#uJmX!(h1*iBdBP!A+`^aIEhs{tMhR;W80<F#N#?FF5Tw5D z8B5IjvN6f)l9TN6K~O z=EJA|B}}qN`k&P$P50kGXF#08hEMuGXZWuV{uib)s^5U-pV>ms#%8Q%H#OeeLp%>J>)o)#me?bj7J z^ggmq+e^f`&=Qb%jYe;PPj6FGOdQzk8kW1gwj;8!i6fwc}>;Fo>zICa1zGces|~@wL0)Soxc+ z&%%CMDH9fcERU6o-fE}bpU#{o{jGT?xfE)7m1#6Lr{gXlQn9wA`rd6^IWU5GWk+Q& z%)7YAgJ68*imt?bL3*atU_Sva=4QFw>Zzpd&ivcG*G_kGTv(@|iF{f(>9J8Rd(@u& zV|uZdt$xC{tX|(FcVN55fl~CRvqPJj1iPJ1B7u7So!#}|lf}|fn*ECjYaQ*Q$evcb zF#5rawD7s9<{xhf=`!rXd&U)ntrp5wQTk1Wb)5bTYLQ36xCgN2+LBL4!@GQJ$OnIz zPVsB?5+jKsi}Q6x3rQwV0`~*mO9pgD#6NYL?m=!#71r5} zr!g5;C0ian6MW$(#Tk+O>e9@z*b}TfU{}Fe1cS(3nua+ttjLWtFX5Pcnbn<*(e?9P z*Z0+m;WOD386EahYw_T(M+D3!Y6!j7Vc`gqk4w^276 zYhHqS#dU6p1FCa3e}BfqXQ _;pT85xBrnIg~UAQyxGrI-L5CY-h2)btF>me!!6X z8J6!JflNPFTLPy3gU4=Pm|Jv7HqHX9(v5zTwFak@eAV3nNq(=W65Ck-f&Zg zAs4u7C4ZxZ?UjXIX+n{~^EXZ*Oh>EQX3+ZijnWkYUpHuIcNSA3=wmzR& z>JFSyH>_mAbUjNbG_5(A)?&iIX*;bEVdGi?B`TMg=68vGDlj4tAKM(KiP%8aa-OD% zx3W<4Xgn-DmBk5fA3wJ_uZEX2y--mX>s|12U5KYfb5ze**%JIabLWv++TPPPI_k61 zF}X^joLmRQfd=2O{~iw6P-NPJU;O2Qmi@@Yo+)wx_*lgcE9b<-oeEd53sO1cNP1mI zE~)lg54|xsbNk>&v%YMzMaI?}PT}`q<7r|ZVjboDo=#t5DwM3o9GY2&Oukm=4+t|d zOa+2WzjnyOeHB@+?J82q;De=#Z6p;Zddi*)NS?1&@!r|PGDCyupSza^L(Eeqgitf@ zy(GT=!mMfstl@w!%6au#Z7a=3uSF_&ri@QTzi;H@wb&KXp_<~0`4s2xZWVE8U7M#f zk64vA4x*hEvGaq&lcdTdW2Cmk4dE)H>LrFxMvd|NN^nQNG)}DVQfo~J^1r`FBl}+2 z(szs?@={m2ek!ut%ekm3hQf?grv!0i;ilf+nV-_^Yf)*U6LanC7)<44>mkI%=QuEh z3Nx{GHz01q`VK$_3k|{TA8>C6=o6pyZZ`*v8@8Mg9k#`NG%uP9Lm6oES?2~XI97vy zt{ti}xaYkezFKIU>b>ViV^+E`BPEk3tYuvfTR?nAuj&@S&djA`)5G?^W@15=*SX}L zAqRp2Zx@VZu$AUvh50l^uzVqTt>0JPeu?1)+=kos5)Ko1GB8^ zaOJvD9q3iw7N@x~=U@L*R>E?P0`q~Zzii`Z-v9VVGR)HSZA!!`5)@DpbdmPhTEivq zt4$m2DWPAliAgG=Hz^k*vsz_?l|;E#nSaziIc7xh?E5@o_(=ZVhZ?k6mr4y_Dp~u< zO>OyN`TrkD|4VQ@@$2YZE-HZXDuXD}hWxKER%`)#3U>n01KOQILpL#j;rB;}Yeb`S-&>y(W}iQgZ{q%-!t4S!%nrHY z3QThhzLB%XvsUBiH~d01YwZ@TZ?qsw=b6HZAPUckrHc&~Q4az{P(E z6H^ayp`+6`K9o1Xsakb(9yESrSDhy&Fwn?`6^O@QT_}E2A3yzTTsET~(vg?keA(}y zx4o#Zb@@_~xcIGiP{B-sr^nb%p9r?bm}`sIxZSnQ*n^PL^U>Q!01H9!-LrVS__&$a zDz2_fpv8IJGuvFlAylBw<2ecK9Y)4k)%KpLoLO5_FN^G)FF8A&#>&<$$C-lOW-nqz z*D0po#p~P|Uu;%rEwS-Xw$8F;M~+Wc!)-Dx^KeV1wf!NTu^sPauQ7)jUuAr)AcbN- ztV)XvDZ4+pT-wPuxfQCFMu2f--3wudS7w*XgUoL0cLJ3cB{LXdnVt*TOozy!@pw%G z*6N7W#*;`p*Fenu!jNok6Q$#Xi*eL$kbUoNhm?Z!PNMD~mwxZdcE}RL(q^FT(OP_9 zRMI9v?%CiyUxJRH#Mxm^Sm9OAR?N*)%s_g#5$P!{n05a*2{W`9xRR zN{ALszh3RMG)5S1>E)G?^=Ll6tHlhfUeZv!$s&FAs#YKJT2lB@k`0HB_%xz|%sDnF zgyTdjAb@wpX6^WCo0m=E5P_D}wfowooJwgE2PPWXQhmr*8|&qV1LE}AUyXXb7O8<) z^x!XnX`RkFTC>R5*S(XVSb1fjrt)@g=t`2LOKY=5X9d5_cOzP9)n$??9(|W3GeQ-$ z5SDy!tZ~<)QPEwRFfjxg;n_3Bbd;I<(o1!_B5DV!Y=g)_)HL7HsOI&13(SL(5LS=F!` zaKzNoNm$(b*7>r7R6{&zm)X)KH->vZeLx04)m+lSQLa#l&d?~1k?V! zBBEyn!4ka|-Y8ji#v*N`Y5m#})`xQ$|Lja>f%wm7O5v4{Snk0<4GrPn1xI^EpD$YE z#U&0rXs<_`QnQAH?sUMr9}e)RH74DXiV&z1I()JzWGDBkVns7&AyMY{rU_L?hp4NF z2PA}rD?zGDSV%3Tds-%QNBqy6u2u*B8duT!$%jvY?ZJ4$AFBL%MS|r%Xl-w zUt}nmBC&yZuuF*Xe<1KU(vEnwoBv@=56aT8$BI7CVT*p#GlZ8=T8a_x;8T^g<$JP} zZou+oV9n)KtJ+40PN@je*A?gc`rYp{RFZ8Z#sg%pBp=OLq3NZanUm1lacfd^jRWU~ zd@Oke!&;LE9z)lpC4#5npr;jjNXHWG0lh@8@n%rO@h9PXERy{pK--OS-nRAa2@rAJIG>+@LbeehUq z*WZ=KlH0oW)}?i8~oilNMcL6kQrRMd+$8XJ%i9+=}XH4 z@`n1fHIK3w5qBY_7Nyhj)|gh7)E|C`-&)UJmUuqP(%Bprr~2frt&1*vp6)PbM({x1yva?qZ7jOk#1yYB!Rtt_nlU%0LaWU9+94w?t zHL0isPEyE5_|Dw6mSF75H=}y^(a~jS@ClK1_~p(+Ttb7|tfti4&uSN@<-TwYG@i`v zrIWm6rW;f1Q;W-qBqGWakB-$?nX+9{{J?&?is|dYV&T)k3#w z*s*Ry+gO!yQ@e`uU{~tsmJ`l^;Q)+!M%n3zuA8msXbwCxLL-)hbpF7YgX`?!F*u31 zk3cnh2fGjHF4Y^|osYiHsS&YrPF!#@Wf?j?cxUWRtjXmIW#{?Dm&YfHsa`IJm!XIU zF8c}89@;L`0{fzR8<x>7RW|-^c1d@hczY^$K(PglTX#_4i#yT~pqqR(-FGnJ`N&VU3Tr$*pz2 zX1uJHOuuOZEJDAFWr}2EDsN}1ZGYB1io#Aof03+>p`+2`f^HfxMd$N5;na&_qRkfF z0;67M5f^9GUKw%G@8SZT`hDt3dJok%JFTNu)y=0m)PKBn`jpNUaDQfKCL!OCv8(x6 z?$^}SBt;a_<}}#qDDTgXRH|hG=2nMVh0>l$quP&el+Sf&2td<5F9; zAnw9^vNbIC%6=AHahoE;k-L^`vtk)`GR`ULn7?iQ{P!`mJ}aKt2MHHYEWHy zxUcA}<(OlyK-PUei##hSnBA$apRE?}UKiYj78UIoQ>@rJ63MW8BjWpNN>)lr>r4Kq z3m`_~sfR_qL+U&e}uEHv4-ORwKXY^h+rB88Rom zNg++;h`mvcUx4*`L_DLk*eU+IquHXAt_$3l+nrcoE8gwSm=)~jTcuEnO~l9RegiMK zhc=j*JbG(rnkFoa5ofSA;4dAdhX&;x-oLKhsAiI3U#}0=^!oUxf`A1wqSOBK4QxjD ziS)qz;hk=3EOS+!lim|{c+St=#a~SgGNW*j9Z8k|4((&yw}9zHEsyj=h&n9Du|YF4z5HtYu8L z$i+I{g{&pUr7OKR&X+)3z38Cw`wH3)#lIe-U#s?o=hB}B4_Z_aoT05w9- z01iE6i-B3XEzva#?kjkT8|I%-v(g+o1e6D|h*dv|jA(I@=b=SOq}u8QWwt z4jn5~lDa1pF1a0uwy&%o+WR9z5!{pI^*OV=hGDYL8DmGL&BAs)uS@sRzi;hSlnWYt zJZ78Ha3CPCKD$>U)E!;1)Ax`F?Gxm_(R=Wb!*VMQl_jRJGkCbpPd{Ua3uF!61BtF{ zGXO^2!R!S^M;LH54rC~3lIW@o3=w)8toc!eSSXGD^uq9S$h=necrZj)*O%Pw9J&I^hY&&qCIz4u05Ra(F(6UF@RC)uUwBZXGvyC5s8T|9&^rxtH1i^pvlMdhQnTF{lU@9zmR)UN?f4^Gg0kl|72$LV1 z_TR1YmHCD-7LUz}G$pH9_r$qs~GsCc6)kCKf>(d3WjhfpKkv)=MPCL(g1(j8Lae~`33fB$iY$=?)7 z0uaXF%PA7`xm%=dvY79+h2P%8iID*VqJrp+I*j&;brv%;sI3T zn&n>0a$$<(C_SB024;z|AmmX#CBg3k%IE?pq?hxS-iPw=JsRAA;QWq#n}fq8hJ!ARXq>^8a2>$sQo zhh&elKRHxDfV@ty0u~E@i-o<=ec~Bmv>mEO6S+(TKwiY83qH@Rmlis|~rANst zTsNM8hR;3UTs{E8?m~iS-)5_EweK}k|K!W#(j7m!mciYe0mec|Ddi#RC*zi29^N~A zcUXJ5LuG7+@ofSntSdzULZ7ef4Da?dhGRHkItfODpgSYf_*)&(g6GDwW^f8;z=6gX z%njZZ>>*4zsf36!`p0c6KP{J9U}dGcc|V0xdu&IAnEEP2s^-OvmuO*gn~C2ia#x2P z1%8P04i7W7RzB0qZS9avJgj$JGU1{ zT@rZIOnwHQzH7UQqCp=EQk+%tswX#p55clMbk*DR7M4~=j{6JWEkf-C6<0jnK;jZac2};18hcz7(A#>z+@_Z@>pVP1_D@`*M0FaH{&*-S5RnS=~XTex8VMofJVZ zUR~KnZ75G5$=iWT?q5w5c7J6Nx-RG02kCb@{bkZe#KigW-l6(IM%voG)`8qBmRX;h zpL(hD!LFqjvX^eJ_oZ%NeA+YDT&1}i+C6%u!$i+c&h!sQ9-JM@`C7Lba<403^D3|I z!grSKR=9?^+erJd$9m9vL7CAG#j#AhkS?vF2B)!Y3g$x*GT*z(jgYjxYC9%(o|Ek( zaR#&*RBKpTDDkB7%+Fw3IR?YQknhO-yCBu-E(jx=`+Z#WYq5NpJ^Q1(%Fog@wQ$%Y zUTIDvvxU<4w{1A4V8cPb(?##eQ4qiHkK)9 zxm)0Nq_jSl0Byk$(W+O_$F*HLbRCd_y8G=rLscBZ($*MtcCL$swd1b6SUlc8q5%o|#uWHUqUhNqDY9#7*WB#N{p9CBx}Bzq*RZ86kF%7ZTNT~mQJJc7`MOMI5AFbTwv`?MUn z^-c=?pphSR;~q}Uy?lti3_QvRy~xZ(%v}V_nGBk>7Cu#(#JDoQ5bfpKzhOtj+2gLa-zYqbq7j&y~d+c!>4-}`>7P!o9Ry7 zuz=H)%vcGGvBKr$l|sFqKc2~SOo76iAgD5<0_rxfDynC?id#0yXS65J{%TA;2s3HR zSMs`xZ_*;2Y`lMjnr5?}zsh8o{VT~(cRQ19LOpANamjn3gXx8f>?30;1btOiRmtpW zDsk&%t-h5u=Q!aGU7^GR7jfT?PC5qc)QG(QZ1J2p5XY9mSMnLpc!ah--Q=V{#3D}H z*A1DrSJ{ww5E^RdnGFe`-xOONIA#n7(+-dT66~af`a=pRWl-+QsEx#g`BZOafw&8# zpFi8vI70lTBVLEoJGrZXTc8xT84ZM9B04tns37FkQ*&f+!r#yit#5nDuZ0h4`2-XO z-+QbbPSkv#3Hy{AT4f7x)11se2_=rx?~e%ovY)S^_fP3kBt7f1UGWbw;@!xIkZ$BTc88AxL=gpK{&59Wq5Jl)Hz+>*ixH0p zBVfN80C&JnT8{~aQiG#`aRp$1eTVrUA%vI{uSCJ| z4-{l+VJc)2uA)7-2fC%1I;~zhDp*7C(1X#_gO`n7`v6vg78)>7k&wk*9-l?W*h>FB zhAm}WN%LOWMH+B5B5ja9OwI3fF$C%lIB?K!+~vP;doLUWE4a0_L`&Ebxu>i0rrF4r z1ut2gm7tR6VupxRC3SO`glit(DDlI)S1W6X2CW}tYeUB@(XGxyz7IrIm1*`GFkS*5 zX&slX3H>&p-iX8BD=R`0q}Tn>A>X$|Mu@*hJbsQt)+j?1rovvhFogex_%-o7#ExSN zKlcdjo^Q7Q_#Q0Pv9Xpp56_QY>o==pJ#eirsjA|z1Dt=M>$^OKDF;0jeN~by+6v5= z#)k^L)V`XLrXm|g_SGiBP~ofaJd);*=Q2Z-N}DO_yOSgo1v_kG2RRAL1D~m-26gGa zuJ!+V_f>3G5B}?$&2d#vp%yy>)av2uN*wG*c~a)F#7X{g9!583&YPt zcj@82R>%-2gitU-x-ekWqdWft^(;7n7=y^?upVQRxLW=RG{wMhP zaRen@CU&0JbCC0+W#%4ng`FZ1LCGnl)z7Bh6+4(L`AVrsjetcF-6DmuZCjajg`m3| zlXwZl+`-OZ7tJ6L9N1h{!ujRox63Di1xKsWO z$3ys&tkyrnb^7TK<*wa8%& z9dxH%S9#}DS*oW6qpPIbD6H5J+mV zSn{yR%~IW<-u-v%yM~GFQ@L^X{|LAZj0HW@uvUMFvJVftW)?|XKDb|%Nf4%%Si3`_ z-=$9&7iQvt0}61SI7t=v*o^(f_V7WkO{Lsg2#5~2SZ?)Tmiw<-6;yG@w#=+p+1PMToHRDY}iqY zjH9VSqo95QGE+}FZzMm!twhcjQ(&$Nx9fix1Hg>11nsm%qn7kk`fjf=d5__{H-;iX z5uJl=wsnuyd%TdvCU&Y$y+UTR>PKnqDc^w;fNIK@P@^Rkbu(6|gIB(1hf{TS19+$qrfGaaknhE6SYjq(B7D^sD#Tu>mVK)>byk3}I=x2g2^#kAd8_TzqVAI%cR_m zH}~hS%2mnhadd^>e3O_UV)dTgVYPs}+!YONx>2<(XQ>Ee`>g%C{avOUFh2lNffAiA zO^?7&h+m!^~sxXj0_o4a)Bp~dyk0i>t?o^LdS1~%DCsOA&hSaMe zzUcTi+Kpi@b>up`Wm=uD=x573_LndR8X4V4zWqgBkX4#z7>l|&@$%&75%NK#w)zvj ztJW(6ZdH|U*Hbd)*W*JGZ^YeC4<-^h#wCvHw2iOF3^W-8p%oRP$HP2*wv7M^0^>3o z`wf`IoHwd3JWhK%;;;RVh~^rvnzqVYuv3XFa}BGenlD$wi59G7>7K&%ugNo)bX!Zrf2I=6 z?fv(v7ktX})_MP@f5SnDzmKXVdWGkDM6XY>)p<9r@Z;&;wYafA>K_YPCCEszqYG6A zci!)9l=Q(K{Bg>FYBPYv$ z|1K}pZ@c)oIhmhtt)5B_tf59BtP_3uz-8q7XPIY^8Gz~<;`C$*B0B7 zzUDq-Zsx6%R>gz4yek{O*>!o%@nrg~J}_sOh&8v#zW`QF#80%S#Kl!|*5j6qvwdaC zuCo38o|mUXX$=>;^KjR$CKqFMV}1Bd#aW#URTX|2mBQA zUHHy~+JpqV5c|0aifImF;cVL5;Z$qYL&13ql<=V^j;Tba%mD5<+re}F>sVs`Lejqm z9Xx+Bw&#zSJnJ+TDm|?t=g&X^B0!=0-Mz{y+D#X^n=~ll z7ODca?%U0G+pUs^7>aDk<#G>?{&L-1*%Ec{+~Macn{PPn{zUaPdRrL!Xm8aU7FLbh zw6ak5cV<-_{O8uW-Ft{ZjZ zCf-|RFvrPi^l~q%%+PzJdn9<2i25UwE|Rauv<;mf8ethjE-2+PTQs`~VYuUvq=sh6k$Zo&pY6^zg zGPo_VHENEtm{+9bFZNF-X|l70j08?aA{mq%Yy)Gy{|VunTj5YB-TOu6QNWnqx?=pX*Xl zp;phq#$p!wj_crP;enl&>XZukgEcCvsDD^QB6t4tpz7|4B~}k|dnLkno`zH8v_#AN z)bilKt0t#&Iu4K9p%z0AgcF5*WH08=PPml^&i}^!5!1L2cwyqB20+NehlLFSN1JwB zu+%y+wJeDi_Z@>8l3_hgvM0PU=!e-OeVBcT?i;(rfBwZA%;k zA}1W~8ceu$lTB!^Jg)cW331`n<}9w)Zz7#*DNQmFr#8-JYb+{g8$WAKP%D5r&f4l6 zf)43YrGl^lDx4tSI6hz>x$xPSPtzuxYh$# zgL_F^BKH2ZVdf*i|8`>8xV*sEdWe5)Ki522IutMt_V<;a?lSxJUqJLpr_S`qsXNud z;q9ltjsk5-!^HnUV)xNDApFjUXq%ebpl&&6lyt`iLd4sE-;4UE7$2#Z$ms;56%Hr{ ztbcI$#OFu%c0_^L@#EiVO_b&-H;9+f!2VCG$_ttDs8ghFi0c$$faIriyAKg4g%l{> z0y=N17ru~xGMJxF{s%tWtYlc>JMq(c{o9ovzlrIr%cTtU-3dr-Uqb`_Uy#eU{S7&} zXmujVW~GYTDQkjHnx9_v0gk4A^56fMcZKEutxM)VuC$fuF?UKT$C>gB2-hP325`jp zjIzyNtO}|A$^G=NP@WhkH;oTak|bW#F@qddd6nPK1z%xl1%mk)_(zjoZjhwi08_3Uow~Rbu8+t49h}W(?C~EFyS87VT-txT=s=k72!KF(pl)Qt zl(F{!dLmLe!+*T|ae797a)G)fq2W1r20l<~FpVv?Osy7Q9MWkJz8IJBQiJ%*#V;7ANrCZ8jU>sl*4C=&cW$Atd@V4)kE{mV zUFWgl>0>cR5$E7mFv~QfcNELveRoXGyzb-(_!(B@N?-fM#Zq~DbOcGPJaAJj(f{DZ|Ss zOE%}{nJfVnH``%v*2XWYai}m-{9*Z7r+vA`OCHdHX;y5E+~#~R1Cpoll3qt+x;)@U z*ZqUwVgKac+$L1?q+@fCEv}mM+8yM1nfUZ<5xK(gjwriKed;rYwIF^LrQTR*%eSL_ zV;#q^M~9+K<{wU-zx65J4nyn%5pfr(d+NC^ZQ(`&Vtv*T?-{S%*4V`qDb)TGUy?WOquGwE*?Ss3Lg%$Yvr*%5|VR%OR`OL4Q(`dNEo=ZA9^DB*@n4eG0 z6J-kT)Z1`b%FTS<{g|>%9l6(0N)f+BwbGm-t z^5F*3ZoFK8Bc-bt>SK7}ak8ilA^#}`TU@}|pO5XrYq=kSxJ~J&4^THM2UQCFc%Y}P zqw(r6iHmIg2dqqYoc=po|0yye94bj*`u;s&s_Z9U{m0l3M2#E5=P?kEQvYRt?E2r@ z9}~}fxvd_6UTmF6P=ZnzEKenpmW&_Qme z(xx1xE5pQ>a_FaN4n|M`4kpYlnp45B=deQ)R{1N0YY8}_=F zp}5`&-xhPUD|3HZp!OIZftf26s|AdGRNzP@)aGm@S!IBnl zer5_=4w`#|0t6Ms#o&<~O2ThMwT2E&VpjT8RXv-uR9htmmPb&bxScJuAF&7Iy%G~U5fY&$Zt72}&rR9;MCN2t zh>v~N2thUSh>2Aa9@RE!g?}$efu4{&s87?;BJO~a)FP#!gYw#R-s(oP1J1Jji@r^5 zeK@avU2~ID=^7hcc9fXS-Si^BunU=QlGqVdB2rdm!Xh&cbO><8u^$7oyoKXLe~?)C zWryxIXf|)!c00y9RE%W$b2`-8@tQh%;K8Hw_E-wk#DLwq%`b z``@{A$a}!vp}hALzNzY!omsVwi4#z!n>pg|?gY)XWT1C_SY9P)aNT{T70$(DKCP=0N@`X8Ln;sDe1Hu&CGF>&&#uW7NnEU%cp9rwrG~fU(kRc@I zh(>#<$Yy2`$dN#-x_kd-9QOUE{`%jy(~!gRO?=DHf;CABZ1cv5S7Jw_TEGis^=D1; z+N*}_Kk}KLF1nW-pnwZab&1&<#+E5N;bC(g#^Jz~@ZY_~raIfR@MIvfaLMKLN;{>y z8Q}k`CrI~(@6#TeNEG0hESCo;9{`(IdS6Op-gt)@Wq|=Nu)j+>r5hW7+Zhm9_NT?| z*p~8+de%LGsWEHo^O_4XAJI8#^HpbRElj>0hBuJU#O|w%BovAS3K~0WWj_K=82Up6 zQ?OiQQZlz<&V(8j&pn!YD|%Ce=l})9G-u04sg5a?YWEMYa^c~pA&oW04uFxiI2&g5 zVPf)ad4wu#e{V*0nz-ZF;px(q-QOi>{pBC7i=mH{K-4^` zE=OdMv)dLh=?p@QYw86u>8PXr5s`#$M5LPUk1Fi`IGj=s0Rj{4Ky@*RJS0PB zX6BiJU%g=LO)jv^opY$G&p!$mFU;84d&!gom=;DFwJHENp>RK!X5Ib1AeK4aPR;xZ zEq*xnuqWUrMB6-{$fpFB8VIBzf$m4zQoBW5_{Ww4y2{v>i29tg&&$aDQK_!%)Qp^6 zx^E8E{pElSZ%6f+c)Yc*P2zvhuHmjGj&;6#`rbb}# z6c7NWU`|>3w*vJ4TUzTs#Sk_sPauS#Y5!?FE9b^YsCT6`o$}vwE%)nw`?n6Uf%8*j z&DOtZ3ojyWba7w>VhVbYpcfSYS7f^SQ4&ZqK`;I{@Ur$M;rHlGGOZmgw&PLySc?ca z!&CJk=)dgp2|># zGqLAgy_+_wf zJmLA4;B-ghQt5OuA^n2h@vY9x`qOFkB3o{O zaBW&6tU^|4OR>-y;g4c3(JU7f)LARnW1pgsV*3sborgyW*@w1yT42&*3i<#ol3ARe zvmH6?$oFA(7`gf*#?-5{sD&{{z5$;86|>D0R$31Gc8IE_8&PCo-EP=G<%npHOpmbD zAl5=7?M6-7;-o}*A!CQdpXaDQeZF<|{gpG#v2LG7PJ&cA10@6SN|&>b!t@~wwUegT zM^Vnk+Z^A`vp*3(-{NYC&d7P&P?BF4NLZrnh^G@(aIfF(@y48MA?i^6N>OG_$ECW+ zbI~q0ILqzt4_Pf7LNcG3%rdofNBrCe5>xj;0Z$JO`(ykkbs1x&0ZyNJ`!m`gwha5? zy>Eel=d&1DE*;<1e@3b=MlN^`>-c_BOfG>oO>c{c`7lAH zMV^79U-so{>rA@2Tn>h|6PunlpQL>h?mAXWF+CacC>>9i!f;0`{z&xPV$e;k{x8B{ zU0)RaS)wQzkB&8AFU`9N$ULND3;I-7qc;i6UPsGPLqTa3Mtv}C*%p8VXes;q;{764 zdCiFh8i()Z%ZuLlG|0==(w+yt`@Z1CWY_b`> z3{N+6Mk!4E_~wI#Jmj6%8K8pzQus>bt6;;wcu^3D^5-23fGU>`e3SRgewBu9h{rWX zo3*9gfQ|As>(n(&r7B@#i)^4ApPz1e($tHZt*pd(KB>ubu@_pFk6;(fhp0+##5a{J z1lIZxMN$Al%JHH4(c(SOf~_=^8`n)C**O~`Ma0tEQysMGgq?#o{OSeBb;FhUV?gO$ znN%JtdZ}PTQ`{rTpT5`7n88>~bGKdT_D+YO{_4q1Y|=V^9=fn-l;@_$arEqd=NZpP z4W@9+uw`~hH`{dC8B&FO!>d(U;%?{s%~0YFURE+(TTKm1B&Ci{J2_D+?heG0HIFf1-f7SpK`5#*oW#uCX%q+`G5wuEtG4TQPodNIQ4WXRu22%6Q8eUs)aUq zc1BIIQ6%`^bLIbA=KQ}vb3pU;$_7rfm@#i`?w070nKF+Jgj*KKfCH-kvByXOHq1s% zpi2kHng6^0cH%z@YM_i>zhQb)#t(c=pn$O2|3g!P=cdlbP*?l}y~x`Cw`#xkg+yi$ zUwI7JIUki)^`*E@`(TPb+_wMF%{`61QX@?t5|Ha&(U14U z7kUVwF^v~w%1m|k4sDoh*UvZXj^PwP7IR8ostwWYi)}deHX_>MK3kpNN;5m|Elv17 zVTGbK2WmO`tFVn zm54>Jv%=0yalV+wQ;RbZJHa0NxL9Kil_7Dvubah_Z+ZHzS4BVhYPu_){HgmK!tEMb z>D&@wL^>L4p$fJR2V1K%=4+29OgZ@*7}uIAW@pCSel0GQ;C(o0aImVdt8{hYS@B5Ap)IuDfnXxggIIR@8lD&(;aqW8jgvgzAY2%@ySaGzG>e`?60KXy zt2Z9`AvaE6l+HA!pg~%et__srqDRNI8RL~dVumIyWt%15Lol?XTvqXau$u794g?ez zHc367E3ylh3_*YZ7Su#i31v2L}T!!z@uI$X)*F0~-5DW!mXc6%x~ zI9Q-9?`R{RSq{l2s|N(l1Y=~5E#wx(;pMWg6)bLmsPVp?B2M@}of{lB(|VdT{Z{S6gP^5f8~M*~>q>?zlJ__ieH3pJ#P8 zpXflHk0uGKFP^qP)AyEqF^6Kuk%O!i>Y7mmX=8rx=yo#~lg`s$^L^0f?MX{_q47pP z_Vj(UGvk)2FKV1|FvwBIT|SmrJfNFeNw>iK>9Z1FR%Kb*GuzjkY@CxoByM}PJ}yz4 zZ7^|(x**Sx<(QFSDgA3ey7mNLR*EY(@pAb9Q#x}8s{9UW^u6tv*JyTP#ry0?Aa6=4 zZ8t`&543Yvv~SFB=6#7onfFM)i`-D@ir0>GRG`s6?dzJLSqU}@b1xq6Jsf;!^U=>` z#WN8_vA)YRyP~aKb#7$*8j(yxV%@gCWrGqoJ>={V1D#mVGe2jMhU#&kYXPzVe$LSNJ-pQ##V(E)TE-lf$xsB2jc-@9np z*B|~U&7S7Rx-5vpeZ&KZlJL<4ipe+jJ^K~N-^E=wM3z|})AY8A7&y0j4AMgkqobiI zU7c#F8VLJZB>}DP%T+^{q{&~=iZ0ZiGg~UUcu;@0b+3M=A9^o~^7g(0_FTQ~L7Jy_ z$s}|ul|OY=7@~4$pyf6*MM<2@V@mseu()M0oYOVOsT~S(r}>|$HkLXO>UTEmjUqLQ zKk1&%dR17~oY9v2tEa*=+EZwrz}}X8gTUJ@ksq}h)Y&t1r#yXckEOL^bRsW#)B?He z6XxBe9*k+7jIqz)&DDod*~g5~=3_PU(Sw_Rbd(N%;mo%&7|BzUDC|V)!yB@yylau~ zPIP3YDV@xiYCgx#F>i?89Q1vX-6|HB>rA+?#Y$M$NE-v6a_FRd!Gco6pT4A!N8UfIHlhSXDKre!W=vgCaI?myP;6*EoATiv@EEbpN|oj>ow^<1JcKO>mX z*exdL`=yYwCd;vuzsP>pdyZnbAkb)ALj z+$lM+Z5lSqD)M0C%s}EQZ{b%?%n$~l^3HM8t(aiX78XR*SV+zC19*zmaLa=uk3zZ@v@k68y*xw#`3%KpbjT?-Gqw= zP-myR;YuGHDH;yILPU&OimfFk(+<7)9j~g=)~RfIaCeXEC;sHop&Hz+=U!lsekI#Ki6dLPiGN=?it5v z8vJBw6)j@5?cLbWvEq?zJ&VzmgZ3%z@L-}#tq*$1`skkb%ioAw(1|`TT*wPsOkt#7 z56-!X@v$rMMVq(V?xTOsd+)qNC^nW5?k!Gnr8H6Z-@4(E^|gziT24Srupjgb%wK5X zT{@dG^!bZfHKAJ1KGZv?SxgT6!SsqDfFE1D@edJtC+eRfbf_jo=bh--#gXoFShEj! z%)=yDOHuG#tni7JS}EO#AwY79g{*4n`YNMDKZm%jr7C(tHy=t@x5th|KS9;LdZPjp zv)L?rZ|cK-=hC#~pkaR7_h}`~qi&dJ25i9j5IHp}8mUhEh2!VGW5br@Zf2ZJ~v6{S562b)UsR$g^w$KKP{o$Jh_wXng0{)FFi zgdC8kvkPMwjW(6ekePe5CVxGiqGI6V_2SBW54l&Y(-rY8-)!z+`GNUebCQl+7<5|9 zi%2;g*q+6HwSWGLQ&~GPNSr1(88X|$)un&)w+A<;B7P@>1sqb}kkEUZ3kp#(zyT4l ztiO5xyhA-ruU5N1Cp%5(ozA0|fwuVD_q0Bc+k5p=eZ70w(a8`t-E=!tuF$wX#w_o9doK zjv`JP36JnkKZ33c<^RYCP!NOL#IFZG%(q506E_8NP7dzKmH0eL$0+FtsCbPbZ zzp?oECXJ%aW0&bVVNco2$No-whnpD_Km&J6KPYoT4+{DCNb4%!V7Kd1#?}0CsB^h! zO{@_DciSeth5w!8#yY+jcfUs6D_%M*NEbh#5joxGLaf5|3U7!@!}rRr+8Y{4rf@*M z5$O2b6_a5xZuBTG==b7iWj!<;ArKzFbY)SZma|VLkK7mG(rm9!_DLE5%2<`0qxTsBQt7Uv0#=v>6pSL>6l-UoAA!Peg6-Rud`R zmy;>b>=O-;+TY`OJ=J$%ZQ`hs>~(C$bQx!o7*5Rs}iOUa+L`Fbg zo_tKwk8^W8ogPF8`wVx`(;VSi?~-`V;ODYW0elPEz3B-ffo%6xPjO-*YI^vd8qP1f z$$RrP=4Kr~@T*e(tpBIX{KoWLgMLFsm#Dd4uo9vKVQzUd8&ft}=Mkc2tslqP+4H!| z)kBoCmuq5u?_TG833_-KGx(!1bj<@fB{LRQ6Bu_kLx!8fe=_{jZm*AbWr!s^f)g(> zfM#|H5q6Kb|NgZOOV|Bi309sD!B;Xa={%O=G^7vA&BIc-aPCHRnF0xx0Y_2NV^5sk zR!7h{gjvL|pWG}Bx*fDsG*QwD1ssgN(-u2sZ1`b?Z>@yn2;cQLt*uVcqk4Qn#}$mr z7TlvHof}QLw6>bea=_)tmENcCOrnZYo-+PoC_hnJm|qyl#LLRYT9ya5u6OmjT#y`i zBXxBnKg!!hkbJN;(W6=iXQlnZ>tkbIoW#LRp8X2^@3nokkR+_C%w7;Ez2@T)SiAE3 z7kluxDd}_IA=T|bd2gtG(2;sY&F#Kqr1I}^o}9y*bNt>m@*L9l*K_$)B@XX>jT*}a zPoL?uAOmFK3`F@6{UOVw!6gxnxOMsNKBL!M{tu;m1P+Zwbdn;j&~ZP61Qc!cI6XRZ zQ9Qgoc|bE14A4Mfwx&ht}tQ7uA~E=U5%#smB=D_Fo%4ZcIU z5b6EgC{bGp{ax;lv7RZX$j;Mx)m>2FPyvjT1*0raDEbwGp(DdMMZ)Ib*%i4U9rm1< zj!T^egV{RKM4bk{gIfYAT9y=4!Aj!8@2U%*Sf$A!?^KUVjp(Q^UDaqGF76~z_?{?? zuU_PM8v1}gx^mg2lcZp|c%yxORxA;2JibeN(76+srt#_Z${dGN0B;P~_uNeNFeAlh#^4)H14Bdv8 zZQ6}wZ*Htf+R&jRyk|ykMWyx)R5+zu_q#alNX%y)%1fVa^D-V13IIF<8>5md`coEe zvS)niVU<+nH6`75kVG;51xfOm*TTfL3uFlZm_*&e2sZc?i$Z1Qaxqt>EvH3vK2Q|- zbFx}{)~kejbTa1xvj|zLZsm^|K&Tm453NsDVPArGHE}PoQC<>RW39`>tfGbPC41D( z#M8^+vDEbm(dQ*4zooWCR{L@6GbUW2@lf6fS1vOU|1ip}Y<+G|s-mB|uYoS02x5oa z8}9N82<2J0s?q9pM?Z2&?Fn>FJZ(fMfBxx`d8P4%pty=~bFX|&c(HRt zw>*RQ$!EQ%2{)|ASzuG-I$jGmkVZ4J?-RisDLeBlgXXNr^Cjk&UYrw4-lXj=h66W` zLY??fwdD{MjPLXe1B{=H)ByAfC-nlw|HN})Zvg=DAJ&?G$2k6NhcM-a)>SCUnoVCC zTQO7mRP7f}T&m#{X;-T+v$krZlTo>4kabfi`+Bk6o`;7918DGE!R}TBo9EoHJj>*I zj=|Ej=g%GM51lDGdZl{+13u66PvGK@ph#%PyHMy}OS*13xAh44ts;u~m9i-L!p@IZ zi#4o!zEm<-?!_e0n2Z~xzp<;IcRO2}_!ny{KhHJLd1Hl6qdZkuzVg}LU^lku0Ff18 z8}FI48^$Dg&E5#rNCOp)j!BYO)BOrWOAWd#ygB*^gMu^tI#uh*kzxx|Fx(zrg+X;* z4uJl@^jwF;8zD;Skd_~-v&tKLht5;#d_pbwPu=`2Q!P!n;a`FaAL~1rw&=x6G1~Q? zR4n-CqpS4^K_0;us_Qx=1A`~XP;k>erlC&a77_H8&_LF4^Us=Bb?&|{ZU^2?I$O#I#mPgy?Uu_&T zTKVKPm#M_vgk@VjT7{>Q*Jvc9Cpc%pZXcH;CsGjLgWc)R~623Nq z>%4F1*!BOor4x2gW8uo`1QRoDe1L)$P~cr zf`c6($?p94acV{4(D$WlMqwL{B~n5xPY;Yr;4usK89E?{B(t%DUP@u=LjYf=`c~Rc z>@rIyOS5k6WjrXBdlcC^erbK@(-qb(&Ju|bSE-a|kqo3d1HLnz-_}Cg-@jVwUq9asUIy-i&+pWARCu2ILmLD-` z^r3ppsZJ^0NAx(jFKN|-_*=s{ai}uIs)j@Lrb5X>{*UASEZ~j0UHvb5`k{s#k+%sK zgN&-3)pLla3lxJ$<)gPV|CyIG|3oVPO;=x*I{v&64$L$eDbY-Q269g8OFu{y=>O;b z`VGi`(_|4a{EOB3-mBb4tXQo~McG;nbg8~Lg2##{sTbqD)qq4It)=5Tq9xF0tCZT+F9T}bF^uKimV(; z=)L=&G`T?2VI+=AuQjm_z~9!KQ7hGg{Z^0*hQdjiDYKbPVnD{C_bw+C*1XHu_%k4u zS3gl(A1GcyiGQ=)(?0#%>Pmm!VAmaSriZ->m-7+(hMF%Xq2|R&gs3T{SWDPmMR%{^ z^=io^@Ny%Bcb{Mq{cnTa0z+8q1|fq-)w-u`;DG*^Mfa77P_&z&koz#hc+E;o!#}8= zuztY-&s*G(@^Vv#3fYb6*1|mdJnQLpIaGVp$wf(FNG08=181kuwUge?y{mOaRPM(y;Wt7#tpO))fSd zP1K^g7Q52mZ|&#uSN23ITwbF$@}A+G;!K}=FP~e#;xI8iG@fG!cG;?R=aAY!R?y^v zKeY@e(Lw>G=7p!yyVGIaW+rZxk(*UYQzmcZ??f)GA7N7x?=a5a8XPQ1t?zw<7a#bg zIpkm+H6px-6jlhBqK(X$mU8|}c&GrDxV{7Bo3`J=rGQ{yGQ7DmXHh4}|f^xT@)D{Eo1 z8!lcRXH7#K{UR3Z1U)(C^o0#LKES17Lk{(Nok@>TB-r+x8CDFGR->9Ln8P+#fR*U` z=seK`dMnSzeT_e91_$25QY7P;A4SMd{76M8jNE2dB#)q9j~E>$EK)aqxHNoALrFBt zR2@gtYjjK-oHH+HlJ<6tqI9)IQ;}$an#`=W7UV62d{Cs*NE?El%Ii; zjiYyr^wM)o2E5R=c=rzZ9j*Iw@KZ0A>zfgqwCm;aqGcrKIaO*B9*JO;0{sGQnL&lO z3{Bb>NIHtjFQ-AT%KCzrL~_T#i77WjcGeRp{((HCkogAP{L$!_rAIB$24Sq8@WUBy z@T52~0;?7}W~W}}GM73b`6$=2LPJ{DO*vRZ6w11z^f%&mqTGz@!lYK8bxClkY@6)d?$|*~I#8O|}o#Gm^`8pG%U-GcO@*S1O z_tJ2uGBxjCt!(&05h2x$v@|+Xi3l9Yq12Ey2a;x5?@?%AFiaDyFzE7Ns?Oa(f<;Ek zFiNc~uh^Nj_hCZ7agaRoC)VGOqV5*|+5WNHtK|#(p2zr40_cB7nD|HPlf;3C4y+cw z%jh)_O(Nc9Tel!36AUEAq`B$;kMPQiJER7pK=YApK!XyL@4^y;I03Kp%V(0mN7?to zHV{!?{_o68QX}c#Bw5aS|1??Uu4Zy#2KRj17U5-RIS%Ypdz#QD)Y^s7Z@RC9hKlI! z{mxqWaLcsMb^H)yQ7_-$-`HMh$$AwjbYpJj%M-{(-dpaJy1$H2)gXh}U7iJHN&QH$ zg*|uxs8axs>xsH*dGT6BpGRRIOONS)qDvjBz+a9GYpocEje2mpcV1McADi}A&zSOc zA(w99J*npzPbmLD{psB=))m6RTuJLw#PU|`6I`+)iD`v}4>_?v-!zFmUi)4pa6(Qn zpSWO3fzc0uuxK?|aF@WQ@kaIw?300?! zJ#2fV{Vdym%#z{OCEda@ME}RRdwe68!ou$h*cF&)eNX{-r9&maRrg4^>dyWvb8_Us zMAw^FF!i}lnV&)KZbFl6x^nK@)3vBe4dxU04Y3jZbbXE27+N|`j7GM?`P$JIX{*Zk zV$D5v{6{JMHWUyK_KYuhmyq!plW2RYBHPidd~R;cKDYkC4_@ueF=F!_C^$psTZ*o7 zYc#P>z2u^LrhbCnt|7VRW3!uc#{CgV0H~e~Z6rusMZi9q($iZmWYwo2-GhzeI?JH< z*Ftmp%A8v!W8Us}=esNH>B25;@STCLUt9C)g8gn*?3Mx7rIB3p7=o)S)WQX93n8lrwt;I2f7?R-GuTL|N(Q+>xddjv-Rfc?+_V8^Lt9+mSfSHPy2wS6ZK_ zh)9lbcb!Pna}mYgG*EF ze;1uJTgjGpxr|@uU8{H-Fy9A!G?TOWhURO-TcT)P-p|S1i%r?f$a%EoseiTsn;P(a z?PKgxq9-0(`JI-{fn?}uqkMSj>lwhudReR4cu4(&kwPMFxI1a%Plgv7>54I0cna{v zd470iiIZ*fTS#bgTo)nl+SPh@$Q07ShzkLImM5t1*ldr+&lYal)r?j2(FJP4_d64D zdMzk7-Qes;m%3oIUI^#+1rx05x0gmz=S&qLK56d;5x%5g*rs-DUy z{t{poCwWOG+Gm}bL{1j~1zjPPtgYT}DxF`9BxY_wR5>63XF~&L<8_HY6<~`B2H1Zu z*_9a0wMj}$mA2Be*7Ayw)VBel={b&DKGnIvl~X9YagV$`vgN35qqFd)f|vv1U%a^m3BFQht%FqcS@=yT^6&j<$6g_dr-3z1zi7%izUZJ}1atEN%ljN6)K%EYIr+NoP4iVTg>GxZIYv%;4#(>0G+avz+3 zS{$drvUWoZOJIQn;p=A^@kZXPjLNv>#tm-~*&J2VCbLJq4}vdb)tT?t_O?R|*4R9s zo`eJS0yFC-!o0z5q`e84iEX_j8HebS&8mi1f;_O&l>C&1?{_?sMhKE>S3TO2 zwqz|Ee6_8ebHbNjPnm!?)Q}MPI?dqH^ zlxr9z-v_(QZ|ijI;XfL!9=(zrEP;k`b{FP7 zh!RqER5beezWK=RhtA^9$iu&N7>`Xz4R?=;ZXT^-+Eph zpc+kl)2j$$&pNTU^S&&syk=LuWHYQKw@S&P&gR;dmr?{sXXYVYkvV85|E^c7r=%HJ zAi4bnLCf%1+Vqqt%eKPnHV+Tmib*0>wInBbVQU{lx~dNGn_<&i`8a=mwZJ-Y$h!~U zT*u&(rsArGvFqbU# z52FIdtMKCG@=uUSNRMlq)v2gx&E%rv_l+eW5+(TmeJ}mREf3X_NWs*(=NlNbXh=`) z*5DK{-ZxM6ggt8y=cMtC-(%Jke!aCe8B(+QSTzVKa8fG z9Ha?~0sKf)<1b3=B1Cni;*gz#Oz`mkks8>Ke@CL$z(Pr)7xAx43$bAxC{RcX^MbM? zoJA6-TYh~Eq88A?{44OPR}Toag+)B9aDM94A>uekD5^YX7yJ*tu^101T54rokv$2` zE6=0wa-rWjh|VSS!C#f|?-9+7AF`iIB+sj#0qIEszf}i{V_Od}nr=KQM(c+^a0(T6 z2hzHpE*~z}^}TQPrpzO$lb>z88=vq#!von%_To>NULEtRiK^pQMMab&N%Ul}&ZEBM zZZK22rEDN7OY^%i z?S2;w#IYq+Hd9a9ENbnama2WVA2j&MV5rno);@9a2d!oG2J%IdyBO_Ab8e4?!%3)j zbuOa0s=?mC8Qt8LKV7(&A#E}Z-yJTG*b;ZO?Dr@=`2G-{J~3XTpOlIm!Q}8gnd#zNtG6g zXH5ha+4iN~u{f6cIZWZ2sI!dgkxmIYFKoYKG3?kQd~>qVrd(o|B*vLZ^JpFUk)}($ z>PylRDjM&(xqEtURpF?lO8wfyi2|3X;7Ap-iV_NvCQ(yTY7|7%jVG7PZw0f7{gNEE zeq!Fqp+z6dnp#6H0PQ>aojZvwBLt@i8nbsFS$nlGG2(`>72|EMaVI{{xH!QHWbG%6 zp#Ysw%2cNncRzXzYi1H1vD8Kqk1qm^X&GhOsO*5SBc;QZtISshkEz)i`L=ChnqHtO z+f3yT&CLs!OVK1b&JzsSkb+k1-x`flF;1la66+AF$jn8Suc!kpq+;#`uew{)C|UGZ z3*kLS*iuN35ts`RRq~EOTk(c(oV10Vz2f|59OC4{q$j6pImj5!Y{MMlfJ|1E3F`(6 zL>sPYtaRLrem|C-=(fhL8w2<()x!%nt^4(ERF#ujw?p!qNsw8J*O~okrhb8bmQTd$ zAQ}ap&O5B)y>d}IQ5(uVo7gJIR&_FubzQ3s&a^p0>?mJVh`sB5hrL}c}9evsSvdn=pvv;+@|algQlZ7yeTwh01{xo79gK@I6{_Ljancbt_5Z)?Ub!iTpX`wnzRv7O3T)aLhJc zY@z<2D##7jsfeI-9F>np`O&gOzs)2FJLc3JkR{Ou7sc@^{TZ>2D8hn!q4wy!)>@#{bf4T!naKSAGUK$oP=7JnzO2 zf%4{`HKj4Q*8hD?`S*162dGTVXSoT;A0Y44?AdK{Nd_VHJcL_do9v|>&s~Vn45@AK zUk>2<JJrf9{10zt@nqUz855QJ2WrR($eN=q+DfTAy!j(K#8Hh)m zm8r8`Y8T?b=@j8%Vf-yxtKBDA^>bzwVJTNc);QG6YMvjP9j(FA4%J_HWZPQs$fsgv z4hbjtc==Jjro}}gXG0Fw&EV}>d>^q}7U6Z0*5F~W8gCYeh=?Rk#lV#$=Q_Lb zGAnSyy1KgYK+_MiW)Cw-MJ|pTd8eH`N8`Hj$9-c1XSMFVI6gWEJVr+pKk;af@bxU( z809MF`#vBxEk$IDNS4|UYckQRT_2fr9>)z*5sQ&&N8K|RoZr#B6F3lyxYTp4X}{j@ zVCb?C;PH~FxF28_W@l&d7#Qtvl1t;kGugdos_}OkTo*#XmTGF|N5pM?-U%N!{xUv# zJSSi(>7chbGz4`KDCa2{9K`X)o9&D>F%kRlmIZ@&mqD{ zrb(A?((J6Da%8cQ5qt)~lOhOw*QI9mO_xfflvNZfBhy+JJ$1J1(Ja-P2{VV3A6i6K zyTSooyd_iWwbM$~ryg!<+BBD#ilb(JqW7-snY9b7$K#s8{Gt54)xe(8qcCK4(Y=`qK zY^L6Y<0NRrE9gFMvwo|{tjUyb#`8xQhf2eC+qHcCVg?JtMV{D?0pK(=rRr&q$&e{B zo@Te1T%0@JP8R-+7rDc)U3}c@M{I)}&mU0rnbh7#Y+zL)Sp-4^8!B9JY{$;6*oAEE zB51#eQ_T#*dCapau;*eUQ-P`$)K`oBewgVt)-=ROP_c1 zR~vgfeHS>fkrjhh9?N5jtv+uOfWcbgkG`Z_)`WlNrN@%PyXNa_SdACJIF zyt5?G?r-<6#xlZLd_NxrOFT_At#a#3Su2*sy2#*u%1$||zikq7J+_fOK{t8hdukpJ z>9;8+7S->x%N#&OkH7AmZbIz_JLnhQs|WAq^$74n8r)m~D*>@whR-1JJ@17;XwZgA zeS|Ib1)qZ+-`k1QClLBZrtsVD)<|bHuzZVYl~YkjZ|qQPsLh>5cdNN0O2)Rnh@#r( z+bSq$VlW%K#}suCD)sB)A(cUKTFk6x`!Qtg;W`e!{*HdWrizruY{CVUy=#A!y6Dl)exNVDsUg|t`<9<4 zaZ1Jqtj+m0{^}`?%;5;%-5b>DxCfOU+jEMgD{rnj3*vm{Z7YEDJ!`4nVEt-eTK!H> zbt5tEPAcyWs?A2bpC8?Q*M~Y<*&GQ!yPEc+*)T~pVy?4Ylpl1r(j@&(ipiT+TMe6D z>_mQJGTW!OKpcW}b@l5a@t$=2({|s6z&VZU-0?wP2wdI*C2J$RP}mh;?4Ei&`1oY& zsJZF*AVN>AON>~U^`C671pLmBR4*r^mjyc4xW$45Ifa% z{id{D^a_vWu(BLvc?+1-+;^Rw8>*t@)uWVcd^(u}(1@yfw$&GIX@;0m-l9i^3qF?* zPELUupHGNZ0iYV?5~n#9?PdvU#V!O{_{YtYv1l`ZL7`4IA|c4#j$^ZlZCX*{o`B9t z{mI_+qF=>b z5Yz2T>$I}-!vWjdA!)~*ohSRl^=va`M+4DIcg$qqn^OVj8RJd}H*Ahqib`jWDjoa| z8V8CW8=pKF1)0AnR9M8ZW=6%)271GM?&w9NOOA9(XZz z;0U4*hX9EG6F~mrG*OXTwU<-nOx&+>lJy=nfg^p^MkXY;3XVIJM*M7vVWODZ_I+uB z(wqy*4II)ZEsGJ%`)HFU*h%|wr?hOnBNN*@u(t>b6V|8{og3X&z&MwHHx;i6@cNLx zcMtoOk_`-1>!W>S6{Frq25zmJ-@3X~sN?mEjBD-DdFXuoP%|1ZBt&F+eDM=@5q^`p z;bGH+F7Xh)qVAEjsPXq6f(wc z^6(8joKk#i0KDgbY86_;Hpmxynq>0)QgLM=-b_R_RO1!srwnC6=ZHaBi@%P)lUXi;B%{FjHv{*`*_f)*6|&!c8(x(O$zKDE1!O89*vWq4%DiW&+#0r9OuR#Hse)8M=l(N|+3 z>~3kP(OpvC`EJVJ|KJl+Q60+xg=^O8EJ_+%$XU~+4j8y?$g z0ZRr`{<(lD19WD{ATqchrwq*U4+QX5pLr~tq!=2s7};y4f^yDfkMzC1)v0SiVHQP_ zp#mo>4L4*X4bTV_!b|xZ+>R{fhaHTGRy?93v5lmf` z)Yszw>KTCGByqw5%55a=>?lq$+oz{jV`WWE*+TWcWjAlTEaVKeMnJFEtAzaPmbLAe z5^W5XyRe!8p}q2?uC@fd6N?BRJ#Y<2X=uw#VG!>dp3nOgqLDkn)s0KUlODClc}e=p2S0HQ;C5 z?V%GJJBko`;?Y;4$!|IV^LA~>=v3Mp7m39D1Ed015nRZv`Mo}s zrjA{^ZbH#p@?I@*%lm{S6WFy)d@Eh!PK+mY7yJ!W-fCLMsTge^tLtxGC%@q6cH@9M ze>-p-xAHxqdVJ|wxsLGASHNwkXX4$ZMiZ&Hlv4JV3|JfEbi0{1Kzq0c|2L4SmAGU07cXwfD<}&Ad;0fR@U5HUwTK z>U^*@3@5vFI*Nx7!)YevXS0Z1Pr;o1k=v(Rgi%|ECcii*fBWSV!N|@gizvSw-C@D*JF#_kPa#HR5{GA{-9r68S z_1jS@ABCf53y%6l-3a4eCUj_9w?b88(nRgfoeBV*m^u1Y=)Yj$+YGdzcXxMZHs(U( zCY9!Mgkq!Kq_07+!0XYj{s`B5Iu0A3v_xrA6b<9c6Q2$Tlwfs6?H1grU-Uf(?9F^X z6OsUeFAayl5;*L*B$;5dDbZJ3-3cW`b##Q4v%&1@?oQ5+ zkk02vZxM4osQVI6Nkb2rG(=s-TSdL9>QeWO^q!i@6#gR`;IllhA%mOFe1Kr|k_&Ks zNk2k7<$}Yzsci$YGT9l%cG3%3!)bB^r>*V~O6d%@Ei9kp@>!P&cVkyBWtgv}$luFz za1t;-;tR?{P2slldzNyHCu@Fcz)iuQQb~VQb2G|5Nh0vg`;lJlH*A;_d>l^p{g)Ue znYMd=!k_qUbL^$t?}A2|eN9lOJmfuYmyY;?%GJO{YE+}*(ViNGTXEf*+c@#ROzRE< z(8PA?SWs(X2y8(eY0`%Fo^ewB1aUn zFb2lslfuE0hT3ku)y97vYYG{=!$0>xjQeL#+8e9|9y0LsHFBD&IZTMDGhYv=may+l zi)6={w9z)q1GJmHSt?MDUFc6gnR4F`Jw#3iZGoUyO2gUf41|tP$S?!kP?|^~7xGqg zVLW)A8eXkis*tY7-n_J8uoPxt_(-58HC%HZLs=bw1xl!i^aKeh0uNe zQgfq`MQjqq$m^v;1vEFr(>II5OEvIxh!kB)9Bt^_dfVZwIU7o3H3FOATL+@7tZaHB zQE)JD(gc|6QRnd?z5Fi4^_$YCetWds>h!4ZMS5y)v65yl=Z$pN#HSV`LiQRwbLU|i zb?3^(jvJ3Ydf>i{C3e?;6SBTm^WTSJU{bE2glJ2AC?lNr_yI>gI$+iLT|y=w^peT* z_N~xh9gf!^8Q+~8r=_s^!MnBJ)5N{)LM*kwDYFH?o!xKR?-q&V8>XJom}Y_-;q1#q z3HZSij-KVbaOq6-)GtpqnTrv-8NpkmL>QhmOS3cBdAS3e>IyL}oh+!V#I$5-+8#cR z=5;;eJW$H|=?tIf`MAyY=Z~8)N-^N85V8Fr0*kKDTIZb10?$&-pW~2o2#MDr)00uy zpO%QKc;geJEcs^>5(PE+>?!!+@2V^{p0&2sTPK$1jM}e@w8O$0kdn6Qny(99`ES$V z>isc*zC)>!2JY)tw^-WgF)D;P^Wg?^_$@UC@YT*j2QlN+vg@p4;0hVL zL`2sV+D1B?4Cpo_`evNhEErgid!9L#SawV`*DhXb&D|z%u|n-z^6|_bd9G&M7Y^fe z$EJ2bCA(OG+bJDd3+%0>`kUzHz2BARwec=yIGu%m+SWkfYpl@=6F%Vf)TapTZnE;d zb+OFSs;B1ILX2 zkZnKef00d+>|Ys@?e`*Db5|)UGrFc>DJrxWS_t}71ZD(}(j0AJwx8vHwPI)n%;^7h z6Y_uCl0&M3;SVi>42B{AL%~s@JZClGS2yC(re5mavK=SkH}~zAInrJdKW@bsn;u*C~xd%n}|xzLTHE#^*jbyZAs7dB0Wc-IpH&O#yItpX{|vmn<-$8a41g|1qP%VkvQdZw|4OMyHGVUzuowD2XSF8@G$oNhe{b{Im2}i#u&dm;KOS^4y3uZkw^s>ndfHd)t*wZQ zZh#mDl!}#sy+Pi``HfZo0DCG%KbN%<#m%>aP>F!McE?!X4$z$U$8wC&VaKuv(UPzN z?_%f`?g34VDLY!z(qPnua;XrjV)LnRY27GxYF65;hJ|3E0@GUcL2AOXm0JBxAa$9b71jZF+C#6uUfqaW7Ffq3?$pd5uVoVDjepoG-Muu!KjU+%-2R^W?Z?Fq@o z;=*!e&6oN@G%v(v-BHy(0nHZ4Z<7%74iK9qbxVGE>`vgHhP^k z+YMDFU}+Fx-&Ei-H1$(0Iq1ADGq~)J`v8LX(B5gWL3rp^0~&ntF;HYL9QILJw}QsYkrUT zMNd;!pEBfDdrmr4^eaK@CMAEuzNFGk(CK+{)({X;MOrZQXAi+-=`lR>PoH1=Qj=&O zj*QJ8i3b`p>z9)QMKK^ zZk(RuW~gGj&q&4_YxxEqeYmz8FWb{n7g^MV#-EF8^reltdGB9DUsB^Y4qk{oyS^>; zTAzI(y14<0p5mgsE>If@aTyaP&7$O27K(Lojw1_kCw>n%LbOz#BB@chQ71^S9Yw#?WqtU@1y4 z8s{K*uZ4A1_$7fa@Y;(ym!;~*WM%hN8;vz%cok1B2147^9lurg&>7i_pxZeK5kXG^KE!dgnFu-rL&?)9S ztK2wK$Dc?ytKnDyZpW5F{Q@-haG*BcaUXIT1%lllg4Jgoxuw?$9UW|Rsbu%l5<}4( zHWOFY>m{#gd%89oD&6WXV~xY!8%Ve2d>5}T;Ib(jZbG~Locyw6{56C?gq1rON@4Wj6~X;qe!Sc;$hFwRP*rsoIn;MNI^hI zsBa34TPW?1wnJkCueM0I=*Y;(HP?H<)482cnBC(4R^O1BVDIvh@^aZc6c$)il6h(H zvK)j)g2SktAK!tm>X>n{);v}J``^E;+S=D`*F%K&89Z`i1|6QN;3iyLToh<9;8nh^ z7m|%@;r+e_odM0q$7jLtpN@man3y;ePyY%p`MeVPPaHw}pUPuV2L~2^nwtwNE9Jja zPH0&ZDVDgoxh=nFO*sV7yi%?`J$Vic4w2K-BWwr>wYcv9(s`YK$L-|p0pm><83FKW zr2j(@AyAbg$@m-=NKZUZjHd^aIhAHvg<)kO1q_hV!h%twk0L#UwP||`S_b`NEN}1| z`WsJwX+HLi0#4dNwxnNAF!d+BZTGaaBAnvg4@#@yWyd;^OY# zk)-WBBKM?MRxZhI8nx7XczAdNW;QnTJs>S54NaIHBiM_V8KpVK+6iUYn47DfVw8li zudaT&%`=qn^UHYv-z&6`u53P+&xcca(hMWWnhgq&2$DEIUzbjHcTK*`-{$m1z>S85 zYRE??vMSJ}XjXPQ$l!fLPBwBVxOsdvkGCoNXT0U!pt{1s{%YdS#pE{x#&&j9X-0qV z`;U(Bh$SR2nn3kHr#o7a8Iqmw(EKk*8Grwpy-sYcM|#~J6zueQc|gHRIQjONp|;M9(T&!Gm-J@p_FO;jwOV^kS2v{i!qa~i zeEcUPzr>*^!@%KHJjAc=Gcz+b?7Nq_I)Fc$H4fQ6Ctviu&H*jjs)79<&v{Q*K#|3Q zogTEGU5du%*Q4LHDE2au#xn$zbK?D974NW8^<%PsedatH{TlT^{7swA9EuuQ9o8ND zxQuO|5V7`*-mYxg7#Uj|V4oPTGA?PM!3`Rg)b(A>`W3B@+@2u7h>Q!p^KGD3lv6Re z#58i$F`}VqxTTH%`a-Ka>hz%W_e(Hp!b5$$F@be7v13O;pMvr@LP$H~`b(hngQ8(baTxYPMG`V5)8TGa61{sp=zh46L+s-ho_Px&}sLm4}& zAQ@mFmK zyKYfdQAs4na)qqC-rQPNS6xvG3NOxHcwLQPCwf^DAp`@Xku60vdIBrD6YA_FlpT>! zG(NEH?Q_aDKiHLWcok~&N>N~RbQw(5a*QF(CQ6#D%422g9Q9ZxWHMfm0ys#aDD@3U z4l`oyehkt{_fkY17E3t`DyJtlb5(Qef2N~tX?h~JrT#IBSU1GuaIR#>Fx4wZO~h2h z*k3VDKd)UlHTBg$Op(NZw|R*>L#ti7ZbTQ1?V;d1t<5!XcjtOf7&kl&cjxZ@dvE@W z>x|c$&t-wvqXTE>D|@DaANGdv)3b!UJgib~6>z0d(aD%!_Vn1w;X+Dz3Oc*iV<*Rt z#YFE1%}}giMEbJr^ack!GID|cNI1=3aU;Ym2>Bl>**P&>%O>g2#~thPFZBb}MiW-C zmYue$pV*i0O8YAssxf`|DrWhnFp?9-$g5>Chgt>(zcA{raJzesL!c$6&G7gvkW3CIB+jz=j^KmCmKQe_gB);v(Qf|C zy)c+x${`o?L^1tkk@TT0DXsif&6LsxRf~en8bPm!t%=j)Xucqb<}Z_XXB>c%-w>dn zJbAKS@ut07l7N$32qR{XJlJ5RyvQ0#J>_pIODM_L+dN46Wq89JK`&Z`u{JDzeQr5J zNOn8LEZ@il&XsS$K_W?=z=Zoar3*tF4Z1h%C*%xB4fcH=x!Y}Zbv3A|$^GsZT6UKg zW`%Ze$Bi6OCOb^&MHgWrgI2{;K~bpy25H9!^JFexw3Yz~nSS$?BaT=oF3sZ|!RP%| z5FV)60lINieP1G8eh20iyxtQZS&@EWS)Bh3c0&v*5_&co$T0_tspgZd|KRx^g|Zi0 zkS!=}S_(xWi**}eMi5O!3!ioTu?0W$eS$C|4hSed7UT6@g(C?1nl`Kx;s&5 zM?}0vFvh2*fErt`B}&BnHJ$I6p4+!uy(x{3Jxt93C_KzvxGriAL$qnaLGer=k#vCo zHeoSfw8OjZ>jbbnqW$8X!lC{4Q;s(;9k3}!XRx8Byts%03ddMgH>A%b>%OTMqvpq` zoN9I}f?Nh8Lqj^BZM};wAA1T&TE|p4%eeZ~BMcswkc8TA8fC9ItHREJfe5;7!-NJK zx}X~xD+1}`Zy#D%y^L-`lr647aKtd2tE)R1H}mHa4vX!Q!#wGsw62hmBw7Y0_W6N< z0dl4{siLBdkY{Ti3b)<~nQjn{p4&uq3n-KNufw1QezQupLQ#pDwiL|}Roq;8c(8(* zdM(?yyA4zP(tSFk-Tc8<%9y40aaO~II(F%-4^TD#eQCDQcA7RF>HC+^wfW2_n>o&m z4mcQm>fHOzs#u#hX5G+wweG2_?)Or_c(3uFEPll_d1g+VQm|Lf8%O&1-cS00%Te3% z%@4byun;*?)^z^zRhw1nCe4~tsI#+7*a?la`j$!fE>g20czmA#Zk!Ql+Am#tXmVQ|JjJZ zce|{^BkW#rI!#6Z=AAwn3OwW)RWcOV22yY_2n{70n&M;s(iN8Tf0YUGzdQXmh35Y{ zj`*7=`)@(-+gFm5kO)4FWj#}3Lwgz|`u>lk(8OcikgvPnPrF&oN3q6pg>>Kc{rndY zix|-~s?dLSs8fR5h6EcKRJ59L4#ZFD4G?P3s=8E>RBAExg*cnKMu3m+)H=r!nEs3X zRU~~cF94bWt-akxWotQXGuPmCzcXINuZGYRbI#q2Eu z(WPz>joj^xDe|OG&Hb?hfs!3BDcPqj=>dph{db0+2g)!aHa0q+o)IrifNPe{zh^>+ zYT*9dN$!ZN!RqL&&MYs+A{KEW1g*Y(u)mafd^ccE9c3mBVl|RV5%qlsym?-FGEDM# z{4QMzg?o>mH`?sY;{xVBU3*?E*boa~UC}OA>Egw*j^4y<5T)W6Ta}1)?u7>DtBTXD zv@vu;S#+k{UKmoMa*Q`KGK)mK+D6w|tkJ6@*^myP0TvJzY^{4Z+w`JuaenyqPtaS~ zSM^lb+XhvsCHUcE6^zTHg~h8 zDTuke#VyGj%gFj|UA8dH9(Cs`H(HUk0gTXo?88llaPK~m`?1oqxt>< zY^oDrFzs2T_eI45{CxcYjp3ua_U#Xrp+{(tE*4Y^K-T0cjVqP)r*M>#eIduuq5|%Q z@kMG21~48d69ykWvGID_nE7ULy_$)i0|f@m%z7H1>){=VvXg>|{7!~orS53LbM~tM2XUu;rAlqy`yLC2k{F%bLQVO#^`AyWg1Zd{qc3v8yC;VWXWT^H5UvEVHH;_WI_J)mXjW6>kypeAYAzyW!x;r3cL-$_tKrSi6s~1>jf0E9LFCdEjFysNvKBu?xVN!bS*yg zEe8k&O%&O!PUu8t-#}JfPvpUvPs(K>$EYP_s%~o0@%_MC*vFRvjP~b));2jwTB<*H z_*))awrX}gpZYH;)+?JzjIvjaGD?-}V^L?{3hB@y4&T_Ezz{ON)%_`x@*%jZMqC11 zKHcgb_AkB)EsY7svPmff#5dpTtQca7!QoC4?EVI-x*wsT)W5)A?Y|>5xAHxmJG!uH z<)MUuqN|^_m%l3HvEk3rP?49kBLm}e2Hqt|jn|7tBIINwP1I3!n6sr7T&W0j+g(Rh zB$6b^>p~SZsQKt7m4*oH*c7x}4KBExb#^O=*CznPnO5^CAkCz-1xAgy3fIZTkUzy3 zv*Lfe#%+aS{(P2Cx5V}4V6y!lI0oOv)ODrwo(kXQPTORREhvYdbwQ^*hGd|r6O--B zeg0#^t9opCW-}Yk*0v%BitwNm)dKg;=w(fp;~uT7`tx@}w|8ItsA__0m-k6)$bwKT zL}YFepV-)EnW;ZkF%BIs#)yF5+TpBzr@KN!O?t6lb*td#@EZpa-7 z%EQYvTiJuX-lvtJ+I$uwqW2++R=0_B!87CnOf>8-Un}AyORKzYr+vyv&B5d`X0b`f%zOpmlq`h|v#9l4xt^_sVaoABZGbmGUeyE=btRVEPrMylV#kI3fY`;!-VGI{HWho^Ji;dGsW0)i8JI zCXU-^IGf#%rVEA|E{bV5tBcFh$f{^~GtHuz2o%cTeE2}ILuZgc9)7jdYRD%0A&W{; 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 Dm}9fZ literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_license.png b/doc/qxentityeditor/resource/qxee_license.png new file mode 100644 index 0000000000000000000000000000000000000000..07dba8b29b4b462fcbbebc1c1d5cf2358ec2739e GIT binary patch literal 26697 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 ONm7}p%$x0#4we9{u=EN!$PAra+NzT8&3htz+I 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%0AXrdi}D_dowCtEwOTCUFzFpH2^T%!ghgoGgc#& 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

    Ppfz1X@%H!@Gyt8F6tVZZrG> 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
    %ogyBdsH&dk zsXbE|JnVjF3#Y8dIPKo$DX>_v7n&M|Iwc7oorb5yVXw{B1+W+zAR$Od;yS*LTwZ$6 z@+8bmPIB^kHfAow!j0h8EylxR`{j*|E4F24b)k}v3VQiZ4O>(b31_}(9a9HwzP$fU zBQc!QJieN|cIes2M7ZThM)e$eHk|%d%FtYYU+f)&RFRq{?KmXF+wgFchJVJKkjk3w zx4E~cY}V8uT9&gg%u^SYkel)iZb;C^+v3Sl#lnYdgLe<~tq`VQHuwGeACN&4;=@IZ z+ise*#@^Dhka?qK?*{BEYeT>ikSt>u-whd2_UvPHwDL@*J3U8^C>c6{^cWbgF&1NS zoPE($=(o({pGjL&Sj1Cv)M6vpI#^BDBXL4#^FHsJJ3pWprR(DSN-zcjI-DHY$Xfba zFRn+!Wgg;}4DzdQ+)GB-7|HboAS5>|B3iepb+U8F1O!;%7Xw4I-O5_;;UONzCp~g4 z+7%3`4854ATD|xOu%y0UGDVgMg6_C1^mE@Maarh~b6QBFYvKA_s0=8E8T$-?2Xx_^ za66n&^$olLZo<&P{;ynk1DW*s4P|5?KFfJ~I`ec#;1&V}?c@%2Gyh%{Xt5oJWI8&02oQq~OGOl_uZY0CHBK&O_A2hj$cO4c( zPr@VQcYpXmv??(Vk0peqc^}*D{r6{mdHJ~6Ixa^dPeGd{Nf87Xc00S4`P}@)MWADg zuzBpl?&+DFG#M}Q&$E#1{nJ?D3cI>XpD~PE1Vo=I;*d z@6`A2V-pf4@}Bt#XC`JEdfxck0mrwz{8L?B-(_=X%I09BlL0c4A=#qG?{6!^>4lEg ze6Ch9K0ZWRQ(^2gTU`+hO`%cCajL{0>THN;_4A+JNQv60*AU7~EG--Q0SMh``9fyk z3p`Xc7Ew}BRD=q-k%N=lO)+rF!+mKE*u?WgiDeaKEXufXaH)nu?YRAx4;9OkC*cn!f>ILnc4m>1_nqp?B*MRj#`fq zLczrJV<7-R+2{0%ZFc(hbY6R7FR^iyZRjl8a!&5~$#HsvWz0dn9QnYb38k?9$orY3

    oK$C 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 z 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�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 R4S=nK)KzqlRd=mI{txmTk>CIT literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_linux_small.png b/doc/qxentityeditor/resource/qxee_linux_small.png new file mode 100644 index 0000000000000000000000000000000000000000..83812db5f2c2809bef51e18a279ae84e381f5940 GIT binary patch literal 30860 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 C_)kj! literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_logo.png b/doc/qxentityeditor/resource/qxee_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..debe4770851e4fb48bb80f0eddf689a63c7d2be7 GIT binary patch literal 122133 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 M07*qoM6N<$f{~pn4gdfE literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_logo_small.png b/doc/qxentityeditor/resource/qxee_logo_small.png new file mode 100644 index 0000000000000000000000000000000000000000..801d58216c73f0e053741ab04540ca58b79ab77e GIT binary patch literal 11630 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 literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_macosx.png b/doc/qxentityeditor/resource/qxee_macosx.png new file mode 100644 index 0000000000000000000000000000000000000000..8753919e582ea93a4ae80825825a1b05a6a72825 GIT binary patch literal 308698 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%0DpiE!LYjOrxQn3%+w;t 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�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

    nj2zb5P4uERKmnBeey#W>^Q$rn0<2B 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

    LPfx1N65`b#eRz_PWQBpM}87%l^e zOd?9uac@+J3K3CfWB0IftMoUn?mb|seMiR5SlxZz68Fci`l+NVDO8H<>LsLrB#;0S zQu!eu8=WjHJ@wSPp5627U;pVJ-uup<-0_>KnG)k%hZ?ndh8QdqLRgknDwX!_+t=CA z5d^_=&pp@F)Ra!8cRji5lx3$ZU%veO^Up673azcJ08pt^`uh5YhK434CiERl)7-dm zV=kA|uI7RT3of|ef|iz+YPI^r6Hg2b40LvO4h{}Tsd~D5ilw41If^lO1aZBG_^huj zFKaYQ>WCpC#r01Zav_85o41?R#_5@Yo8U@;078VSvPB}r87MH)7G9nbFO)rHxqzTZ zG33b5L#S1f8bt7XBE#tIda!_i6KlhUz_ zN>%LK&|LIo)u(e;wlW~!_oikF&mJ0DeF|<|oOD@-?)U9t>;$I20v6lbkpw(`u2p8n$xAufkqQ$>xZ zt1bdpW6H#!^_=yXC(jtAOJ~3K~%}ui}-(|0M$M>ey}Vn zpU>~xw=W0+&be*dixw?9=bYDl_xraY#_b^~HehRF&|n<^MBA2ePJ)yYq@p3~Y9$!+ z{)3ngtZ_XRXD{hvzj0uiEW{XVYHA9C;8UNv(Mcr!`Ac6>QURih!RCfSa`kzxhmoVg z;1Tb>P?&X`MAi2mc<`Qm`<`!Zp0{b!nVC$cyK7-OlWJhHYE=rilbh zOd3;D`M$p6Gc(h=T5l@lB9c;}AC!K7&jY?!IexsabHRc_Vb=3~fO@y3D4#X1`&jn} z;%}Zv?}@uWL+#b-uZIo4U)P(p9a@LZfHW45$hCkOpy~Oj`{U|@ml(-aB!dbN5E{$b z>T-~pZ^dFyA8VltLS&2s=!U&D*@%Z7**Y3jufL>50vUp?7=@rfQBVoJsxVM71jdnL zWRMF0DWwpmR4UG}0@HNHkM0>h_Tuu@r>$OldbwQTwQet@i*`yODb&r?>sWULP6>ce zP#_>wpcCK-=H@D5Jnc2-*<=7#Mbbpk%LhUsQw+-rq#_LlmlUGHk^~)~7m7*>kU$Y| z1q{d};s6$b&=Ve?0`UN;?nE_i<$63vk0vTAQmfhA+1dHYPk!pO(>7R^IXE=*^IzQY z#N&^@^2*DWVFqESx0817B#9_$M?e57=J(g0M)kEur66wqrF(XjHu7-Y~O0#`=3tAO&EX@?V zdU`H7eLV{%hg+>eV*VPlRiadJDP3KsOrs5m`2GL>U)`KVDe{9*5pcwaNlu$>i(dMr^TKamJoAgqOf2YL@|%~2K73y9+I4I9zp`)j>ebIYyXT6_ zE`Q?LmkYk++)$CZ_Sy&pGN@E!bIQynU5NEkF_`kgB1eOiGA#xQ3i;X&qt~rj^31WP zhi5LImuuIeGcEaCdiicXxLQAwY1Kgy8P( z?(X*YSEtVHRLxCy&-A~&Np8g9EHkcsd~KLk>l(SS|2A0);kw2DYz z(lZ!X76kvOXG2{WGUede_y~$$wvskoPT{+zXprSumN zL>4di^{X74(Ze$Y#IV0F)r1J8+n_=v;#Fo!ZT?dnSD7itJvuxLG4&dl?C~d7F8@xt zGv1{~mcB3LvYX*hV%|_+|5aY$#K+{WrT%Da(Mf_SvE%e`=#%$#HLwF&KYDYQhk`4Ur&TBDR)3etkS?IKp+KycuZ6D-*rq)Ca72cJ<&+hn$O-pW^t5P zp=v*9-t%ogqRkZjfwlD|C4_gmK$@uJPjb*qDT8{gMUUy*!PUFff#qMunJ}!hXgqn7 zWiw$x5##USMet?1qigEWgk-`2R4&J}rV`E2F-|jo?Wnxly7I!Xx6_2!r^y*xbnBxUteUplaH;_R?6wfE~ zr)W|}NPaOs;#_^ybp`UP;<)YlPbaP`qIm@z74z(6$s#x+_Z_I99)oY)iAQr)#6oNj&<7|hRu&eQ-o8dgMt=g{ zwICng-{1ci4cGe<&=ZMU3LSWPLdA-#&Dn$TJA0b8vyuEU{gET~2kAG}%)lF4F+6q} zS~l9J;b>dn6c0~Yj34>VV;g96VHdTVqMlFSc4rDKZDn~d#O^m>%Uwx@`eZDspG>!UPr_eKhA2l+Y#;vzd zvehCzM?^^U4cfc;*D0ewAMdd_W65yIr6N^)i^P(cgVh}H9Rb_#45B0{3Kt!RYOGCq zt`&ywqVL8-xNP9x6mnq5HbXWZbd;1Fd6Nk~eac8r=XSWprtH58$XV@4G2U~K2OM`M zkYs3#p=Ju;A984pIx4w(xI8|V<2(Wt-F99&pm~r3z>sb@KtmDUfAWbN`dp#i=wo7{ z0rm$FHMHUKO?YzITfefNFFuC~-reau5gE2V{yXc&1c@ z|Hc(X|Jau)_~@q?=znzRHJV0_ z|C1sNt2vJ$MG_YjHw}F6=V|5PV&kvnE9<+}G{I9(^##U(tvku=U%!40r@Dp{Ui9GV zg2-A%9v{n3@#mYEvyYs$S2Uo~Jtbou^FJzee7A@WoPKJR;eGi@X}WARt;y%2Pd9Df#{TJ&@?khi?L{D61uQF zu-*aQ?$|h5#Rv%OhacqrLi$pyU4`kY6MAG~!^dWzCGsDpkb>wBClHXKoJ{%{F5vyq z@w({A{qpdyx=<1q7q`1hH6nl{Y8nB?j|ME-&4Rb@B7rthY$(WM@+jV2AT<|T2&D)< z7sI>vC%&(?t&mjSvjy`|({jefc3AHdr)_IVJb1G^ZW@+=)O^JC^u&9CN!)_Ej;vRwUv@A@rA*=N+G;)8Y_zRx|KRUiKZ!RhE_r8$}omVd6I zx7MJ=L=%Ns2C?856LHwl?{eLK2!*DZ(UqZv*^9b9QIeG7RC8sof_P71gN)r)<>cZ#qKkps8-O=jJ<%9Y$E>$Ix3zqYSqiXl{7ou1Fg zp}W2+ayOE62k|%RPf4RgDCZ}IlWer`yUA2lD^^7gJzbXk&gb?2_JOdS^xB;gl?o!P zkiriwB=(o1l`>Tj8s8Eo5Kn>*Mt9%(K2^vI`evR8^P?3d9W%L5V-AS@9WY@p{hAH< zUi3d}Y3S&1HQ2bB(LmlA;voUmDEwYl8KVX53b@;g0<@t69NG?Z=Ity)=fMLzm zo#DS#I8ax=?)iKGw?V^~TpaBBrAl#i-30h~c^%ff%Bo&p&WT)^+Mg5n*oinbh0AHZ zQPG>MsUM#(U%U_UH@4UHJe;NvPPnezZPK%;A?QHQODqJnU7(#zEEL_d+c=%d?sm2H zR83vOW267w;HgMVmmHUvJ_(%=q8W$|^e&-G(u9PLGFVY0i1)0YyU~jMV4g}+)KM)N zf`uT0UsOcGOrn}!<%g9A%17hga{1Z&oop;wod?u~DVHW=%Ap42z4dX%FT%xh0`AcEYEfo;L_Xkdx{&Fw8` z=16oV=Z<6ZRH;I-QhIq+hX(R<>JT6dzdE+MoV4ua+!@!ZFE#upGy(hp;5w$t6fZ0^ z7fQx{0H)s$DDq#wy8HxbZ>sKNI$FOjmHO^A%;>^Wj?bv-m{Q*|W@~>fg^-4^l89$1 za??rz+alHUoQdjeH#J1NGOGZOQc-t01Q z0m^aq$NyH3L_vu#iFreRnqa3X!asL{!D;!^IsBdxT;*{ z*j!#IU8+`x)!v>~4LQtHL*L^aJ*Af{%*@mSaUY37(&FiN)BqFNvuf4*^>46gj;B9+ z<@C;~b13!CvlRQ@x_N_ow3|2{%nhiK#ovka4Vo!p$9+4EfJA+Qq!N;aMW zU()@}o1^a+7NP_*Yu7p=SLdiSof2U*w@TH7p^iP9<=?ESLlsFXA)%p}(IHTaIc(KW z0$yegmsa_TU!3;q=X3=d0hUdsO`EyF=HEnxS2uh6i^ZeLmES%u@r9C{zjOsTIGzjp ze6Jm!d~6&X089a?VZF@Gc3I+rT*HEuh*s7l50~}G&$oW~%^g%;C=2K$?y1K2Fz=}G ztw|z*jpVSn*ii-|WZouhYhU8e7}e6}B!S`4fJb5oL8OqD_~wWPOESrjQJ=l|r(WHY zaHZ=V`7Iho9GO88WM~6IL4p<(fJNII&#s7s1=kxVqF>yi6ik|sk4DP2*$>zF+51+U zI=#i?OkV$opO9%b7=&m{qjWGlN?-XQ_?(}4)w;;oN6J@PeB$Tq@mK7*BuTiEF;D!H z?+9HGp>yn(ASQhw8bobaad1C{UZNr$%~6dWneHI1E^Dz6-3KF>qBtuuS#g4WIj~;K zCM+7d6QQ6OD4Pv#TW(IHY6IQ_{hz1w)gd4`zdrfCtt9X4q%4tI&q6c2r}N_pMWpV-ZZc6SzI0u+ri&0liT9q=d#dCy zdRS5ikRS?ZzQVw%73-2IcS9~o6*DGiI)z@TlD5zFMyuKTET*yjy09on6zAatjzQ3$f=o>p>v2D z<3s$KV7LDv29eqPNa|_)V66M4F@clkhhl*G_gg;GSBteN-N()OzLg70x5AHTcNsRS z4M#JNOCBELz_yh7rQlR-$dC#(UWC0HR^9d)?>Dtzs zMM=sOJa)xh93=XpG~!E(MZb)A<9lNaLGgscx1!9Pv(}CQAO@3u373{ay!8a@{TbN` zk4RJK$3G&!AuQZ>2pK>huxbls#Y?KbFP&Unh+cMp)K`ds42 zF=CB5L5*{qQu>)PhP*9`l2TJs6VuD)=f@ku2CIchi{=tz7iVYS;Oy$WQ>={3tn=~X zM;aVtVKg*h1=mL6dwsN&q2hsXWa!WQ4gD#Ox#;MH7=A7;W}~$3Hfzm>Ey(R1|3z(} z?#mG1Z+Ls{mW%~v>W_Rb$IH_RBJ8IMAH(@(2pPlGc0ztATVP_!jy+rT`MzXNPLM}3 zci8n%`&QjPrZ?xlOb;5B?~TncWmV~BaX<&A*A*S~x(-f;AgI)#AqjAB;T;0>+3s7D zB*i2J^kkszs|R@irFC~1-(p7i_E~|nH-I+X4g+V&btDHM(~PM2qhf)ZOV!vZ5d9EX z;Dlh=w*BCT-^-Dx3dw-Uq$n^$Fz6r}$&1St*7=iZ%&kN6tUS6)lbO%`?^j7F!NQe6 zDFH&Hw6FsKaTIt_$myTFPUf3Q&TUqFUxID<|MBdF2~iZ0NS`$y5$((jDvf0`e6X@< z(hs9T5`z;WQw{qk>@do0*UM8C+LjwR`24I)Ao)Hbap;CU2h+)!-^=9oF65H%iM{cL zi#QGSDh8hp`v8fiB&=`~N+wkzB0(zc7~6SX>U*Cn`TofiM>gt;+y6%>Q$pM% ze_Qm-y)+M>O59*45OCUXWL6#pw+skv_tpvGifJ}MV49tKmMVwl*E!=zIkZvz z4|C2%^&JAGaB3TjH5Zcnqr`g@vNRjAM_rwZWjgkD5^vp$t&C8Ze>FbKA&8L0NFg1P zqWcekxPPP-V$fZ_Bk>y{7e@t=7>NWKiZ)+Vs4{ACFXK}QdyX4yz7dpLj2K5Ab(7rc zl$~5ka;(tkFMT@1qkBd*0bS3i!|CBkkj3|#)Mh!Z-7S4bg36HoTRHQ-a~$iW_@0R? z)FP$ryYyiQ1&TCxC4#;@g^P)}wv1Ps0RRcR zKEAKn0-oo=xDXqp1?k0o@K+A~SXTVO(PvH=8fd$>w@g1K_1B`QgNuucMCz|L0Mo+5 z{|+}r% zamNx3r`dpr0g_+Ahh6xHuA48EqC*a7-}>>=OPiKM!|;A|p^A>JL*hwc8lyd&eO@;w zsxK;zIIS-DW7$_PCd9xZ;3^GaF{3ZL@vi1NIEDqRC5T^R0qk^s7RJ65BU==RC?~U23+W;Qh^uq zKu2$Q_J1a|HxscwPCh=fHH@^Bu+V8%JUlXo7!Z_h&e?nI&RdXdp--IWBl%fp;TFkZ zeL)gMv5Ri?%H5YK7Rb~zdM7kuh~{0FGrb6D5wrt{M%ZTTF4UU7J&(P8iIFfZ^p-)L0KSl!;--CbjKzD%Zzz1jURYnVhT zGKhKzg0PYZy^Hw+7Y9>7vM7+oDsNb+Rm=X+g)G4Fw2-O$wAWd0n>l|q^sZAQ5gaB$ z6X+;|6FZz_yZZa|-+TK3CuLWs`m^ZK~%gJO^>h1~o_b_$}ik66iN{)g=~ma%DV@!I2Wl zxQ!p&LJAnLh*{zt&_TaX?=wj2vC_cbQ9X#@^CFE8<;#B!l*@bQ%h()md=l0Txveln zB?dA=cuLJ=aBh$3dtx?@HQX$!S15s>;X&w6*n)ZAnPwhyoe>=o z55Oa%Ox2}nfMIdDk4}z;Tz7&M0UNK+I*-fWt9FBGK-1Zxm^^TDt)4ggoQjhQhYs{J z1@H7Y(PhgO0XfE5TeZ#>jjxo6eKEwQ{lK{zOPDIV-ddLKhq8>I=j{Sq<8iC~T77>s zwD@t}Fcxq`{aijD_>G7K8wumK>jA@}vZaL}d~>`olSrNWH18`qs`8E2o%sU5Ka1EQfU*3wZ|*V6jp{CMp=r(No5T!fQ{WoXqG zL)3hYsuC!5*34cJB-D>5X;!~1(!$Nba&G%}YZvKiKLuknf<^!X1DDiKK{|&MLo0@w z4x*riA$8KEj?MkzWxu0Ln*|xhPI0J^Gs7Mcv*5j6<8i*|RHOk>_`Tn!yM9LtBL|&Q z$`=Xg!gwf~SqHGRo|P?^Z*Q}r2g(k4jdPqM?0d`{UUxA|w!DI@uJ!-`!@F`!@QOSlo*>`gWnS3QukhYlV@=u%LvlGwF>A_5eA-`ZF`Q7yK zp_W=5C1SW(R@i|Ih%z3F9EL(TgzWpi_ib@)&e2Kh?cv#%OY(MX7%5f8!6sY)!oD|_ zZTVTVmYsb-*}}(XLn-$$X^Y$Q_Lvp21;E+c-(H_kKXQ))DJS;}GEoLBB*r@ zv5U$6CMU5;L@;6Rg|~gsc=A38`VQ)flLc3;wd!uH7Vqq6&0>i-s-O$uXNed$xDD22 z3$c!-!=_XZ>mJIqwQMG41DC%D7QZn4q*pCFM_hRz?c~#PojJb}NjQ0b3n9p$p0v0? zQ3hUyN^XdSk-T7B%r$DcAOBfgS%5!&<`7eO>Ca zs=NxVK6}V8E=xvm-MfryCPYU>wa$~Dd&JiC+joVEmoRZ|BN+8OXd zQO%%^8>pMS)lB5g^2;ju?mk zP^mgu@s!640VZs~AOHqY6u7LC6Fhu)xM>_*Xq$M7we5h5i8XzLHjEkbQ?G7|Lf$3G zl~_%I|K{z~Z#I4J#Q&SY3lV3#4nuQghZ7UQib#qOgPT)v+KX;T<*Jb-hmBgKGS^Zu zJY6gg!~6iwd(v_c{^u9SbRk4G-u3~=1Nl+psq1j-GBU3^ENL&ut=?qdxer>(d%?sL zmY6I`3OAUj_qu-SV`LaL+Gltl*ks6>0y| zw=yer5)Ms0O8vQ;^#+Mc!)0z)e!WO&${tObBx6TLt&FWm z&V2w84iwx~X3e|lMAP$;s=#F!ftO_}wm7L_>!YC0HDI-_B_iZltW<7aP0Pt)lM76m zeN{!m9Zbij!%GQ zlwgrTM91zJr;MWB>EAc%KFAbK;1PTRD%XHeoOuTF)m3CJI*LiSXjo!(t5{=XL>aWBJ& zGb_n@hTkS_COw_)bn_;scWdpRzx?EHI;-D}9EO@PBjU434A#^Aaqw*5egiwJQQr+T zp0d1+SlbcnMPNuCP-&p%YN1@g3q)2bkBkhEq7zi+M5-)O4uVQ$JBl)c3vMJSo=w)I zbm_7ymFZH`zx(Tu?pL& zCColnk5isZU+(PO&&yhUsC(SW!D05N4};FQ1oExaz6RpUEsU((EbGlSXz-Owi?xz1O6i^#c!S{*FQ2!>X5#c^rLdaD-515o;oWH z*$T-sLb#`OOsl_5H92*GE-9bOXGGJN(0AWv{i5K?KW$r^1uI^4ksKbLa!suiG)MJ^ z83l=BFsZIjy*>S#d)*~4fia~D{f@6ZY^7U1!+ZDGEKe=L#@WYv?Ba`r++Xo^QW zd{XRlPE}1I9NA47 zDO1()IXHD)DN8yY+>6cSY2_B=vVZj|!>7^9BD5w(jPp|)LI&r3*q^a5ecK@Xs+t-v zN97sP@HT5p9g5(P$w~bl;g{2H^l3)|^y!+tww78UJ0m@(gN2oqpS_kvu*zBX?LPMH zo}BnWirOGF4A`6}OIAYxGYT)by~J1I(Dsh(QalG>d@>>DIwA&U%T{XRC58a(a{ zi-+M6(Uu0Q>{4v-5x?MoBiQlv?{>q^^MORztPa+I ziY-_u%3t3h(jK!JvBY?BR3!3)(~xCfjaeG;m$;YKQtDi2dykD0&5!M?SS_`APO_`? z?RHIqw=KK!IQckOGUbFYe*?O_I0`=zN;NbozVr^e)8}WAgAjlLe!kJC+=@zV*aEA( zAS_H1N_(K3fw63W>Xc+%*+6VmE`Gi0{7WxANhQqdkJlR%n|;_ z>jO3d&7Na9lY@h+?Vh2bp~Xqm3OU!_xvZ~?`J$>i&O7nllBca_9fFTrf&}}-{W}hg zYd%leuB&BO-aEE$C5jBAW1|r)Q9M$Q{WgY)1nhe7Scw1NU>jn|y`$*Jj0xOM>WO(B zXW3D}q9Rt2a*5EQqy@Ig5W_%ve0Ds8$a#6~=JN7L=0t=3A0xD8mZ5dt)K;CyAfd7T z-HlftbO{;kN@BKA0^z(QWh8lt7-PF$?n%l3dhjv}Q6IwGXZaF)0`Wm)5r9Od@vDi# zPquS+$ZHVH(s+1bBdf*WE=?#tgq$QpRr`HSTJzg|A=V;9LUK+&YcgtRw(tynhev(; z+{x_K43$E{aqbe7M?YAw4FdyW`S%0IZ+7@Z^UvFf{7+)VQbGc17`N$TP6x(oh0*4t zmag*z0V{N_FClN=j>6&)UU+H~4(iU~=B#@=HZ)QvD#WysS5&*pE_&ak}j)@yBN zPdy^l^lRAkslVQd+5^c{lH^N~+;A3`2(S=!%lGn`#X9rAv-h}6TKlI5tIj>x^%^H# z*{(S}jxF=r!zQ(Y_pjt-$}NfvA^IfAAMuGQQt0=nMt5qORmyMLLq8#PpYxx2p8@PN zmriR}&Rjm%Q!Q12yUU>}yFgP%o?@fgnW*t?aKDbGOX|bjiJ-Hbp_8~kRb`E;rCjM( zI-Tpm`7=|ZwmdHWaTKv%(-s*BmHuP}Zn~C52?ws>Kc5Nzd zOE@vfBV5s?XvGqW_=@PoTT~M&^C=O*5l->Buo2umpe|uY6EbE|=9SAycl-#H%m%_1 zf!IcIM!psA2nqfOCj(X8M7`@@aS3EXy*J=;ZcWiVzx<_@(y$^CjS8WdK*a9>8tmM< z=HFot%#kKyEdA_B{*F^a4<6HVS13q4?h@P@Y+@t7TK=}NcE5OgfJ#SV2vCq6-LuAm1 zO}%^uSn|AG57JkrWu>|8E|`8s_zl1eCgS5szzkCy+_43iVFbwB;bOHj1pB8LI)Ujf zMc*mmC`C@jgG5Rg+>Ckdws}-(QPqWh0!YO6*J;W3HbW&Xfr~v0HE)Tks-5b{3DPAL z@1;m65*I|)@<@xt-Z2L5^|p3iEv&?TYHKoti{jn5a-3#h{`%nPEzUlZ&9jT793*q_ zLqVE0%|M2Z(ujmZqWt0QuQdc!N4!W34<~ZvgE^3(YP@QPKSK}BGkvnT4eEQnYl~N-a~@?&zN?T9%DLR+hdtS+VFoN41S*YmIvzM zV1OHOnN)};82$yfF1884RpgnV&>#@FtI#oMQ4kBR-(<4edu7MN(4`C>In zxtaU)&}z%9`!L$NTF)Uk6;PRZXQN>JJ%P|1Ifj7-E}m2hFs?hf;vO&i{TM}0%ytMW z+7A`fc7H5eXzVl;Q%N#c_Nv07c(U$WXfu{A*1Gi)%>J6oZC_Jf&cZTo`q^6iXJcvK zmG@|q;^uQOa)~|`(n_?1qmdH@L==6{imCng@830Tf4PVDTJc2k%PsBf_MaadOUyG$ zcsRHxuiJZidQ^2hE;gSN6hHb2Wh#-JP{u+1m3?aQZ||blE^=FDZKY69sNN$qn|J6l zNufXFa5-NX09s$GnwXdYgy-$A?#m1iom&wjo3P^iJOOC(C_uhJ7?hf06jcojO8|hE z+?47ijytm03IroX_Zvlyry-nWeho8Z!=M>v77qk6?RP7qR2E66A_2i|qn8uFg%t6H z%Bt5yhHF-?ap!yxA(o=01tL2%wAJ`QA~NzM-~m$+MM#6njTsg|YL8#@_!IR5%liofiCJ2q zqq)k#czvXI^&VHcGT}XK-z||~(fT`@_1Zm6oS)3bDl#us6&PNcPSgA`D*Uu`-33s) z8Q2o!5Gi1WRr^OAL>a558*OwH$Uru*{sv(Hyk_E4P+WtxEGaH7PJ{IVP-=0HytQ@K zcJ+1aYIkohvd5ryq3xt$30gLt3M3?b-S_i1^5apx)>mJiY>$Bino~^Z2qn!N?plZYayFky6kGUhJa)+>46DKO z=~y1E(}cU17TkeF;UnrrzVqtEVeI)EUQ zrtEz1?fAyvLSL++D%-Fl2Xe-4Nf?-(+%(vhJTS*VLX(1K-NS(Pt-|rYuU9@yT24iQ zQuN8$r0KwIH2*XSsr-iK9QgRIQ+|Fz00psO!Pb@k$TRd6jyixXl=6{L`Wr89R5E|n z9rF|=`szYc;t0iytnd9M>s7{*{=30iYi>B!w?Bd5Qxt*{Q zJ@h&~b)~qFO2qUWbSA z^M}VWw?>Dc-fHu+<;ax6C`yNiQR{fx+v$;DVW6|8M;R~GmJdboeaeR|*>bdT;4^Ra zZ<$9!&7yf*;FHMhP<+{TRp1h-M=P;ad);%rdh5}=Nk=}v(>{Bybe>xOR{&(wU_Dl5 z{g1eGnvI2|oX__~NNv$B0f3qlRrZjHYKkf#5;}CBo{$?JEjeDh%B~%7;4Chd60Jfk zbF4H>L`3-;;#r!@q|9kK#K#YCp7iMzH+BXJVvIo`Q8@3;`_mH*Xzh#9b-Yj!O{xo< zv}yAu0u&aVEXZFF^^@Iil)q@``S?&JKX{h-mXBCSDq3vZ@UV{^25UeHJYmLTuiO(xvB~^JG{|)_<031xrVwO`s^N zQdJ*W&qeDji1+^<2?z@Y``jK+-psw1USfmb!(Sn+(fW^!4!h}$FD(tlx#A*~IcXsM zK~u;TFjP1B`}O+v#>Lec_&7wrxpK*DwmkVK)p|w=en>%i+**@9++K=4D?E*=MHo#G z#f(O${?^78wMhw6$L(P`pX;#$bit})DC&#n%E!1c<>tC&I_8iAxikJV`jP?y8w({9 z)}O5JfRyR;fr9Vj!(Q$i&{6jabYiMWFH|B*mrex+1_G-M06f0Z-`{_K-=vc1kJoFN znGM_%MorGMHtu?xsTlCNA;V7zfKbf2)daM@kA3IU9<%gF!2e)ogJy_SjnSVZN=5oU zE(jb2gY18xDJ<5v3$COd7LqZFio=BK>4L}VN5n%rs8jnvqliTVhMR$4v6Uzi6Uh;T zLyI%MQu%D0y3T5RvWTXo1jI*_sf>&YJNgsk^Z0xr35Iih4)o{Q95Qx!e}-(f80JbB zzTeJn|Byu@x43dU-Lw_P-al#Rkbt^4qtyvJe7*F&b(!?j&+tFDB~Cpx?tD>=*TY$4 zAG+iV)hyzcQBDXHdOs|bKE+PYb*kxm2UL$0Pg>J(pSO#s3b^>OK)Rji9!_ip&xhH^ zyfzba=W9)4p@*xUXYD{k`0IY`>ko~J)7ADT=GRT;j@O&2&OQFKmG&p=^78U%5A8Wr zvx{65LeA&G_Pf_@Y6UDLaZ<0fyUOOv=Ex}hM1zTHz z)Z+UDsLfi+a{}>$+jXXQ<_vIGf zOh90ezrBfAgyOd;8_FUPGU+g3?+&I$l-&}Qb{W~6bf&X~glvXpTG>yz))-~l{Cz3GiONU9heU9NxQPRAuYlF}?|*TRYhOeiOt4Id$wtqcXK} z@a?a-t^mmsvZ`wg)vBvm-rSk=4++qDmtS&z=#(LjF<~t#O|^y|rDqcz8>8Viov3?W zD=pQYz6ZD{Q+|!yW~;ilzJ+`kNZHi#9e>t3d5X&b8(EUB@H~7w=%7AZjZo*ABDO4sNfrSW(l3; zZze|BxuQ5>TR8(y@|+o4K-r9wxDuaN9+9QwRlVs+)MnBiL)S>BP*URT*gY>?JLYS@?jZzxk8?29& zhY?J6Oot_g=kVVNF2bxKJOCfQiy;wB!~YAfb|WDa)-dV=qld1$`9|y5*X-;c4oj~$ z=bE~HRe5!zxqrI{te@V_9q+ay^3SNP*_E&V%Ho;~7PuQUZy?ZIWR4A2cr2iv9>@Z7 zs$2tss@Y`bzWaU zNd;0Kgg(hR4mImGDj1d-ystkRIK46Xn@}j4wpZcm9y7iVP z4e^8g!CrH!Ve>!ck`pj7aaZ47?^4U_lP3p}$A~GmUD%8@QjFCvq%c5sqv1v78GgUM zSEZJeAZ2n{eq%lcJtRT(@fI8SSy?ZRYsVTiDu5P_z9~2m;HD^?_^vx~;ar1~1XJ|YlAP0sAVMXD=;AK5^FjHV~fzaY%45P@|c4ZawH*xku z!eNE-QgA^bkT}^u*%j2qF(uUdj64WdTn}6DEwDMTwA8lMp>4WUFaY2VSj}6wEC{sWi4RO5Ti8-q zlm$o+{ubVSGUhzl+aef|bgK1)?_m+Ks$?T6F(DSqEPf!KRFHOzqin^^`R4NZy@WE- z!cPCSu>dQ2u>(IsKmlHRq8Oy8QXvZ6D1@j~;FmWx3iSbQIr!TwmWfI~m9B0Y@!{Z3 zqE!dO=YDQ{R`^_1IzlbC||M@kTjypnIho_iP` zy#MR9Mn%a4|4W84T@UZ?ulwhsyk{TDIHuM^9&mKmp?#e%4CtaMBSw3BCcV8#MgOfY z0QQGI8o--OE4i4{eKhcW^V{N%{c?HE2PlD|J~yhl-n;3Y50|Ov7nQB6pE)?L&%57l z@3#aGkB&Ug{cT4^#?BVn_grw2zYE%GGo1KvB z*V}7hY_f_fvo=OQT6lhCN$a#B0*KM6W0tX!Kd;(z*uahdP-ZG8VW^b#(y41arkTq0 za6`>-SW>=J2*%hk9q8IH>W|IMR#T&e^GiY^F^GZlE2(#42$Vu(xE$u#90oaQ6CCz_ zk_&!yZgtHl5za{dl$vDM>yIVjMZUsCNp@{%0NrksJObfu<lt1xPF`!3$W zy=a4lkm3h+YnuZSPCo6TpDWliksyJjXp8;A>`P+l#lo->4EEyjT72A2ua|i?$CCq% z2zU7U z+I@L{+H~=Fgj=%Pc{l91M}I}$T$B_c&X`rbQqmX=5zflAT*8b7Eg$h$3Rbftqs`GtA|zw6j&9QUH`@%|waFH^5ZxSn+g|2~ z>U=%w6mYr+YP7@9MBc(#dl`r77_zyq_{UHKte8T>vcx|Hg1dnlnf7!m9E;E092+B? zjbDk}a;QcA;6;W;n8Rj6Bc%LA@>LU|;D60uG5yKt=E{B653X^z)c&c$!NUiGqVV!b z$Y?YDR4FfsiL3;^S46GU?#%q@==Qyfco;kg8tCn7U3J{jv%Y=4d%Zthd)@5h_b?-P z?bqqKw|1>Uc#CWK@JjI+h20NsNPAJIN8rzSaO zu`v<$_67xv*oc>Y-&>CCq_*cO2)@F>JXcjdy^K}q`f3VRg&r=1AFBF%d6?QWy)(i~ zH|fUK?2Sm0Z-up5k}jS+kZaj#Y2hPhLl8T@B>$Y@0Ft5{Ni5f?fQgIINvKAOiLZPx zCbXs%MyTQAS@!Y%toG`g#|BzFKR8E0-x?7U3X)lNdzlGyyFM1DrHz-7l-3z-*0sDJ z?}0(Y@(cEp%0$MKF=1KHf>I&C${{qsOra1A8wUKLruXMQ{PVY2Wa zmcmyVEIwY3IbQdmg%idDpHt3LB@qU}NBzd1B!SC0L`nU_L9PWCFWQFvk@}(^^XBMw zntg#{+9Vl*5%byE-ooA-U_$~V*1MZqMh2$stLB4)bO-nPs!m;*0klApVH3w@D_(Gb zL3P}iDV!iUs35QSE@rmL6;i5F^}T$DI>*i6Xtn}ie1`%?IM>JPiEhb9iC+AzGN%yx zPkad9d=g1(twQll(BOfalgT5=I5J_++H19xla=+nyeD9qVZxc2ot|#qh~tl^xSmCg z5e}q~5~&qtk8UXU4m zeuerhU?$^SeE~P`$@Qz<8y;zL_ zVD?ZdMUw-EdL9QLW4R4%C8N%sfSN?X*8zoH++7YhzLwbeqsZ!Z&Q&-30Ax^*6iAq@ z{w!FO)RY1n7Txfhwy4}mD#SgJ&4}8`ECN2tUT+_EbymsC!vY!|bEPKO(N2PH=vBIf zZ^LVE{h=<_bv)k>mR6cJ!T#O4aVmvT)`JWpt^5ZWX?b0}s2vAwb;|+k`k77}U^DT5 zTma5%Dx9HLxq)omN~>de0Z)ti$;&m)jMKpq`H+}!|0ozCF)|js*ze5-NtIL2&Ot}E z4%)ZZnIylm&x#nuA@eVtY@iflb7}cPL%p@bh_~G0a{ns zvo9#jg11oHao`D)Cx(Y@yc@rhg`YSgj{WONB`CF$Bck++fHv<446O*L*z?cO9raT)W4Og9BXcw>4Jgs_maS<$N1pG>}wTf{nrlcZ@XHQ%a$ zHtyWktCddW@|4eRd2b3!@Z$DzFleDqc%~&F=Y@BEP&%CjaEgBnOblW}nXQ~}x0%qa z->?e^Yd(vOW*BubX<*$_zFh05ZSjLzxnMI`2)+mc3JC&1z%XtkeYMRebJzTy z+bs05qul6X)J%Q}dE$PvgP=tieo_9SAJm`x*-MHXyc@q+Hs#OpXv>B^frzTc>p7Y|en8PcFlw&~bOX;;w{PVx8dhTTCk8m;{Fq(f@K=J)5lPw?PakFH87YYd*uG_TF%FN zBT`-MhfxIj2Yj3_4P#}x?kB^R<+BcY&XhP+G=l+LJ=Lyjl~M7b?z%{2c&bHyemx^& zFn&@b-O6nB8g%$sgjw{UDbB&qQVgntn8g<4!#8^w!EzO(R5+PuZ3TO-w#R~VXk{J8 ze67(6pYSv-bh5`>xO5h8#=H13RadH;v>x|rF1pGt7mQt9{>aDP$k0|w*%I3#! za!K2y0jNH?(#b1ivGSUmb7O=`AXAX#7GOrt|-3M8r*tYNR z2jf6A@j8VXqbg(08Ea;)M@n{#EbuGHycuHH#pP(*@=ui=Hz}GO53z647WL4Bs6RX) zvlmLF!yHrwe|SAoO8Ik z5sPApB7z$e^J3-lnFgB`05Opa+l!7L%Y`Cc#?1x%+H1#%2;{nv$ukVXk%fsB1q@>9q4mrDsc^b>+UDr6cck&CA?b+vTdAU4U<2e8Am?4X?cw7@7#JV$v+x3Eo40 zTZ=^va(s`^Hl)ikDp51aNevqrV@Do~;u1f$JUb@|0m%6WlhA|&Y*FXsZUx@8%kwUg z`+xPVZdW6`sbCr)%xj_Uc~XuwE;g198oMD|3mMsQj`Um!Lii?piNZ0vGQ%y%TU}G* z`Y>G#BnRjF&{9=s;OdI%<|2W_&gU^rF`r`p#r#I$l43<%FkLdXS`zc;Nv$^htsv(e z?R)8S+MD0{3bya1safiS~K)`3DSh-f%4hq#se`2pJwKt+r{Wp zJ&ccuMYLdeBO4E?ypPM*vTGsH3tUjAw5%H(E^(O4~RkJp>=j$B|=gBd!wWAFdd%U z&mOJ*!YC3dQZNrpjp(3bv<`76^{8JP>I2&od z-_+y`5xj0i5n$(Ol|gj1QUU`D)Xa4=uO{EmZI=lQ-H}vBaz)Zo!&wD+pOwR~inU|I zL(|_y^)?bgO8aG0Z^XZHp#D-`B1mWftd61$Ma`n)I_Awno!Vr1*pAs4%tVigD40YkOu0LXLL#W=6S zTd1wArRRQLM7;2;Z$Ll*A!3$`YQ37nlijGU4H@DWh6J{lsvj~;EX;PRH3uQL%ebL= zM|_1}4y?Va6y>pj0bef6%#L&ruGVQybh;pkM-kriC!UE`vD#a5fj@cBuTBn%ky7>a zjJj6)9Ss43M6_^U>8qIj?Qe;Q2x{)5niCIR8RU%pE9jBc@UH{Wz#T?!BQ;x*?N2!G z*j$b4JSx`ZZHzZVtk+VFu;3$-tzFq}>3#tS&H^lag5d9OW-iqva|8wJHTy#Z1=l

    {O{+8G|7~o@F72!|Oy>6UlE7TEZ2(|R&KCC`u#=rIV%{~v8I~|ik z!>BB!pIFMmNTM|j6SMPlP3Pp%JZFIOo~b&b)tkdK(_+FKX|i>k(kX9LDfyQIP(mv6 zws>tDJC;>+`1I0SqdKuHY%7(;7#s5)5D4c)$Q1FU5z7xm25C1=v5xD=n>sHA*`9Hk^tM7?C?A3wQ9G%Kb4Y^$n#=?JNL z6GSWO$Ykldk?us*zC#R3B)2|UFV1~&vB9C{kU$H8@V9q2*{rqvXkib}FszcT0}u>5os5YG3EXe!IT5na4yr(i1~pk#uoDS6E0g+$=v?`C9X z8VI{Tbzf{*DCM3m{pqPXR%5U$xym13@f_jcvXBgv@HRK!t$Hl2-OUntx!VeOC<<6y zSz!)67aFT76U$@c;h6<~`MJ7{x~I#HE=ocdp=R2ExF3)lgU$gL>|)*X{aC;%S0FEw zEMbW)PqWw6ZcJCIj13DD)n=^KRv0p(MAj7;de~r3YqeDs-GK-KeM=N;|7Au9YM;jP z{GtrgfF?4P{6u~$(O$S`y>Q?7TuwTjJc7A62N}Sk`xT>=bU@Q}l?LmkT1b;a&I6uZ zbhfe8ML}ma%r5C%`&1kxX$Ttd%mkrBUf(NC2T|M3`$?L4>nz9^W;$K+V=DBrUINZm zb+m_zP`}Qg()|Hh56$@0?dl66H$Rfh);BhLXZrhIFMm2~5VHcKVbXnH>bHv%4iqJW zzqDnc5f?!bgwv-m0O2T@wtC&n_F5wqXb(Pf2;azYX7!Oteo*H9ClTIy<+rYMkz@*^f@SsDZHu}6XZFj9+Hry$W-)ZR4FGWgZ`iHAF= z5v0E3uHPujrqJ_W373X4#RMMwsVJQJF7i^6Gf)Mn$sr6xHEvsV@ONVAS}WSWzwjLoc|DV9ub z^*j<|jyLDjR94Jxiuov}euYS__m@VDuhU+$c&=_lD5hnt2`|4u?bKm!$%UVamHezN zErH_pK?MwRU-ugFFv`je5x~&$`rKPo2mc-gTNHtU_%UC-$n=-}79v9^+aw2PyZk6a zy8JA8K(i|321c}XtZhE-&z{`fmUlvKL=8-;HCDWVDK5ygBr@`Y>s87|x}^3dvgGm6bDJMA^W2EQqniofja zoaCWKx@aF5RWv$oX7VEP3XfD3A~w`R1WAXYJM@Q1%fMcNl}PYg*ab5A#*ttvP~jUa z%W!kWFr)Q?4Ev{Z0={1p->9%~HEP*z+f#+7=jP@nW{w_nW=(bLhBY-Hj8M!K3HHs{ z9||?j-0ZD*H+fxrY>0A!CPBM~mnV=u$}oLyIaHJ&*KIW{B_z(dD7tC2lx@z(&hh&> z1D`ONvw5$b@F2{$9mPK!FfjH0gt}dEKk~S{S;y9Vsa&OfpsHjRr?xK}^Zil4?H3S2 zQKrUNYdv#|0OCN++P^`vS6&|K^7W$9QlHU$_tr=qLs{g9DXoQ*vCAEyEpM zNV?FPLb|X>X@tZ*WI8Dl@IdsTgtKU0qlb(XDx)G%0h!?1Js6LPJ3$WvMzNS)(gS>WN*kkNv8v`>!(Q zy;f)C7rw0^5KtLwFNy` zqQHhxY+XzSBaOyW9O7CaNte8}2$IMc1Y&~Gw$h1p)XVsjg4J*YmN-QGpBvYjpFQ|- zGu}&A*1Ek;Oh``-bl)cw-za43EPCKzoABv3@acW1^rD(he#mn6>MG8=pSAT+DkTf% zqG2znm2JRIcpKuja#FwSDjpEfref{s*`QoPJ*nMGlo0>)CW=W9mLfNuh6ikim*An4 z2Kl|zNH|zONkui$@U3e|+r#n8yw(}}1=1=zz9cIOc3CK?P2nvr55BY!m<1+b^~Z`=MthEcnfuLDr!-1uhYIui%_!;MEfbyxx?*zgQJ7|W zq00&2raUzuU;?Dfga-t#>p(KP*yL%Zba?H)yoH2lK)}UrC@2N=lM+UxBpEbvvWO@k z7Cqt*q|2PCW2{WWxn5hQsxB@-OfdBi4f(VLo^`aSU-AUvHMS(lBD}nxpzj^39sx)H z-=p$oV9s1zTYCklq9QA7;Xuzil2<`8IGF6h7x6!zl^Vmty+D_;L zR4icaS8h|$qCu}ZD+~F7)nGA}lPmmG4Q@=bv5jWNQ~tp-j7Cp~AKh66ahA=gNT;D) ztk$XlvVjq>FmUj%f;i)i2)%{Bs|5#I8G~)!bDG3>3>>U2W?PO4#5N1prU;!VgCuBW zK$C`^%v4AaY`Da@FVZ%Ekd--ksIhm~^Gm*19wCyyhY#mI2OD}jd<_?h;|jLO-Ljtr z`UlJIvE>zB&2xbt$Bdu$zUkP#9_>S&e`_UC>n&g={VH28`n@41{i)Sc^YRcAGq=^@ zpZrTf>2nl(u_=0}C^d+AJ*5zfxv08@zO6x~z;UL5V@Z{a^5);o$jC^xpii$;7SR8I z2nd$YX;TD93SMZQ*^-G#K4^S-Icp3U21H&@J=&U^n={!p=-%u5-OK=_YXY>e{nN7r z&IXI|;|1PT&yd+BWx!?@MquD8Yt!OqC~hLI*te2eb8%!+TIFf8kmvaggjg*Q*$-B_u{E)p(%IM6QL)E6@yqT_4`X8{gPMeP+NeN z=SuGg_Wo~&DQy4f=U}+t!9=z8<9(|bae8I?2u4_>7W4O>5)0ypirOYPs!OpEK2{CJ zUdI8kYX%s5GX|VE zIAw-xTB@f_5MCFZB2z@6d#6L&p!Q-rNX%|ViN9R;|gP>og&=WHHduCL`dXK z)l!z1{b+F^Q6lmIm72(8#~E5$ov+J6?)7h>#iyctrX=EBn+cHXTnnuQB0rHuJbzYK z#{}woUWCo=rg*G5^&MXy10MM(3>^r>|1JcXab_^U`}Hw5;B=qNV_BOag=uJzbvReR z&-$p&&RbTK0WXs9RQ*ph2zxoSIbdzoBAzr%OHjSdiG9&C#>dG129mKR1P`)d^Iu_b zok5>l{)oLGX!)iPreG^Z9yD#aSF9O9fqm!yN38_NP7#xsoMZfYq)6}7#t`WFYjSWQ z!QOsYztXEc@B;@1a&LfNa#qNA5|YGtVvTuJO%MM-^sn#O42J#6xa`Kdd>9HP1TyqS z(4nDEDfopIjsLv+6g+E7C~vtFg7oTs$nXOUNKANgDyNb{l)jIIyy~iOSkRA~ztEA| z`BC2Th#R*P!AZge7XH@l@I@f=!(=g9O2%J(?|;&o5t~KxPEJ ztgY~a0XJOLVpo9{o4In8Mj;L%Tb-16M#Ntey@V7RpTFs(oO$_Fv_6E8-v!c0)Izu= z-jsux%1}X(OhZ(^qKb=68RDE;niiB}@rpDi?=C=a*7y%OJG6;Q<+@o0hZil71@uIr zWchT_%_<@-NXFC$IOnk|XZv3;FEYlGm@m+kItR%$1!`w|6AGG86|i>aB?nz0Uqc*z z7lSNCVeMqWmM2kMAtQ!aryJ*iyph*HEKCX**!>p#RkqjjxkkW2#_Nlv?HJ4DNwfU9- z$}Qenu~$b>pn6q2G=O70!M)5>)r!%b(ta@{|vjR}X47qnV?kZxaoBME_AZHF;XEK6Feq&5cn0>`|?hFccKJkA4!-`}cGZMuR{}cglwVie-n%+DL-HxbVP~;+10|v^9dk zfqDxRZat3`H(ItoOh?5S;SIPn?7V6$Ovyh39nlKWQy}Q`}CqfqnLS z6`I9>gzSZUq@+V|8jr=Eg5#&JFD-Sleu&MK83)UGiy}@X(x3zbjHijUrxE3FaRS!D zsRLl7aBj5KnFYKub+9F*S(wiSlmLBbD5f|mY4Y|N*zPdlexDzKi3Ryk@QRFaQ2oC&-dGD$K;N- zIwh^YjBZSX`V{sTL+_45u=JowlS#?=HAPTi~k==yb-~ zd_Sa7KM?eNXy=InV1PN7dAfkkW1M`SAia%PVm2yZ;sSMog;#j4idy%TZU8D0FC4&? zE_1rl%o~pB@Q9Bvc+U{g&RrC36Otfcg8$K>*Ekoe7o)--??6*Ekx`e?gu@JEx~PfN%;}%pPOhl)sLAE`33vH$ z_r)M!EeK%_P(I^)by4Mn?=5F``WKRi>I`7@HmB2F?w_ds4tSaXFO?gY&*P;LKBX*T z*Wf^2yHy95X<3T0Jr|8O&O}#OflnaH5O;tz%b6D(D8tp|nR8fMYW9*n6Xy-w%{#<# zl@EU(z>zj5M8QZSv3V9xb^Dmn{nTNA!VCBGt`eEhU&@T$aE!&WR5@s zYj8zJHz_cc(;=`TyZf+3N<`4PSr-?GYtp48^suh(^Uh&aSgSMzF1^xSn;qF0MKsol ziYk(yUz9B}kiFLn9)-V@lFs-is!T}9HwZ3>@LmIhN)!&x5fo10`L!dxBn#7$XS$ys z#9~8uqn=Xv-KmDzu=AttTY79adCFcwYcoa38NV0JyZ}!A|E4M|v7 zo>jSLhtWODM;6qYX!jU9ITepDuDloLG!c?4kfAS@w@O746&ryj+QIDfMn+f;cHpVm zHniMzd;~#SPI8?O>@`9rdGbY5Mucc)G=wxR3rIO|NO(Ig#>tS8DSeiNgrMtAVq6Fo zx|u>EtTttmdI=?;(Ht`9yxl2NZc$idHwTxSoe60OpjwgIj9v|YeZD)MklQ)#l@ICR z6QwCQfo)c-RRb!Gf}~t8dvK1iAF_v8g+cD(#v`gO7q*%e#=@*{(Fr+&+heg#kt1S6GCd`-e=SZeQFzCr6B2a4FPo z7ytjc06zx_9mm1dWpAwDh=Q`M*c9*dh^8^nxK|begE}>72Q))Pzx-ZK5_H+!UM~Jr z_pZh#r;qRU=}?7!x%KGWM)g=U7xK?*Hps?%;mTyt4ci1pO_4<72KuAsqct5rGP2%D zw^o8ZVc8p+eAn@Ddc~!fT^w<~%|07To)N7eNyWK0Spy7~pk=}x!?paK+AR4GBX%a8 zCx4m|e-J<$BftBQmX@PGyhr}L@KJB&Gr%RGPhp%A31||Yh&{#sWd$37obZ*Gh0R#I zIXSUsE4Cf%a!6eJ8K6uoE@sG3m&}wbyj4&;mkzj=EySBEKp|?>3g_> zX60>^*<9?6^6A$8P^Qbs@iJ4Nw_nw$$R{YPCH^%se>%nN=<)r^=uDPLs2ZEFOQ{-3 zEc20l9F2<|3Qhcphl8eWGj{2A^kCq1b7MLfDn8LtrdE1|UafiZLG%bOF_P!w$j4X~ z#GxIYgN1{Hg>c}@YoW|Flnetci4IpPnotxXlh|}? zq1X{461`iVhyVxU8iy`({3Ro4L@Y`3&*rgSazEstU=JQi2??Qtd4FZuJ>3pbdWF!g z>Y=&cKYzlcQq_htIDQ(>RM4Mt@aI(U2PX?HOe9p>&c#bF;TK4hf7_8-hU%BNhlLw@kj4sO9m5l z8#p{a?j{;U=JjL-l#3XL%RTvIpKYK9AKi*?4(*d3t)jzW70=n%s_h zdwbpW8f}dHjhvmG=gJv@eRIFbx6e($=4|b?Wi2-&$NL=l9k8w@74U2*Eq%PZFll={ zSR?yDRIF~3E^qGa{7acm=(TeK=&9B)C)joAi^DUZT{0d%K?Aw2}IuXyT@BEe}|p)ma2w! z5)%w6?gbCZb=n`94B_!C%oyii;N1;!u6fXmfnHXnXs-IbRuY zws<*O7v#5)BK#?Voe3_HilftBA+Wv7+6$Yo#^btxlYbJiKX}|gH;cT<@u6q$gNXNb z!i3yM-oIBX0sk~_aj>kQwHzf&4+g$Qq>R<$3o(cI^sK|DG)1Tp^M)!>jXj~-^qK@^(jWwHNwVrSQOQ~B-9mv%mjNZ|HYTYK|#e>I?n zRvVutS6zjRIy$uD?BHjmST08o%P7DS#`jT^D4UQ7G4}N95a^kgoA6$IJBBwwaqR4TFsHb=5U&`768sV2`v69mPcRi$Q(n zuA1R`OEF{LWR~90ieh60NrS)_5~|t|)YPxKR0eIyQt%NV^qtwhoTlrEZ;xSA=l~O| zX74eDhG7Tn&+f1^x~q^`LyLIc)gC&Fk#>Me>{4k8;er=Obk*IPN3Sriw&G17B_%CASelE)nfY;^Lp zCWnEH$zXl;9DiBqUbR@ueKs4B%n>}VgOa?@1jhMS2 zAUtzmtVoF2kcIJJjPRWCMNLdhFqY4+rg(Nw>9?!=d~b~k$)ClV-0X?^W*72pb=(Ra zt&>gp)Og{xKpK)i8C!Gq#l?&An#O9bQUVJxoKvbCJ6KE|qOi>MAPM`zFpiUC8mZCH zVCLe(lPT!&@{68@H#M75jvD8ak^$kePeTh0zp5&enYTKnC6eV(Pv>V%9dR$OhlDxe zjA{{;%td0{93|aA8qsnzP^2jRI4uZ;u1lL{MwpAUw)OLKW9dN-`RHsJDPREp8D}CC zQS#SW)5GS6&%b3!7j{`jt)X3Oh20E;Cf9?^wWnPm{reu!1bOA8%2QhuT7<2Ob~8V2 zjw^e3@Pa~(8ft24d~Qzx%ZEj^_E6R0(voVi8cYNDU!x>Cul`m9JRbpK4kJQmmZZf0 zMifAMQ~~H|i*o%Rbk-hb!!b#Y4nMkV0YTZz{QhnrkYSB)hA1aS!U@0E>Xwk&A}z&- z1J18so!u|Dv+IWgwnffhfNCkGt_R^wlqr0eVHW4qAEJ+w+HBV#VC8tXFyBBtPlw3+ z5cj)}p+~_LI}q+(t{NS^`C)3?#x%{545p@X-kX%nid;T2rih0Y_U6+RxB9eRqfDRv z)lM-{TmFLIzirE&)N%l!z~gF;5;pWj+TJ9oLfOS+cfYZ?ioo=T1|8nk-R1aktKCub zcYB#K6*yeBf|6gGlfGonAE~y6E02}I@99GELW43LN8EHi7TmXeboMI9lZN8aQM~Zb zj@rD7W$?a_&lWsHTxRt%2^BR~eyod_029t**$fj|WN_^*ry*l~wH1yBVTBvX$qgM& zd2$X0T#c1paBVS8-&y1OQN<0TXk!RyeT{(wVKt$8(C41%%uP>BI00sJ2+aNEsS!8| z9?u`e0hC_be=m=(&+8o_Z7(+DUdI=Gt^Q|oQTr=r{$qgAtL+Bn@Il0>EWc&TpzWd} z84Rtiw(+)Rz%bL)+{;-B3G1wyRr8QHuRL!SP915})5``3mLh(;7@!QqTeEH29)g;e zb%;)!fivSdGyK3l85ONJ5g}d-`xLFo4AK2=@Yk;I#oMByvBMRZBm^6sCqhBt4d?vgtl<4m+Fm43%q+X4| z++GAr7BP7X1`sceVA>BHuXov<#0*s94We~ELzlZyd zHO1y2W6_Y)3|}L#zmw~CF;XRPM;u3ceDiV6x>V;nfR ztQ6XhBZ7vmp_wb4KIAEs4$7puCVzb!80$0{O{p51%A(igd}}{;%fGiPRG@Nobv0n> zY;L?ME%LMhK*{g=9iEV|$^2gK3kwU`P+~7X2{`nk{M0A~GR+Gn4|I8K1m98_WBp7D zkAZw{)hzpjHI^l8*Gzu}(&@3b8CPM1qRPSrQH`X*E2tc;J??7e3jPzL%GTiCNpL5v zulio00bcR*(`7S?g0j7j#giI)>T!7f!jqn2L@2{FlR|?a$f#{u_@# z-}59dVytD?0gY?!Ej2tgR)Pk^N|Y*g=qtu5Qg?Xau1TNL-@Q@6l$2DPnUc&=?)#}n zLD*xjr%CD-j+JCqKaOwVy5H_%9f{6UfZyg#1xBM>x*OdEI_t>VAC}tcnRLeRt)aYq z86I9f6(RxrlAMf;+ez6N5Lh%~R!6o^%oN~t$qmW1d~O*EGuBz7m7t!)NH47aX#@&n zM-e-;tXq97UuFILG?OlN@NM;K0+TtDtEcyi%*T6N5uq!kqCErICGBE76UU{E@`au# zR z>#)B{+u8q8!$%m`pHQVx#=W>FW~VS!HVX_qB8hZj(k$l2w4?Vm(&7&$M4+#AJZ`Ma zdy2}nH(0~<{D4Je0)QtKNbHAd-f4-&zyB>L4Wlm$U3dIzh8y56uOZ}Uha?&#w)T!# zM}R?`zZ)mW%63Gf2$ldMT#UcEmWx4NbFwzvQl9|>z^+E1r?0fU!c$pR)T#z+h2)B3LJKF+Jr{%{fNSKe@C7NKRW_1y+{k zs>|D6pT<#oso$1LV_yL2o#PTJgTSEE;&a>Dqk*mZVP$0ns58Lol#`Pa9UZ+`4Gp{P zVWwg=;PrWEXo&oAzEAl1K&iQ0hYA0z`Am3AK464<&T;fAMvjxSdWuuEmB6%W0eHIC z*43DD6qCQ)edWxiT+Jjhkq}6b&k^u@I4rGLU0mbf=BYS|P0Z7Xj!*%OykmZ{4!CJs zt=-w2b|wUy6$*jGiwh)*>6Gb5Erf&xMdk#@br^i=uyol>e>H!;pXyW6zn&Z+qGaSQ zhL{}8~@cI^)qwVhQ>tSCLxgSvsTY@s?*}O;kgyDc)CHH>V zq8xDisT5xWCiGF(;kKiHOCr~K?_T$%MZDKv9&zJ)2+_N~M=~U%?L7gp4TWW7?XGPZq%cdGjoK8YdbuGrkm@B zQ3e%t0|Uqqd!6$MJ9xHyii5r0h*D(gJGRkp{ywZ4lZy-sbvf36ZG;W7`jb)C2TX7-c{Y9B z?Kf{wE|~13lUPP$3$ObFY^NJl-UhcTH0Z;SK!xES))X&ac5Okkm6Yl>>Q^A%b1OV1 z|05*?Q3;qE%iKD$@ZPdpR=p`Ai%-m#7&o7Gb&JY>77`%Cz!b9xa7Hx8D1Xe3c-9pK z3ZaP#I2s*c)fe6^ExxqCtkggwAe6n=k%sk)P2?A9s_pPE(Ox zvKE_ftwGkdwoR^9+l86-t&SVI($afmz^Q%x-3Lz~w|Qwv(7s`#cV`FsQ91{J%$@;> z4>!mnPkFf_|L&5TJ>+P{vITj8qxD^!KwX;Z?&qG(GX#Vmn#OH}`pvi5Rod)B~) zI5Nvyz3ULHtx~OsM?^RE8Db}las~vgo9>tn~7pSOaujY1_4wXO52SAVJY4a3s!WA(1oyoA< zGbl5}DD4vG3b3$c(S}Zzourp~yPykGFb$#L^`I!;g+1HT_9WL~mq>gFHoti!e0A9W`CC*bWWAlA z4JkOEIg;HGr`-+|D1l>3g4(Zuv(?^h%kOF4#P*|BE)kMIxc8Hq%F%F;uY`k(88Ten zPDmhT2|aB8%0=_%JE^Z>CL@+QcvJNL*3$SKhQE`qnH*0z<6o@Yz{NAMIExTS`z^>R zI`_S0v55x-g=EH$$qX3{^?jpglfi+`c-@HUAX`f-1#WXP3y)W%B{x23?Edje&b*f6Rg{#5VBQZQ~j ztF>`W5bs~YP-@lu^#E%^CcA4^7hy{#zCKI(FV}aSeFw)(cY`*&TlMCugR zD8v!;rKYAZExE5q7&zo&h;ybAu~i2p&`v_qu~Pfpk6rdgYwK%KhlG7jk2|l2ySKLi zn$+~{MEZa!Ul%NsCx@JvXPdD%{;jz-lBgsEWoUk5cxmw2ec}+{1L!7z5T+ST1HY{}-jbO^AbY5M*>C57(;~Vj@rZV| zV$Bqlhga|s=qU=ZadGkA@JhdM{Fuo2Ua1vM!i-7~A$58G6c=&Zc77xM(@0M}f(}9L zi=M$>|G0Rj2#vdh@xtwHtc*z56_&{{oHDGYX!qGO+)lcQ0}7?}S- z-cuVC$QX!0_)Cr3G{V3qe;Wl&ta?4+WN`=&^{{8{S<84$U8m3-94mRjiSs5aqOOY> zIhl*zxR;;8F<(epU;QCvwLF3~{vZf53mtJiGQlsD2alB)8?h_w0{wpT#hk=Urqeld zYF4ed3D&&PSbe2W_k%}JxasL8!r*>fuugjb$$)dxyBelsknz+zCElxFu7&>A)_SPG zg;drt2~m`*UbmVrD_bi=yM)^U<_5EB`y%3N%b<3>d0$LMGRy3v!BNgA3cqdZYCOs+ zuY)AS86F8PRVk*1N|=^!A-1xLAJ8HyV=NpAom)FTo3nT@|BB%VD+S1~a6ftH7!qW# z63nzy%+fb6>wWzG-c@{En)`B?8Nkg{A{O^`uXTS-;4sbse|G5e;U`_>=h7g5zaYP> z)li2)xibA_j5=;BB5}sqkJ?wkp_yq{g-c6IBqStCIYQc{>dj38oax8nuNUOcM-RDx zF-k1&Prz#spdtF~ae6#|rv*EN0dJ!Rs0(V}^3CGZ2lxo33U~ zHHmF%`oy=4_dn}M)zCT0qyqF9p{^F1YdI~*nHftDb9SePMEi#yEN!igmeCN}bQvtE zdVlrSFa8ds;IP~yRiH|YG?j^E&#czd_6gL?H7HReNVRGdqo!1QYZ2Qo<{IcrO|Fcv z-#}`B``t-{cP@ky1qq`A&=_gs_ha##g|=dXK=vFsmwH~zwu^66g8ta-45E&G6yZ8T z-s{xLgTwkoK~K*E&vC?oO{D2;l7dwE(D#%53N6ksS|BLU$~>Cfm@8VKB#5WA$LVhb zyPlnYRG;xv5z!tgQE=rvlBK0}PWlJEv|I;UTYwWRX-72Rw6d2Q0Cdm4w=HLOR#x{P{Wi%zv)Z?nl^#(AlWq*{~@`k61^YU9*Z&WZf1DB}KUko21oy zIq}H;suKX#pKRmboGC+%qasV6EVUi&(!2Hcj2N3Mtz@YwTfSP&BS$Erp_B^)fAg zm*nLk+#38RL6z^*>Ib@+MOyKOs=o0}W3GovZ6xFp@zm#MROc#uSX@U_te1#oHn^#e1r&$lrl#^i7P`DL+EI1u`c z+I*ss0VkoLKee!>WTF#c_9 zm$@Jo=T>N=4Irw*IycK1-Lv^z1g`%kBv8&N=(?QL!bV00)CS+V1_V@862F(9798k< z`->z?9lEgu^ueV{zigc>Exa(F}zMKwL*7|`3Mxp5kK`RP!YZ|Kge2080>d5wPw)$|R zFihCxut-j!oCmo=57QB+_wBDwhc@C+&=1rtf-=SqJHg;oMfvB{d@|>~(eRQ){JlbZ zFdXhrvl=F;eS;9ny8&e837QPt>~}eQ7pVvenAxWtr(q_kr1l9ZlqgboXCY^6iLz4( z1p129KWt|(Ckq;jrDF5~1q20whYE0v5El=zTgrW0-jn}&_8%%X?Eb!^1MnuXtN#7E zr=6?}nyyl($ggMa7f z{vQ`0*V2ZpUoRu0wsF0^W1a9<45t{kNfgTPAh^OQ4R*g3pHk$ zlhH;6?FjaZHxyKs)5&3ZSE=R|OB3OVX=e32<}9Nl*EQV7`va}+R>kx?)TA#@_fHE0 z1n&QKGdS$&X3lNzkJWnW(rEr{MtqJN-Oxio$7}i(LL~T&4dcp;T=@fub{WzJW((}c zeIV!JY_`9d=Y6M|ld*eYX>~pA0M2hz!u^cRt&e-5OvEfHl$-<#eXt9}z49aL$&`j= zyUmliH36Z5p^jguwD+tB2!=VyDuBdmPTvPsR8&+^F@_lG(1$*~W@a6qulBAx;O;Uc z6e}1Vs*XbG2%_pq3v#*9Gv{7zJFa+|%pEZbh6|TR>0oZXV3NBb1mV6Cf+4 zD_XpszZ3D+*W}sT0kv8I1nkhSeCVU>o(WbhBGM)qICDc>LSn;Wd@>Mg|j;GTZmqiSYuH zzpV26V!NSjUKKrZS?;-A3?arcXjCZBZ9bzcqC~AVcQ%-PlyT+|Fp;;uKA#0#diYz+ zmFrdfy^OA;`*Or$=&h+f%MjOyPHmr4t_+f9wR!c)-GWL{DRPrJ($%UF0Jj0R6f!*Tr?2BxJAI-1wB>dcA>v)O z&#Kpf>tz=s*r&vH*#ecI6YF%J?r9&o5fQSJEtlbjWyxZTsPMgt%|Q9}!j@75W_77vd~~-&HlOxjn5Q_XCevR5N$^!L(7WkaUD#P;NG>`A;YX#a}Zd3VpAy zIK#=-1m$)<>l1m|AF%_5N$Nww=94s+#*xa8SEyJtcX07$nh309G@LOW4FbE#x%wI6FX4NgnFFs{e z#+et=X3hq*+nu~k!oh%RuihB`TleOMdVajj+e&OHMVnw3k zg#}}B^p}5a$x-8TbEz`agFBb&>lEE2C;weNZ;s|f?$$$8RaNWk_M97ISeo`90G9`( z_;{)QwmWJoKYvT?JZE(ku+)?n6@5wu)BG67%gdV*S3(Lp^F|f@XhFnWBC+1Ida8M= z?|;8@)^@+xarTTvvN%N951EnmsphWpS*yI&Z8o#~D+RHJa*Mivg{rsqQ7bDMdy>yS zvW?e{p7x6U`>dn%ZJ<#bmOisKKbu6Mjmbdy2B#pnJz`wbe+DO5vsbfbUuV^2>fc`d zW1jP086TF_7?eQUKCg?O)U|*k%z&E&7vYO{5o~iP5Ub+xkSOCDPF9aQzavgCc)$*h zB445+(Qw}-9|IJmA&OIjs3L);_EWNCqo~N)CWoIz-0!uOn$}jGg%Uvzm6&A4;`O-m zQ;u(|8Ur_%soQWazJsh~tmo+c^71kudu1N--V2$u)Wv}gN zXfEJ>=#i!M&UmN_Lun`)k5Nim`oB322z_4j+={!2EnUbz;s)KU+y9TIvy6(W@w@gA zN(hqD-7VeS4bmyy-CYvWNOvhiIw0-P(g+OQ-3`(W&$<8add~;GFpIUAQ@_~z+E;I5 zzI7Y?4qn@+bC6*8KvjG)8b5OF99VI%RYSt;JWH=*m1n7ar>*k_9gTn*m7t31`;q&a z9E?DG<%*BvbE4jitC!E-76ScF-APhU$FTPh&i6?h3bbx4D+1 z=sl`w-90T_eit`Pbd-cb!GcvgUEYVb;`a0TIQ&ROCM$!lO%M@;d0nSc|Cc59L1v3# zcB$*P!@*CFUN$(}P{DqxL&$^LWHd#OhYstJ^O#wDN2LX?JPtusT6U7ktb_+!BOQ&T z`Z?`)Gn;q~*7Taca3HBz1_XXN=09OrjHi}uTJPch0?{b^Vq#)F*&y+4TSQB4P`NFH z^9`yjRR*`r8+p2;=&tzKH#SDd19gG471au*X&dO9mh%sxa>1I%rC}5Drr3jTnH-oL zsOG|Up4mANsu7w1MDau}7g5Y#UwBl4uyt8+!f)BPj`k>M97Ph*Okibu-}8}Pp0^t<~sM#dF|)LjDt3G{`E$NDC;%mZi;s;b8qG7+mF_T+Ex|{lyMKD7Pg6^uL_F znuzj>T#P(aS4~&sg!9Yu+M;}Ou4@H2N>OBoM|R!)|9bz$Bsk1^T{i_JB8Wa7KZ`B4 zK}Bd;2<5HKDrrAhjbpAjUfkO>7Ycf2aS4m?rXQU(Mas*)C$v@J)Kazug}r_4H}|p{ zM;u>Vb-ViKAmn|z?73Q6`21u;3=^7tC#&WVT5Ealj+;S@KPo zD+?D#a##wD+%_|A2{y|otSK}>!-?8^Bbgm$ru>Fj)VbdmsY=={l!ld&0CCJ85IeLA z3aS(Y?E{Dh%Z|UsH1% zNt-;H@&jo-5@mA()`IrI`Itb4G5wvcyaw!!TYvbyS^Meu1d_8?7O;%tuyV z%fHy!f?SZsPpoqWls$m}0(39e&^yHNkC;!Yk4Mcd^$ET+QH)PI$iI5^qiZpH%XS#~Fm$&NfrM35*O|?!=w0Lr4 z_jMZu8q@z?j|m9B0C0^)g(WAbpO#AuNC{Cz}^q^zvo*6XZJ&DXb^SaLd;+_ z6clPT8q?I!shasuY|X(TAaJ1fU4zjthK7QoX3u*xmu^@^_+j70rS+s|tn+TNY3nPF z)1J;#rT5<4ypxdjF+D-nEuR3j4bfC&3dWN&!1YRXC zved!clfCxa6nKS>=?*nBl}BxwbW6ccqW()xd|XjuP9dVHPW>{xsl_?8lkw_s&zrhQ zfyWqPS0)$*%Un7;`33M@Gyy;BNOJ1%-UW`5n!8=&eXB$g@46F^mfe7p&#kmhiGa8J zZnjY9g5Po+z66rU=GI5+S=ihYgO);*#V&d^50r@r+<(T6AcTcs$u)jftT1_fnENm0 z^|#xW$?LU=iCf=?nabL{m9d~xkNd!vx?#4e8GB$r01O(olEhvX9cr8w-2ePduSy+? za9XN9qDdVB(jrd%?{^aRoD7PwQ5%5%BApd-6;^b_aBH4Q1t)~ z)w6)LN4(fjKO?xV1#@vOOgJ}{Ne_dY_nxp(H9BF-^61vfGC5f13&rz_$L;07S>eld z&Qs1i*^h0u!Aiu9OvUzXr3#9ccvvlL%y{!uJ)O?nl$Btq#E+xmPsmPq#oP3D6~kjD?^t{hSDed z4A#Fc*2{DQr9{>1`QYi;s{e-N{I8Id44Z4u;(qlL5`wYJXRrKsxiziTORKA*qN2|FI>5N+Y+r!gnWmX)$>Y;8)R}hHC5`}o#x7)Rmu@pp ziSY;q0!_dBvewYPazdHY8cPxyiKcKRMSSfx{?^_(9v6&@O+m@oz*?nM=}LxOYrsk% z$W_AV=;Rczo9%@E`7Y4_B3r`eBIxq!5D@aT^_rsb*?nlIdat7?WbF;Uk>|zsIdC$) zJv}=wE@0KwY#)>Dot((YM8S>9w8H(_*=_M$Fv1?&KS?5cgVB=x{{Ag|&cZgYFLQW9 z>Fp@H%%!t_t*}a@7?aMV!e?5niLzY2DsBo+@lj;BFm>Y3WLv`7UC1+}m4cy21e{i; zQj5xx5pS-NY6D7!&yDe24C2|TV~g|$lN+oD6&%bB92!{P@hb^p2d2cv+w)U|qUZtH zX+x~_j%wA_Lv`u&&dVBcT!D0c;P;F^jy9md(Y4DJ+xJVdB zZqy{j2X;i5gn~M}rxrybE^Xxx5>#364##^Ws&9Dkz4Bz{?_v}Fy=uMSGTw+ioz3~Oa8PmfvX`HCo>!r(k-1SVmvfnK3?TD<9e%@nk{ya4^LHa;x8gUoFkIpYF zygWT^xzuDD8I%K3q(SiAw;6SW;WmvC61vVnnbl#Kv6>Z3$;vvr4uCx-KK|<5#Vi_F%RmWPK2 zPLMww6%_^4hprWFJ0{QPw-PvPJ`^{ptacfI! zYt;^?gH;KSg;8>>CkJ8J?2z>4Ogj>9e!%t*a_IB6Zl_-hXU3rCV`E)r)0d7>lK^+q zQ6-_)9BFaa(#?3j!l@Q3DGkI$xqc2*WGqG%rk}Qq>C>EPiZffsM&fJ= zh-8L4GCWF?D?jQuC{|q26qIa<%!&=+V^9Z4YK5Xoz!$w4Ul?WjkW*NuLBWETsQYp0 zIQVAl;}bwg(q_pZs?$e|jw$=$!O87ug&9@6#qTit6Ua0fNFsk-M-sb>Q48E02Aqns zEda3A2nwV+!r8@BJdqu)ja+UKNPT~g~isM=&it*(ocg%_siW& zg#(umKq(--8PvS$KBKM6(XZ@XuE!F*O-=5%8AIoqe~{t!P|X z(Xxa`PW$K2WuM>OS<=>zu}E9=tLMo*nuRMjjt4S2vhv*<+r>SkU&->?L#F=i#?gH= z&@sqa|2!CuUC~v~+J7k1^(*wBb@U(>c=qh@#P4_?1?mVr;g=RKN+zr;wZ*X-#A%EH zR_cCUXX#&(n@+S!?TmeP(ls@UVb5c?1+CBj_LT<8Y+{2aX!bFM<374583(Bja3RrS zppEf>kAtKxLwwF|)Rr3n7}wFi5*FBF!VrbYjSzt0oNY9un#t)~B&nJG!aUb3*5o4{ zUNEHAXqg+NK#2dD_}yMoXK%cah8$;hj&=O87)U&-U@ua_bh{RhrA+3vlX8*_iD`A7 zpmvQlRa_bX`b4=v$Z`W7qoVWNoDnUL;BM*@eYbqR+cMa+oifjk>8!coy3YBUN|CN^O|G?}FHGy+HOD4Y6H7@$K{LEl6 zQPIVzsR081#GY?h2rzz|ZV=!QIY%XJPY|3xJFKb}7G&3h%&gvFmn5&oV!gUdX}{i* z#pH_jd*b%cY3j>~qd^lB^vlB~@mA@J!jjmrBF{NwA1P+lu#L5;;X}1DO`2%a|50%E zfBy`J*ZSep00@Kxf)OC#^6o1l8{jA;TH;Q@6~f(JDi?Mu<_V|N|m9@?kg`0i+V5HHo% zQ?eWx<*p@B-8oTQR6`awV?QA$dXYSHPp7SpdV;jz2J{D*&VB7b8Cs-fgf*>t`*S?I zfAiR=(&Gc{Sy4nt%-jCe*4j3xslI3UJ9XM@PQ&b^(PCV1-?r+W4_cO0xIMm@3??ziHK04qwN=d zNE}+DjZmvG3sjQMMT;7gKW>8pY=j7JlJ%}xkUsl0+f@2d}Dxt(MS3N_LMdj<=zM;{dDns=0ijNt*>v zsZY|yWK5~%6q2F%^*xz9m@Ajh1PHiaS(<_a#hUuNUrZltm8m7^%%h^!wgd|9!c-|`7@*vXU& zGm6YA#Z)GT_T>(6GxjYy6OFNz+LI`m{kJX$AIKl4l8zUl7HeTW`77)q?iirizT>+R2(JUZG&)KHZ5M8vvHRIPq-90mjSxRF;P32Fuq z3I$=5e2yLHO(klbtcJT*ow}2o;@Zn!EBYh=zI!yq3V|{R`Zg_VY)|w>!!I=v6A|U) zEcOG-$v;>-pNxVHmh9gkzLn;{h4t3d%SE%$(4{w-ZUJyGtG zY?Lg3vY(xw&l=t#sz*PXtp@(q!_(eNOHH%QRH=#?K~rPn=*Y-&@Z*VGA_?^AVwlik z#(svT)RRSEYzuzfpN2U86;(Q#g!rwTf0<2`K~BCo%JScdst(WXb#l>`fI}XdQfNOW zbT@*#Fh4}F_2gi3idjzAnA`hLCDcpA%mcv%6@p?yL2tn;hw`$o7_R_Xk=|@6xs97D z%Yb~AGCO$f|x+Qxhjo?M>L)%nUT- z706^`E65@^IzE1g>%6<8P7?kP_P1P{FC_X<27xxU2LCGr41`pRwU_svf;{}b^2mma zjplt9$;ru%O&sZ}xqOa?yo7RpAks_~C1y^aYHekwH7d5#&sv_ElSFS?Zev4nS9q!1 zRWvq$&=F~|0bZ5j%GcyMPH5|cDg-EX0esO!=nWba@OsjNy&J8Rjq`= z@8+0VnOM&KDqm0HLTCX%Wp3!$=orilZ;RbPS$`@y*Cq8ppRPZGX0P?}>^B&{WD@h~ zBGm44QEeiB%6^MwH;I`|hGof6Nl6ilt3a(g9`Sl~5YF(Z&Sz^LO-Dgxr(wXw?x-<8 zKiP48V3V7Z>j5=t)Yt%ChuY;c>;-uQnPkELnwHq0+bQl)VJbZcHoQ_%*4BW2X`t zL4?`ZooAqSTk|3`DoPT)DbrRE^zOz2Jc-QiG~=YspbVnepw%{Aj}>X ze(1e?ygl(C5t^PXQ9%yF;!5pn60ulwP$9_mzvg!{{=^0=$Q{GNYMf+MQ32#nf4J0K zMoYMcGi&a?hCD5bz4VAV&;ODTs9+8#T48*-B{{B{*Q7|v=rg|};vC#{rAk7bA4`m| ziXkP<$TUar7%y^YE@f`s{Q@&$7F5uO1U>>%O7kNeQfSjeEyu^1@p{XO6MoRUPky1k z|DN6-AyYA+pl-*W>d<smS^?cy#bHQk;ypXce*#3}{}A(pGUBBMfHB@2rKVy2V=HU)!>xYoPhAK__P+0rRc;a!T73Wn9H~!7lG6rRK;OolOQSixp_+_$Q_)b^%u$fdPGJ;>ti#FN z6HX8Pe_7l>B>-e)0HN@yfG*dt$BAD+c14~E z+k%~=t*Su`w+~By2~)-(mZU2!uEmHJf9hLsBWn8l64bOx1?@|(#!3esZPtUXF&ZPD zme=Lo3mehvbLi7m4Y}{H_WF1DjV5^c37K4!#R0D1UbY-VcFCwp(7Ct0}zAuks zJAHq7!IJRkMvA69Z+H>jP4pVz_6wGCXcy3_BgckWVxmqfBD@VvY51b<4$)drODhez zn?Zjjg#qilXGaU(D5NSEuwx_YK17NNFZ?626%)cC1tfm$|SweI! zgI@nK*BI4V4(0GvIcSIY-o+pJG3!=4J2*L<9XtLdBPJnPuG{Ih2iQA}bDzBztibN~ zfU@@V)YQ~KTjm8eC*Ub!`#_R>-M7f^#jX^zK%vcwxYyReS14bf zEie<0^zmsnnN(NA)A0#+OIfOSx&LDoOwv5BO&1rtyBgCV=L*zZKUzJ#^&qyOt)!%yUWHMhK7n(*(iYkO5mIGZCS z_X4K+kFy2h&((Na1&4cRJ>%>%i%rHATD!Zu{CiNN_zy`kqxg=w^)7Ui!m|C5eN>}2 ze5(S}#f%NUT(XGZ_>hIUwg22l6+_wQ=KxW#*84;drmv&~b6cL6C8h2jv+_s1$5?>~`O`Xi+s$H!*AZ`?px4FVe_6aw7b&z# z>66l!nN!j`oz+KxRV?E#?OM%!hl}hVb2je^{Zo;tHvvpnJth67&R-vkU0zUjm^WGH%zkCW(p zrsFkX>t1f#oec!C6({4KrtkVOzhO-&R>flUq584OOQE555lUHmC{ry)OXD)=Xtw+% zP=KGrRHu(B6!33c~3iW^~qab2aa7Lk~X>9v{#^iq5ZBBYxW#zM}Ur=oP zE5J8bPEQ`Rc6M-L3^ngFDps$WK|8BFpzs(oU}OZJ!zr_;&)t&u19WEqC;D*Zv6KlB z9T?&6pP(_^^2}xIvBA}zw2iG7F9Ec=^o7e&5?OBqmA3}+x92Uf=1AM`E0@AY$<6v| zakOtPZ9Y349m~c5xiE?C0R}nl2g-_(oS%)(9qQv$o<~WD#v8HXo*dyIZqKDDs zE`ow3EJ9odJ_CY1U~T6Vg^vps-Z;2;EB85_$;ENqxoK2^2GBIQJT38&2mT8)@?aAa zszT1JG0Ig0hYjLtQ_D_xL%2~)FH}%dD7-O7EU+*wKxjd%(9x6_G^uaJv)EQLnxZ|5 z*-hF4V=4p~ZNYDsnlA4HYxR!L&M>|d!MLKM!5Rm>6;lVw;k6|M12T3R;|8g38@jw4 ztCi9*OTHM$FT+ocXLp|qNG9O>aDlt5X$X|@izfb%j=gf*F**kXLoHQ#1w^NtHaC}} zs%-v9E>%YS*$xD2-}N zay%b;Nzacs*pGXb_iQ*iME&= z0X+s>paTRB8yg!0Lf^zWAePd+FII~Q+*pvGFXDYXpUq_>D=P~amw{)?;rOMeV7_V@ zFipjd`H!8`V-7&yar*}bnA4e>k54hr>+O5uI)fK$}k!yn^?3>`|A{;YKpizMbc0Rkq-Z_E-#w4Mw=2w_v`;xxK) z-{*sK^7IEr*z`Rg+Tl(JEzd&6Lixnrj~xG=mco@=Pg5NY_ZvKN47aT*Afh1@UpFi! z-(@~szQTC5zE;V+4Y_B2{mug|z$td1`jVg3PmF7*#~|-ujBg7RN|Mnkxx7Juaz=qJ z6_}NB_EPZIa3^E<6Bdo;Mqg@ZlLsn!*FCvf6P7r9z@Pn3J?MOn6#<9ZV{U=7DD8%+rnF4ICg&*tQ+BQ0PDZs zX6=XgSU3f`K$82y&oiVr^j~kn={WQ8!o~40(LuGrOm%}SqC(*fzuOZmKrS1^)6_rd z8k*)G0v~C_Ny_E$2C5GC?4FP!S?hUGxUz`@_g>rA4Nw z*&Ss1DK9x1#6yrO^cNos#BEIsFr#jOM9;YPAbk4N(-@ra;B}9F0UHR!36Ox_MxpNk zafIZ)8JMUC)ORQ&=|sFR6@%|5z4by^w<`f|P9RPR2Fnac+mhd5BV`l__V$dSJc zP3}}lG0Ip9xLYCqIeICVXhLC&NOy9yhc$9i9Jc+c#pvBU?-L}J4AC^B*(`XeFc*rPB;$%dvTGFy zV>pJY%Z()3y6?LQ@_Q%uuzMQ&%v5U29!A1cv(1cW-gWhs#(%}U2VrH{*Z%4J=2>M} zYiZjzs!HgSzct?bA!Ez-ViIe{OmN9HKR-E?spefUU+!!k`yI`3 z#Sh02AW7xC0onEjD_3^&?;{#e+ej~JnSvB0$8`g3E zbNF~X4?Sq}=%Kjl29)pTfG`dt_}}e{7D9cygT^Jk7OGsF>o4p4=^^O1zsnj#IU-IA zzA8xGEr(tEPhdtU4N8pX?5HJS*TfIG&X*dKwd;%O*^F7q;`Oig*AEHMCU2Mk{`XU5 ziHHzWr#3rmn~9_!3W6HZAe#PfDBE}zdD-!??+FkSbN0-J{^Y!CMAS+BxhuZ!iaMEX z86=%;!ZKW`#e{={10?$yIB-S8<2Wqcbai!Ydv<`^<1QI=JM<_u?9eHA*ym`qvk|^n zB?w!Ev5MnFq!z)YdBr9*b;Iq#O=zE zCuFBNp)JFlReu$TpaZ;y{K*!rk?GMLGi46kElzWNVqtMcnMH%{pB#(T zui7jLWGAOt*br$h-+<$@c(<18Si&+axR$ylYk&KkIdqsP(WaU_1CHIZvyi!TAOhiT zwsxpu*T_K-m_{$T2@hi%0cm(K`kIF|N)DQyi3Sd^H~1OFserBuK#2th13E}Rg8{@Y z;K#{88m;szv?{@183$tqTsnIC+v5d~hpU7C$A#)#b{A=IylzFBb76U6Wp;LTeEvW0I94AX0Rq_kMFoVZI5c|$dq#Q>6wBmK=W?gE zgVvosw>7GH1*%t$>dWu{%KUTq_OGi>UT+aQo@0CDZmt9+O+%xCeOdx@*1k?h2ArCq z0#*=(-X3}girq|zT~zIyt=@-ASN`!3>9N+sp#DX`AYTIAy>9cwJiNJqnXKN1vi9C~ z272u-1$5qDHDm}-{@yN%{6R1`$Q^20E>G6}-yH)P zEh{Zv{rfd%@D*Br>sX}F6s6;I<6F*pF}r{1{DdrUtD)Pp>D%6P6R}u`z`dQ#|#A2z{xx+DCg-LpYRwHA}k2BR-t2}*Zj%!;| z54FfvXfMkB_J9LWXY!p1oehkU1v!Uq~uD&=IES$Hg* z`!(6@S*YGe#LyTR5rm>j#Wv#LS=+9ND$%5X0e70y7vx&FTI4J2XWyVFpFi~M#3SE5 zIx6gX34lg8@VpN{UE}2B1RWhcTe(!H>SVF8s-ofm@bChNudCUGaDYMxv#gwnO^K7Y zAjp|<{PbY{NvbHT90|s&pwi?T7)Syl84v(cTyl6E-XbA|Q}BBortSFwv!<6n;Yff_ z1aJZ@t*x2ZpvI~iVcc`}>lsI$p{p4;J4F*%C^Ay?^uk%2k??z8sh!@{|Cg@wbRZ&j z{d`zw#Y`Zn=Hdbd?&(Yu?-{N!=jREG&O+rfooXXP23!+=yMd9B>6U9^of?DJ?ssDa z0Vl0;a$mvIsj0vt#RACS0W9?)?4qJbZ_&&Mah%I1DI6?KayDvz>N8H& zK;SE2!-W5))n5?l3nFe!+#ErfHUr^Si0AgH;kdCe&1iTq*)23#8D4i4Sm|@3rR$gx zlyadn>(->3-TgfkVb1ti&+-U>szx(S7M^W@YmAuJ(KW|iRd%-y92eNRkG}_RJiE^< z37;tuWrna0eg@P`(o`rQVBOhyKk9ONe;@*w{PH2BKy{pHzms#_{;XuoCuy6-i0LH~ zM)#ZyJ_`ywj%@eE#e#~{It*$MZiKWj z-d~aw8EKMCDbv!Ks9?Pzj|8laaM&j)^T}*>tTeXD?8=itox~%En+@wdwcEj|K46E`r z#k`D{-102D(P>jTe|U1{pnyz&Z(t>SL9>YGT1WfMZe&)97epxn88YqWj*3RktJg3K zHh1?w$tiV*s_M&&PtM1oL7iH=oA|?X)OIuqw)cmIaMVN__XcY#ij$M8s(ts8!@`v5 zf&kQ^=*3+sgp_oY6{^LQAdB6bg@-I`&_BMc9M47z#syTjSQ5-T{$%R%(tx0&nT5G< zY=8&Fa!?Ooq5{WQo&XmQkCo9EX{zK=Yav0w*=l2;izXT180rTmoSJ2-qc9kr(vCgk zZD`Y$|L5B3j?KPhcc!LG$g!$@e> zji;5JD)qJX7<$|Jn$Nd#ciMw=^Ln-Sn(ySOK%@CZZlfT2tbn6mfJ$0Mnl4&&Wjp&{>C_FQ4Uv=-7SbRR2^kdUG=nfI zWSR^q76pbo|AwLg>ly>L&gTOUp`7`(r<#z0zd$Tga~46Yej9sbUBW*MMP|x~Y-zAY z1<(c57d{_vu9v%|Fz%v6W?4@}5FTFBvzXJ`0A=ZMQ~zz^7eKrRSvybk$+~1oM9Quq z5P?h0`Nb0U=~p)B8#iW!<@fWxp7tMdH_KN@^gSW-=kMB8Mx$)|2)?xR)mR{jBa}xL z?kaqO*TLjg-uu$(9xj$g-sV|;(r)vx#?|R5-~gW#rXJg%W8fQe%MH{tPxF-?y!K#% zJ+$mC{^EFZ4GqO1UyZ1(fRJm$#~!}V(=J~Z=SJ%IQOp9{BAqZW!zL?O8YZntmO`w} ztMi`swH=UTZO9iqTG}TNEF~$n-oeWuzD*mAfwRlM+1Cnl3V2`?ZAx9>mBQ_=lb-%g zA?f%mCm-uejweZjJ>(9Zo=mw65?jL%hohH5QH)K=bchmjuqu^d1 z`j~LN)9tCq{nN1n(oJJilLBGKzitnQ*=iw|%Qt1JWPll)880p&VU$_t=R%{$V^diH z00$=|A<4_m9(on3QTVbLa zPdMv%SugHsG)Utz`PCY|%Kh}(AN|#fk?%44@uw*IWxQ0a=Fam4~edJ#LIjn)5 zZhx&auPBzu(Xe`K3j<~g=_VgfbTSbmrVl_C+(Rbhn}%T1Xr!rRmY;=U9KS_3kV$K` zkl|$5G>YCEA*aM1mublnNYC5Z(mD7;#N*O3>zoeq zf={+$*>jGd2zR%D56e5aZ*H;dzERK*Ai`gQkxC*Drq(I?@qgs)KvY<+vNHy>`v;}Hn)`nG{WkH9 zDzl&HTOVurAtfc!-4Xv*r~D!bBpL=9T@LEdCBA4+wj5t5!k2`On=gKmS;C+Mg-36J z;-!_+xGR#F>*4YQJo>-&&K19wj`Y<11VAUsJ9{1{`FS3cEM$M$ee2;f?&gT4F&pET@v5T)$MuW)xPI4Ko(-4uJYgz7|bE^>)Q@UFyZ#9^&{Bq>*ISRKig2Y>=Tod2a!yROzeI;6-cY$F+7`}pTE2F6)og@ zCoh-}+i_RQWCQ3ELzbNVW?XMN5D*XmL}R+WisBmpGeM(yU?k^5mF#IWSI45H*NxpT zU^cR?{i6>TYcwHSMkDs8M%z>SE&J17l6)E9$>sE(^ znBcCPFZibdPxH3#acY(it+^*U79(wp9RBI|@PPrny#cHe})#JKEY7zs@WJ@y_dePJ6u z|E(Uo-4|rsDZ-9f{nvo zI$Fpsv~1xYZ&a7Q!n)Lzv+(C%lXYynJPBA%vV% zP6gDc8PeKLUI5;0kjNa3q_U<;dYfMr6R}DMBItj$4`i1*I2-|~HnVGME-AWzZxvXT zax=#HbX?|J3xDirZeecp6=bO{SO?YB1zQ72*Eb)5?GTWrw8re@L<+Qs z5_GA@C&o=SV^u=lZTl^3pzL|Rz3%aloNGcVA;qs|>NWF;CYgk|X{u$bLC=kyFV|8v zM~d)N?L`QfODpdNrb+vohc>2j)r)mB?BJaaDSyDsa4lXG?BWUBk*Rh^>iZEwH-4DK z?P5{E+b$G1w76~&Nz;h`@uf>m(@1C527kz^$^MZokui-=^o!*kXfT#Vm63L9nf*%c zcd85`os6qM%12P6<{#rhsWL3&;}QMCY#9tekNgM-s(??Yrl-=AhY(slt_V^?E4Sh+qYaf@=A+?)P~lRHeVR*O@>-O**;5nFkC~IafkL%6kM1UcLt1`~vK}Jm3-z+2piI8vtD6F&0#? zc|2sZxQB2ToUanATgWtkA?bqhO(HI!DOkJ{zXH{zW<$bw$vq9owLjdi-Kxz7T=~)^ zDIPa$e^8>*T(98CI0raQsWW?#k1MlRbQZ~d zffMh7YX0?%(5n?3PjuENQMq3q&U_SkZKNJhOXJR4r|0(UyI_|{hcNE{iTz}xn+gj&);=X7uu;QJ+l-}AZ1Td~bHatIWArNYA7kw%!w#j#6+Y0af4WS)NO@Ww+68ohdv4E&r1IN z>S;dlT=h!FJw%AlWO>DpXUyeijdvzn^~%CX=j%aVrirls@d(b*5%CCjK6mO64R>ly z$G^>${FNUAi8YYSx}cdGhAqbU$mr==LyDc&M0{k5}uZwBOntu8Fwm%Sc`JjR7=d^>$0}c@mefr?uh!3R9lAfu$o6Ex1P|=NQLF5>ZNY_{SlTQ za(ixX6wj6x4G}YGfrBR$YOcybX0a=SR+pE$? z$gyd@VCob`?Qk;oGX1djT;WsA9cS{4-+6-+0%)VkR3SgfD9hND5-dyEPji|E~4yxkl{raP9dHzxo*^Ol8D?8D&jmtrO#co&pZlGaOx= z1fEbK;RZ9l>neXs==CuM|MeagC+4{`vexl%hjc3HIg~h(NOE6QRljz(%U9rklmD4~ znKXvuZ8aQU@3fa6&l>cm&*XKkRSZZlx+5*K5j-oJ^%>r8B$a0{uM6JJ>b)EB^xm4y zpIZw&n!TBLgjI`puLZxc4<(}eiF_3~H>q3jxU&1#Mo<3j0ugPR#otCt`&Z*~yZGH_ zB%3J+WE4>HEI5LX40@0c=4yziFH&y59}af&u3TO?U0w`-47vJ2{@iEs)YoUev2Nz$ zb2|xE0uqnE=9c;qmz!FUH(neg4`#*yI7cR}gRtRQs)f@zuQ6beH_G_J1Vfn`uq8d||(ff8KCX zR#t^(+Ss@@Dg__fQ^zP^V^1N78N~@84KAW4rys> za=Sh}n$njdlhjP{TQ=62#0r9z!{W{&3$&5`0nfHSW5K8IbgMuAIA3Np*|Pih3(JKp zMP;BKeSQ25`la*OQg8fu{>=V6j0f#0qS5vJ&VfZQbK}V$xbZG@si|^&&Z}+be=yoi zdbi-#XVmiPtGCFY@9d8w;s{2I?TC0(7%P0p)ySTH@Df?Q?rsRM28n&|U! z)kl-y%Lgx>_OlUannvN7-d{2-xNm>&TYaJ&=G{Untm8QL@^Z5@opVZaM_c-0o68S1 z{t9Gg2Qa;7%c}5Zv^!;syqQ@Ug$x(=JTQ)sN328=^{(`>XEN*>ql}=AO<4DmXzsV9X=DmV>UM*X$f16hkdRB4*R^+hV;x%yHe>l>ot*jbwymo5XYv>W zGu>n$X_^rZS?!<$aAwE-{xZbujq{EU%BiVyc%QU*U4`7o!z+EHWDVua*|? zl-{q2Ilp3NKbd1@cW<6`7s$_7>-8sNOOKT)w&hRNhLk!4p6 zg)ssHJw@QbcQt>$Rnr!D-^i?Mle)WozSaBGJqKin;CKEX08~M%z7!|T@#Du%oIJI$ zzA>3hKxo_c?#|B6?ylxMTUkAI=FIsE7jNIb^~$%twYhco{DlV|eB{yNCr^18TMRMO z=E}@sHFqYZuA>fw4lNPZ)5)xA(G74dhXy6A$+^eAv3Y?Y%otciy}PsHQoCuI8#iva zvhg5eNB_?KZ*OmZ;#2>g;%sGQEyNIG6GH6wR9O`>qU2uW1UnITuKdZbZ>PNz-DZRN zY-QHW8k_p&-{>d*%zYQ$`R@0;^3pfw^S#y8m5ufFn>TOGR#qQ*^zqf%3ILZ0)gI&V zqrPho*SMO3Z}&ygwu=Gv`)U*QT>sY@$e52F<9Tlb0v3cmSM6kx+~i|0@XIcf$8abSa;j zvh%yHOI?i3%F4>+S6=)2ul(b8Jp1f>-t(Sc{Chut^VZF8eB;FzFTe7}^&7Xgc6YaP zRwJMZyf$l2om+eG;=@lo{_w*OJuHF$^o6hg+u!;(-+JZS7cSoa&?9GypDQ-L??VVY zGTvN+!An&8M$evAkUi-pJd<4?Zp^pC&e80D?s|HL2u-XDBwzL;OQcyThF z7TPJsVoizkMOMtfIps{*5JGtK#`eGagFg%sa!vroL_Qrx(;WKb;qcDN>w#rPd@$(= zK#Z|%yJN>sPG?ik2V)l1%tZ4Yef8V3)Bo)sfAT&5`lVa1(=jSVw(S7T-#_+W9rq_7 z0%^$r@Hd*3R?BQeK$_X7&R+jix_FH!jOb`SF zH{`r@cj!V4UDt(>=X)>w=5OD*_WeKj{%6ilSaqJMw5S^2HFJo~+Uk+mOlB+V_nq1U zha*Sgv_Y=Z<^JlxHi@{?@@JmEa_xmbm~DSkCb3v@O>J3ItA5tWJQ%`LezM5Q%F6EU z?uip8c6QX3UPSe4M+=53d1~mkb63plgxbM{cCXGWue@^M!iA52{NoQl{O|)0JaGK@ zaR6t}p7k`3w}~J!GiLXp2IugFy(0X0NIm`;F}1KNtcF7uWAx@BF~J$K~Dk?C~0@1}Yo>{C8WN}X!8CGUxvv8owV zl_TXW!Xf9b>(Yr6$G`fuuUxwL&_fSBOhm80{v9*T`q;U1$BFE*#~wd^{Jw8~^YRzJ z_~p&bO=d}5{^TeB_|&P>k3asz?c3WMM8Td8yjEv{ByHubK=Aa_sqKIhnYo+ zU+&<#xG?oRmg4>befV#F=%4(r|MYvLZe12j69LLzKxH&Vhri|2yFX}kQ(3LX=x_3; zh{;j6Ym#BB4LQH=^C77fZC6%i=T4qDb8PJc?|(OylK;Ju7=S7D!6%i0Bfy3dHHK11 z6k7S}xGRWb&oQpeL*&jo5c$0%~9 z#5`4q@1<|Lu2aoj*XEqHR!GQRC!xL(mDKNQ8Ou9+^KWnNzSioE4yvOSvgPAL1T~p9 z?>jMj=Y1!oX*Sl@o5|D>Th{FItzEQRTbp~kJ5Hne2oG2w%fjSv8MN>9H2!lCnaw79 z^ET(KrrvswYLfKY`o{Xkh9fqwVd22ZwW-$$N)Bqfu3aoVW+|t1b zcD6SOcH-EvBS(%%j7A_1K{z(e%F4=QHk~w+$#fDW?#<`ldF|WZdF?xQcXn6TH!j`( z;MsHMH#UyCjnqth*C@zvE;FEVrC!Ubk-+qN(J1K`Gu8-M)S=XN_=-#8LtY+{MTW06{C)1{MT)$Dts zz_Qlk9NcCcbWZ8FW&r%O?(A$|yYj}jUwQe~&9|<;@%lrLKK_xP`MJq-#>}1s3BM^K zstq)HH`jqfbkED`RcyJoO{v@4-PzvUyz%DsrkPBq)4iSR|J^UFK5*$MF=#!0o2_UwXc1BZ*Otp^r>UVj*GVq zk0n6K`5hkl91boaDpYb4wc6wF68R% zboEwy^KRN(fZ5ikeCkMZ?nF3yBt+Z$_P1aA)aSnPxi5UBZ5Ln`afnTj*fgGCA|wCc@;WT52cQRki}~XA z?OPWwUNl3>>5z5l0sqn`Eq&nn+Un2#%-{Z=mifS zKlH(k^^M)V-KJ@XsO{Pq;$pFgF`C)>`g#bVZQHfAHTOW~luw*I;q3)apFZs+HOFUG zRYM4$`t-m5(wDz7X&Uspn}~WaIU1+c0ybI{R5rDga@%%h+P3ZG%inC`^qHsM@%R%@ z-hco74?OU|#zrvHot^ZpZ@u>FtFK(Ye(lz++k1O^L|9u}zj*P&6Hh$()KiazfUT|e zU;WEp`_!jDv2kSO(xpoPnkIOFlB-M!!3VOeR))()wOjnbzxuV0e(v*M6p`8o;O(yx z*5-Pg4!8CT)=)J%`R)&*FUe!b=5hALuC$8St7CScmOVhC4^wk~&RIbw+E|Id@N+-C zGM&`B>er>HsG&bq$txXP0tZjVa2|D#ZCIu%2D`XuJXOtI>NMrWVqq{>0Imq!BR3Xs z-+f}2FRQUg#o176Y1hetXD9n$h)VUeu5A~K`Oe*)wq2yI+nw)j?d)b%SoOowFdiae zk`Uwi>dJ|uM`tT5tE+31>9mPWh@l2UnQJD^1rd!iCH@>QR{%{2ZF0p^nphS6AA&c% za+haCYjt|-jA=E_QtDEd7K=qK>eJrc-oE+Pjczd)!la3_*>pOcI!Bmu+MCb2E~k__ zMKhhAJauMuZQUs|k>GI%B7NNRA*F=v;DCf*3fC>u3Ib?i}}1u9W%jF*QQISUjMIt{_OQvZvE!(Zhrpx$*D6J*EZIe zV+@T)3z%7Cx_O&;j|Jb|*?Il7S1*6#h53AL zK+{Y%jvQTETbr${&Sta8bmp^BEO`+(_-Y*g2Aha=zrx01zrsqW-@! z+O}<-#ckWgo!hr=zV+7L-p=aE>ZwyFPn;`=n>TOY%2|&bJ$B;M zsrB`Z5ZuS+OT0w+4=agYNC8XFjwaAcRAsM7*SVXbZQGr@+c$5%wKs1s+;{rn2QOW^ zaQ4`dBkSv{o?o<+Up>f9tiat?lE-j~zdLVmh0~*n|+8CVK5AuB65O zNvYn5l(P$Y&4ty5_b}=e=@2_`=uPaK>O;lY;aATNcwnpu$JhuOZ$3DbYTo#GjAIaOC^V6XE}gu1`o6R7!0*S5x_r5;U25C5 z?b3Ri`!ro=F+{rIf5NV34*i3mo#-Qx0*)}9IATb#`U8|j~+jMe0O*6ke3!PHaxS$?HSy8^~^I* zrIhw_i{o#sEV*-=s9_ux#Vn1gztr^epa0^~qsK;{RW;WL<1K$rx%T#U|JFx7eDcJ} z-M!t}Y-X@_-ZqnFKA*=J7mLOE`ug`YV+XF!eD1Sf`qH0x==lEKeef#tY!2Q$zci)X zb=kw&-n?<+jVo7HW*bjG_0$i)_q~rk_Q1yaT8#1f_3f7~Uw-3_H*Vj#-F02gI+;w) zpFel;;-%Q&g|B_>v!8wL*6p_*dg#HGl~rP!P1jb}R(I~+H5ju{ErhynEL?dIRH)q7 z;y2jN&dx9Yga2v1x2XMg-~Jj-*+J-EpBc0To&13h!V~ofP$WxU55E5p05GM28ZUN; z$5((H+L-%*eC=|(_|Q-M=nuW?`$k`koWc9`Mwa)nF zQmVDeixEFm9v|4Fqxp+}>N?V(Ec6WAi>a<3+N{DOg>#M74v1vqt z|77MEV+nrPAIN!+t2l7HtF#Y5)x6@+Fhga~2N3;#kO45NWmE}s0A535eRY+HZf|V^ z2L@2yUG%G4P3@vxwDaA&cQ-fh+_`;g^Um$VRjUz{n96fgQ z*omXZjvYCA%pFHkGo-1;p8Bj*|MDR-HoD;hf46)5p~eyc=ANT(P3SD#Dg^uS8=Dr>*_XXqiN@3rjB^Gw|> z8Ee$FGr|TmA+D{i#74HZcJ}6rDzR)di2diMUv@gcyN=8`Rm-N?EI~Wdbo=JDM=xG| z;j+xuFRrX@gfL|Z%ngi5cw~Lq?b|n3R@bJJY001^sv!&ZT{h)t5^~PYWH09Pot?Y$ zyv8+Os3P>jF_7ky$wjM^K*b2xTh|?=O45Xdos4I8!v z;92G<;=>M|*@GW7^4>24$a`CBy{JbO`2Kljrp{An?sC_qF6Z27r% z-rC%37mG{x-@msvU*_@-JKOs%LpI&r+1=W_Gju*hZ4uC~|9DXK+qsY19K`9q^NZEh z)s2mf>2y}VZ73&bLr{4C*;H6)0Knb5cVm#QYgH9iLyNi8hxKw>s5o_GPAom%f4gQ9 zL>0|s(lpJ|Odi}hOLInU8-h^eOiC#^U?U>CyS;t=`n9_|yGJ&TojiGbI-LQMQn$0S zx4pf+ySt+~HtwCv9Y;zCnq(O8kf49v{UD~ z1lncQahQx>pZfG4|N2M&p9*s5z?nJxy0-x^Tq6$vpvZZ-mAQ|V%iw6AWG6CrR*sBq zvYgKxU;lf5=Oa|_ww!h|_GqFC{rfw&kzcsKwP6bO>9&#JI&hu_vbho`22Z(w7YOf| zS%UYz2_8vl2Ncx3uH~5;U>s5hG`QT*B(^Kzp4*(6!$-3T=oro={W5>RLe?y=~TbXeE6C=<2Swz&f7jr z?7wnJB4ckCPzy>7yyg;i8oJQ;+p~(cdwlOfeCoxfN@{^w*6dt0;3W6G6=1Ij2q@&i z+fL20-1Nbf0Bsop%tMGw+@*Hk+k3F7TD(~VXx2r$Sj^{UIeQVjl(J?IP0*aYlv0iC z4;9sgV)qhDjWLiKBGt>tF$~y;`l}OgX(kW;;;$V;J-XnYUo+afb;~b_iE*H<@1PP>m2yec$?E9VQ}jw4 z6bOVS#`U$;NfS4>cXnzURN$})IOP8KzowEYE-FD}!YZ6Yw44_6`FuKUBt(EyB2`x9 ztZc^q@0ZeI001BWNklMM1@si)GRf(?Q7GR z469M{!(i^sbS&?;kvi$$jO-%~>_W zh+x@~!N}|xb7T%-ec>EQ6Ome}TTC^KwpVRK&r~ME2DGM9F+F{4o!1fVR&0 zf5?;Gyu3sK9C~NNTkH9<{+40PRJH{bl{iVd&?zf{$7_u-%V@erbaGB2&D5f_&e zRe@`D(+t3VI5RkKat<6MS~>zlHiIfunJKUY;YHhOF$m1V7GcPm`@rkNS?~kbvN1A% zZD(SvPFmY7+HS~I@NZS4M4U)$I#sYSu|$?2VDqf6-T+V~HP##nQFF*0X;!EajL674 zG?dBD-gnH*Offq4qcgTVX$K5U8~w?w-=q6pLP0{)gn8SkmR`pMlx{a5BB>(5ScC_5 zdAL+nYLy8p=e7|h)$IA6ZUGQb(=1eTawknHHGBTcF9%|^Buq_nD##$E zu5DXXr(TPzj_c-Cr{Qp!WME3Vl;EUtZm1hLNgSY*Sn2??Z5 z7@HP`Ev2lQQzybO_*Y2qN)u7o8_sxCn`M7<&N*jISuN+R0913y<(JYTS&ckW`%tW1 z%J=}e@4rE1fClp-ndOBC(7KZbp7_L95)j0glW_uCqfC?(8HHjIATR{hhy^o&2GyWhQ?i^i zCo>gy|JHgGs0L3z95n?HAU*pbSX%n3nh&h~ch_jcdk!@omY=W1vi!&~t7Uk0AA{LQ z0!Ky28j)E$>Xfrjh7GuW|Bf}3QY~h+Wv2hJ_siC2$RGwWG8Hyyl1wyA5Y{JC>qryS zs3gFUw_Pt1N(~%JnO82gIVDO6qiRdXh|U%BW*gY9i(U;D2CWcQ%B-5rP<2Z2W9QtZ zlq5LGV58{YdXvj*VFe(UCNO<%bOM0sbUK?&cXoEx*Vnp~Y3Xes;*r7KgQ)NMx`zos zOta}UtEMicoDX}6RrXNEPab$7_1n_3g;IrlF-8Jx?R$x3|NnIrE`yO$Uc!e~V7RNF zvRSG(RYa!K*`%3txnts5eU7L`NvT0KJ4u+@Wju_UOO=z~Qg3K*XuA5k}R=VD-q=HjR}C z0{SeOePO|cfzI8#-|>A9UOYGYN-%R1qpG%D*Bgjj^z(3&v~*V^>AZ|i4um6@aH*a` zvXX#H08-LU6A`@9OHIY+z7{n`gx1@@M=Vdb^9pM2tZ`!z2GguyST3m4MTiH?8&NhS zlEk4SX+y|ynq$mSEU*P2jA$w&OA1BKL@A+LP+mw%nJkoqRIPqS3nCuSg1Qm=+?RXZ z%i#+6up8~4AwmpceQovb&aQ(|pS8k;Q-NZ>uTk`|Q$%p3OCVQaeJHUD;KoaahtR|r zLLkGUTe#lNs+z?kn7Nc?G%s4^jRJDYZmD{>1dqB+jTQ@zi7n^ysO0^4N?+$wrI!2f zwEoU{^caQ?a7>?a9`DKVba@uO2-`w)mr!=Q5jupa z@=5g)LSD0{1b@^Pz(igEC7EUKevw1gWNl|DQ_4_0X5oFgXH-Cgp)O=`6{@PUq+rsr_(ViTH6e0j2lhj%_K6*kHuYB<{pOf^ zP0VFVF|k!5epMxGd_CKc`vT|4%R z!#+f&PD>GN%*2pLEC`}3Ex03|8(PppsWT){a>%M60x5z)U=3jcYse;S4O@^bmL+G) z$ut!^-wdN}si?|MAcSHQ)P2h%Q?OTI@Zg&@f5e1QrSeZbb!OX4PYTxafXkWXs+r~I~WDkfUC2#?qh1AaNPCRJ=-Fsi=Qf zF#xI^+tZkZMkbh9)@0Q^m(md7;>w4Zn%Fc=?7A+c)=E%uB^av>U{K#mBCG~xz_Bz zs;Vh>E~8cJxfnO&07{Ar#wG#Gb6YdaxDQob+9PGg7mKdzQdTW>qzVmOG>w`eXWvCi zS*#RmFM8hte~Qbrq?Z#Ji+J8{Gnq7#W;&ftC({tT6bnt7hM9Nv_KLk6WIAaKNLd&2 zy~Sd&m@gJ>+jX78ZjXa^KZKbwi{l%=4UgXqK|DHwBR$t(--oM>S*H|l_vkQqv)BS#jaogJ zDyx#Q!B!@#(!{RImZi}2Q9zQZO^}Q(5fSBbI`(bQ(S8_N2m~@Qh(eSb&`L@Z41g^a8-)`&6gHcx2>{q44-?o#1y%tZmzrI${!*lt84lX9@Lg!g=0XEcW~WScy#55@1s5 zi>j-(9$J=D$=;@d%6t;~Xqxe8dSjqIuo#=wm9?MwTOaw#^UvSi*`*<1@bGCF-{$yc z`>k%OO`IG*aVGdZae-it<(*3|MRID%QkTwU%lKrEw}AmUE&k&V{FU{!)sd-T1j;$@ z?Cm)oCMPps?)mCn0b}=Z(RC#Sr*ORebogQlEdAc7lCg_Reea`3%Lt<@lQ%m*Tv#TZssW|JoF?#;dRl*7b+YpTZ}xO2nA z+iMZA>v~@fk%-!m%K)#8k#ecR+TYhW`2+w5N|{nhOvECb6;irDn|Yr=t9l6Px!h6l`GF=7M;F_^ufTS4|}nxF~9jmng`$=x1t&`wM{QzaNl0Er_shLlg^ zDvgL>ZpD=tVif<|(mC4Cak~_$TZfq|_s^xV_+YH}AK{G2q-?HWu}R*Hq3g_J3OwTj z)zRz|!YtLZj1slKUpE^-re*>&C3shABBo-H2=}m~SIng)U+vCj^(@yE8tt(~HTX+| z$RJUe+paYw7KRdtt;SY)qlto6>tvyZn~EEW1)};JNx@0c8FxypB7+l1iM7N6QQgO7 zD1fOcLp!sOwPA~9ff0q-jG)YBmfc5cg=!x!#PY=Xd4LfIk8x4r%+eAsi1$D(@xrWQ zN`Nwq0qWsL{S<{NOks+wlFbs(87H=>aTE@kxe?L~%dELfNl%?Hxj|K{t_}&=b%zc# zvxAiWc6u1$l55=XWLd zu|Ppg24Lvl$IR^QxQ2GhT*IiAoDM`bG$-gQky1tf0GiPWE0D>IjES@?QA$a~b6az+ zd~4EODO&Y`HA?XHmv3osT^Hu^a{w33ZB zdKFa|YC2T2KuO-)*Ao^*G@VY@*VgZB-tOA2_oLPO_qbMLW^Y+jNsAKAS=VCZKv;r! zqbEn3NK2dfQv337;>HhxGnD{&CT=xusmgS6XsjeuT9g2kGchkYIeQz~hO=el&2Y(5 z>Q9grSXt0=vE%`iY_5?LZw&hP1u{6?*PPck*8k>*{>DH5)qg>BPxP_x-j^`m_=||M z+1=gkP`#Z4njaJN<8ChpG#-tgv79g7clv$rewSzZj>10(u$FzC4VIh#2dM}J95<`d~P9_G7{B(7n^kX1L$No;${uogyfg()g75Y^qxK)&M z=gEJnS(SS?5WqyaoJ9s3vXZdd6t&k{nV=Hy=6eN9!VG4HD%|!fwM_eu9;SADG=SQO zkVXaokqm{=Yh@Kji8SyN)rkdjPZ|@&CWhEdrjuqeZDJIcAZ~R7N@!Jy(G^GlseBax z24WCiTjdZ#Y!-WaUIjI&XRWA%AyO!*ccaV5%ASFcGdkr?DYGa9AVV=X9cqgg0x&R? zP?A&x#(;ngF#secz>w-Nj_V)GK2*ePW@2JeLJowbfKC{ZP;sk}5raqqD=~yU$F`~^ zVucAbnsmtC85hWA0CGn}*-^7m$P|etMnNqwixRQ>?fg?)&UQvr@y6s zSNDxbsXV07)xW&oJVd<#69qBIbTV;LDs^4Q)Oph@r{jw@g72Zei50M<0*Q#!a@}bq z@F_MiNNAc^43ZJep|vkt$sws`KyMZFKZgeeVW4DqsTW$wZs0lY7jGBEZtCp^)Qd9da z#B^X+)res=(rlVDD+eM|F+{Uq2xb9MAYxzzgOV+=>dO;epg%=$RBDn;vP_e#B$?$f z%VCnG(Ll;DQDZYPVgp%?0Q4$^6pAHW>`o!?j9vO<*CGrUG!OT5h!JH%HYA`kWJRvk zxrLzt(Lk^;pr5*(Jzmf4ujb9Nl^` zB6fGV1SzZR@MGBk=;wWaIR`$OXnfLBPTr-{cS%(^)pQ$U#X-WM_kuD@2ogtKIO_M@ zXBCmZlj(N9{-h?#YzfL);s|2ddq%5+|jx`q+%*p zd{qr*B0zgz0S56ticKIg@2g#Z-Dd#MavuQK8Qv@X$N%#C|LBvS`u1zD?;Fp5z@@6~ zVjilMGVG0-&(-THCL*)kFOnTdmsK_7^k01F13|>hSjsFjqjx!)L4@6obo#&M-}WDC zW~}Ne&KnX1V@nwZQU;8QAD~(c1s5`VCmB@Jj6C0w)P3H<@GyZW!I{WftxJnNK}A^h z17Vj&eW1S@5vyeb6l`qCED3cSQizFTj*U$wh%vJ%3<6JdwQAa02?;~N25c^+%sHC^ z6&Pzn42lQ5BIUzu5#I`PPr!R)W`=KToWlxJcC zfvtd=f}vQr0BtiSm@tb3Rbg?=LT&@8szJSkvl24`Q?gPbB4;om)361pMpl$UIb5pB zH8FN=*LGdsQOW(_HgZ;#gMb4m`t`JwQ^piXxK`p}CJrprCv*U0R@i|;6~rdSrislo z#3n=rkcqn5F|!3_Ae&jr{v8YwK!9KffWV`hEX<~mK*F*JUE8LV{2r(m;ba74lLmq`dBw~S1aRTqlYr(`v-&s<$N!~KMh&wRkqjjxF?ax%ka_GY zl0pP+AQRR=O{69$wHa+mlvrxq*+XdyBa*)fZSKmv16)tRar7mr5ZK_&dkjLedl=2sTNzs4U??d=5 zqobu;H!}%ps??=SIcLNWa)s|pZ*~4gwFC}$5O2+6Y?>ykrmpK@Hx+%1-bxwSHm=eO z)U-l(C=vSvIY#n7xj-I$0z331SN7FrvJva=kx1f=x~%+aW+`9XI3^FD!h@tH2m`c7h6B&iTE331d^4?-$P{5Ob6{a8~ zGA0W|!W5ez%z;P(11!!zRoGn!4|T_bqjk?0D9uMzEgKY+VI5H-={R%}k~DcT(GYVa zH0BNNsb@NSUY9}StOMnohq#YoJ{Ci%ETpfAPZWqr%O9!4x0#Wt&b!2>q%c#>S#xf^ zF>1T;7tbwD&P_8hLyXY?Vd<5ep_FPi zn1~zwBqGH|sDU=DMjF-ZWU7KihOH$g-iiA{_hcQJGo2<7a47PC~dLHapel2Hbung~!# zKUYg|7eJ|8LNHY-=_rUX#t;_m0w4(zn~B7UOd>I5B7?QnL)}(0z)41*K?c~%j{p$} zpy0{=1epZzDtYbfcHzlt)*GKh?hWz0Qy;R2^i##eN}i2j@ipk z2Q^KAc|0h(Uy>EwhB+y`WsU5|#8A169@*MGV$~JHcKuZ5$tj5G3?g$Vjy< zU6r>BE+2bDI9#h(rMcW#eQ>#;s9joD9kg zc9h5^C3x)Ns$km8z=Z7aHez5Hd5W949j69jV~gxITW!Hmbq@13($Xnw6(L?xu&{nAT0_6cBcuBTCO|vH;d!_Fp0pOGpvkFUZsZvq! z5EBI%nSlcVCYDW=)mT(u0VX*zf?MQdMQAyzMFI-o2$`5nJDw$(bv#QvO|p`BtyvX` zdtKKh1&E9@2&G^=nTvu#>9K1{H7mYbDgk(4J-Y0b@d(N1U&Sz}jY!~Q63^FEJSy}D6&U;bxU)Njs!!3>gegEX6k391J_x;GnoCq7^000xg zNkli;RVx)GfT4y7)uM04MHG{QnX`JjG53(Bs0kvYs`-hGkr6r&gbJT486_e? ziA>DMR0166J=t4hoj+l$$ z9gu3xavWlrdb~gS)oOXEeH6uL8!12_VS_-qB7mGt#6QG0xf3KzM8qTnvoK;_Jm$D& z`_*zBB{jD67;}oNnj|QNR6s0+EU$H+%I+5uRi!>$&4A(uWcUwt9l_$zp9sP*%FJZy z@Q4{C6iTm4Zw=)PN+8g5DifQSda^-IIdwU!=B~mb&o7sr_AKYD%x*1)TZ(Toi@T>w zIxMlzk3&k4lEMoji9zB-LL{L~9`M5n=<0u|I@C+-hna$y&>O);!x04|CPN^xnH6Qx zX-=i@sufKNGJ+Zz7dn%T6hd`Wsazj-mKs4NP1r>=-dI8xxpIC1Z!sLS|&8kP{h!cRd&nyL7O2k)xEM;*`RcPq!uTnv4MTd!SkPy5b1l4%7vLg3X7ywW!qE^zYG-}Y&PeTWp z*>}jwxn{9fvW?ieit2T!Rxy*Qc%dT&vQYvja4rkYgqk}pTs&O9tb$5hnH3)qnZU9k z!iYHY*`&XAV4BknR~x5LW;U}085C~n*( zWLD^Z_(%JmX7?=-DU7orgE~_oV-PF{0)x!5+a_LlBd`#VH4(_5sEC2!c`MwPEG#X? z05eUg^gsZ5_x6|w9M+mEWb{p8W--QEi#K~e%_@eu-`j2=m}MeT1;ao>B~_0AA`{k_ za!lx8!X|>CAsTlcy2vmA1yF!A;EBpK%QW*W@ifycQ_PG^nxYw-vezImcxIjZ_`JMA z^}IMPU=Bp^2pIOX8zyEB0Za)@gv{J2)Hr7-c||EQK>ctVsSRZw$Wcg1ak9$ZnNcY{ zKxUxQxudt*nJB=RC}n6a`3S0|%D`Cm)h*M*Dc?3)vo3*NU5`-c_&x5&DjxD$d7#>a z{Q9$e5yRmWNdby^_u>q>p!69rgz%g{G&;ZXv8$Pwi(^Ldb0m20xltB+f$J<^b zV`YGs<{zXz*D}DEk#*%jmc|gMXdE-s`C^XYTnzS7KX2t@2&3tNs}L%rc2!bx*Oi%yLhk;#HVVS%9LnTbR!U?CDNq97PE1X@dgdfjV- zAt8H&Y|5FKNKG?YcH&N9+?gRl6-L1%k_5ykG3Ev$j!H>D=8*?p{K-m{}3oLNYjL0KI?vn4XmvCAxi3={JN!M@lEmvZ~X&L~kpRaMrV zvGglO{>g)8K-JZtl3!dHDU6N^RmH#(P<2f3zQs(n985V?J#awHu0TZ+{@A~WnMnbt z2kz7pJ{$~l-i27zlLk4&@|TnVfE32w=7QX!6tEY@5GWzAummQiAk0D{5+wxo@Sh+g z?0q4gHqHgA>^B~o61znXUo3j{AnNQrET z8^8h+W25LI#q92!(^bVoRrl%hF>`14UMH>9YUWIz?&`1luIfojn#M_lJhyxiWvz;U zc5Ga2><}`6!$-5KXIh#>3Wp{mzr05ETngvRDW@TGK2jznQ=kwPV6?CRNHwJg&8?U? zj#;G9Bj0<#Gt7L$Oiwr#$oOPZyM=YY9%s2O+E9FnM)_k0wQ8CSxsYL#GI%q z-D)DBSz|FU?^@GS5v17`LaW)YexqLE-K6wTz02foid7QfwCN{kv%>PC}I z2IR^P%-X@A*v8C>=VV;g)Px}vGz)>>y%4qlpF*ydE}wfC5!e%qDxe@ySk;6&znPib z4-G`_-xl7W3=74x&jT656k5I6fCTb@dQp&sURKrAaaE+O;+b2gAPf#bGD`{?jRjnk zT}lqYkpQ4f6EVORTEgX$8o}@Cb@9NJT%>SqMs_8hOht`J35kh}1Vm!k`#bM+}8hVX_3vpaFb9Iw&6{oeG}>N2Qa1 z1)kWyoW-;lTw@pElV~y|xg!uUk#a;?fs^~YrIe6T$_d0mOw39oMw5^z7e!H0Bc*VB zRMdEY8eH$!^LVy1xKkih03jnYmw4O>nN-<@0Z?e802Ubtmberlsuz4~WKjs57qlX> z*}}T#{=Ex}-Ky%nmR0=FLX8@+y<`FC-d=p@yWr+MuUh#EXSx{)vthRFqD>nuq^?N< zZR0f=;Iai^J6sDbusa7Zkr6YqvXU}06gA2JVnkIv8a$`V#%8L<@CfOdD{h1QaT(4xWZ$;33}Oxtx!v0{p?MxmV&sI(loD}GaVIU7$xIClB&I}`7@3t} z*;R**nTgExCED~h&CG48T42R6s+|QRrw7gx=fYGnNg_@}im*}}GI3HcXkZM?i6{{z z5P$;nyx)`Rs1Pz?Dy9?6y;hWV<8c|4z| zaXO#Rp5eYX0yM$2-vq0#{oYu~06h!hetg5>a6BGQ$D=0(9fm{7X&46AI{>KRh2gs6 zB{CFL6SlOpgUjH;)@k_5H-FXbRe!arV^s^+2NWr;hE#);qH3b0lp;l@GO4ILN5MTU z5~Un^upB-TL*kq{GiCOaT{XP8NGWBSipSEHQi|L1dWjkJMAa>Bo|(c=`!M+Rbgs7> zBGf+l<@P>*$q6!2R)!@KpwvHS{R^ye|E_hbiTf$|clQp7c!2ibkPKnxE|ZlJDk8-I zkXeGk#EQoOPjisH#O0A;SkH$u=8b56(!Lq;BGAh? z1gYPUR*{k!JXk?goi!9QWpc}g!ZosHL}uXRQZ|Sp^@=LAK>Tl186TvabsFm(+(c>M zbl~B{!!ZrS8EypNz=ePlLJePTHmChySOOU431B!_V-4G8Xx0Od?CRPv4-jDh$7Iv5{>jzWh*$6|+KR20SB5vN`u5PE2E0+FR;%*cbK zWH}ipq=*T2%N8O(7J*0%N~9h-r8p~!(4-^+1(|Z_k%@>XC(6u;DJ4p-M~M6}CZ?ic z#Bq&s7?cQ1mdFy*K$J{tRETHDvji&Y_fdc@oc4W=?+;|w5m(A z?soL_1-id~`1I3HKl$X7)g(A)6}Y-%pL*8cW}l_GyWGA|BUVwUA^)ldijN$Z^jRYl z1eEddm%sSMFFWstXYA*;of=H>EetIl+>!%6t? z!w2hv*lI_M0PGF{#F`Uxjl#Xxn@hwfqAOG_Y-v7=&ZAf;z_=jHn z_y2tO>))_E>iI#=f0Og0l}AW{iU%LMeLSv}v=Dl8&ZU$krnLr^e_e{%owBy8v^eIg zCo3ZIqED&F#V9&e*@7w}FM0``D$ z2IgrqcROdiI&W9r3WZLue&`te?T3bQa%V4a9!9DyxK%~umRR=RoAHVG<^SG);~!24 zM6O%A&YJn74ZGj(Ba0e7FpC7>3MHaqg-8WZHHC`g(^~`Hd3)GSOZ)WJ06G87${EuG z%9+lOTE@6B$dg!C1Dq?8u{@m%$!Z0ax;?&FA&zKNlTsuf-+mQTZx1MsUqMEcM~n|N zKI$|94XW_H=@%^4t5eyxHg|P({WGm>Zm*wBG%NXwj@k zdKSsUZH8k9!rgN9cZ1!~#9l+&cV@&vAZ8WWspX>ZT1JaawN;aZ))s&+`THen{tCc* zNyOn>vo&-}V^?a-$CDZce=ft}BG1zhr8N?1)*_pLmU+Ww2(~-_0&p7={JgSeUN1jh zQM#v43){AN;mP)4v!m>{Tn*MY^uPHwkH}V&@2j;w2b=fy(G;2&+v4R7GHGm14If&H zK9h<1UyN%5wW(dwHXCSyZLvid+K5og3>DyFN7z7_GbN zIt`!fS8z7#P12TUIr1Qft5H;B5r}Ih*}h@Kv=EUMP9iW7Q&rJC-NHze$FF#Lu;~Hg z1DzjrI@gFGzroESL3Qwp2C^d4-Y(sHw4SX6IlBcLTs``?;*%$q3;WOs{?2x}47G&? z`*1v!Ql@FNW%ae`^fLL%Ip5vg-JR~f{L7bqvF3(8cAq^DfHjqQ&z z$^TXVs0*XLtyDrVAHtU}|4Lo%<{q}5#c#IXh2nAP+eo`vv$@7FnwBSDw86~OVa*1qfEcOJm??n0Q7 z+lI~X;VZd~_Cu^V?BbH_LIon` z;sGTY3cUF2OEselP?^ea|MV9vXO&T>Gp5O;s7#KqcA)CIo~NnX%`^jEH$hi#e;{qI zZv}ugeV$)h9G&iT2g_DriR15ew4wsFRm=W?h-xr$eW&)j1%AolugFJvMH=DXf&7FuPrMpqUJuqvU=a}G)?Zph8iec zyVE)l_%5qyShj=Q-Lrmv_D_O)L^IR<{k?AL^bR@AMQ6pA*J)YQQPcZ}$3Ohxj~{;Y pv(+YRjQ-kBp?4dpoP94X{2lBZlTcsn8A$*D002ovPDHLkV1gY{g1P_z literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_macosx_small.png b/doc/qxentityeditor/resource/qxee_macosx_small.png new file mode 100644 index 0000000000000000000000000000000000000000..afacb2c360ad82cb8d8261974f37f46d886bc136 GIT binary patch literal 55887 zcmZs?WmsEX6E2JsG{L1v0u;C6Ew6>@iw{ojoKElE!F-(DoWdV@1Jsa^<~!cXO6z!=9z5#1&<{PWV�!i0~gD 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

    ; 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 literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_namespace_colors.png b/doc/qxentityeditor/resource/qxee_namespace_colors.png new file mode 100644 index 0000000000000000000000000000000000000000..e93d71bd45348c971deaf1bf2aaa35844a9571a5 GIT binary patch literal 20213 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 literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_namespace_rename.png b/doc/qxentityeditor/resource/qxee_namespace_rename.png new file mode 100644 index 0000000000000000000000000000000000000000..e8227df560973badfab02143e41fd4214b3a08e2 GIT binary patch literal 4861 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 T*lD9*Sph&mBkkhb_apuRbDeq? literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_naming_convention.png b/doc/qxentityeditor/resource/qxee_naming_convention.png new file mode 100644 index 0000000000000000000000000000000000000000..2a884f547f7148fa1873b25bc521c2b3b978c885 GIT binary patch literal 7736 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 literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_organize_diagram_layout.png b/doc/qxentityeditor/resource/qxee_organize_diagram_layout.png new file mode 100644 index 0000000000000000000000000000000000000000..96cb22ddaff986e352e2ae9abe1c133c5c0d3f53 GIT binary patch literal 36547 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 h13+pr@dqV7#oRe2s#Fs+xj= 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

    RX3X7n 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

    ipD2aH0s?Omsl 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 UJc~E*X`4a!wxL$htw)jn0RWvxu>b%7 literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_project_list.png b/doc/qxentityeditor/resource/qxee_project_list.png new file mode 100644 index 0000000000000000000000000000000000000000..ba4a11e5c8f4ced4ed343b9d02ce0ee208014bd1 GIT binary patch literal 12092 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 OqbSIzN`oZLLjD(!w@nuS literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_project_new.png b/doc/qxentityeditor/resource/qxee_project_new.png new file mode 100644 index 0000000000000000000000000000000000000000..4ffb1682fdcdb1b2d198c17931bbafe311ca6c92 GIT binary patch literal 5628 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 literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_project_settings_01.png b/doc/qxentityeditor/resource/qxee_project_settings_01.png new file mode 100644 index 0000000000000000000000000000000000000000..f8e1a97e4e1dadeca54f6917a5d31d28d404aead GIT binary patch literal 11156 zcmeHtXIPWnnr@^>iGYd(1Su8}A%LQQiV!S_bdWAhsY(k)Y6u7lNLT6bQKd=^olqi3 z@4bbp^cJKONHQ-l&g?z2_dYX!&UJpAAHbEc%DbL*KllBV*Iz|RhK`n%76by($v%7X z90VdO1c6S)QjWKzGv|fq9^Rc0y7Iu;4e%eeERS z+9h^){v^WnVc)*_V7yMh{VN9*`R0n)eJV4~nl)zY zA<1$z0zTX4I;%L|iDS(&o~W=~N0;k7QCy1>`dL)01G)me3k~7k2N%|UHW#?Z;9j7sjPCt>(L0yM(AkyyFX8g$iD>me)3_!RZ+D?V zf%`S{RI3$1!=`DwJI-=1tC>bTS&8yg!L38~=F5u`2mQaJN0M!VoGwt`uU9pBnDG_FyGke8Vp zB-fhHxwkPuH8d`1JdcawWFf83C3fAn{qZB%K`dTXjgPlkRK{_5FJouq@G4@lPI}ZK z-ks`udzPFOr)Ak*1KOMAicBk~pLU=%e!tqZk60tXu4R}Bb04qQm`tp=U0FRpagi-Q zu6Qne(yC<*os-^gGy?hPs{P4RFDW)Vwg|�Ndf;PiCv&z%?4E(yi7Vsu0#bgx*$! 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 OfMlhWp5#4x>HTk&ODWs{ literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_project_settings_02.png b/doc/qxentityeditor/resource/qxee_project_settings_02.png new file mode 100644 index 0000000000000000000000000000000000000000..2107822c8491fd8a35893671ba02c9cc3c8f34c1 GIT binary patch literal 29294 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 P9;B^qaJNeJ{?mU0I~X{_ literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_property_settings_access.png b/doc/qxentityeditor/resource/qxee_property_settings_access.png new file mode 100644 index 0000000000000000000000000000000000000000..d418709a50a31332d1c69c931089767d42e2c4ff GIT binary patch literal 8036 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

    t- zg5GJfhk`wp3WoE9)1yv`8X9IV`Y`8ZGJI+oymbn_ne7?f`z{rpE(sFE*%|yHeGG{g zabPD8kl9i0RoG!ky`cZw8Zrm>xzcdQP9`Hxa`=;dLdT$)^|>adihe$3fkN5NE|_1* zwDW?2qjQq@AdSl5vO`6+TZXhj6Ij>;^MU3OT^cLS^@$&S9 zU@y=@ag)>@~EeP~tL{s6_N4{k4C*QR5nu1l?SjG_$WZreY8=udI={ z5ayIe8bjY^U&_~B;oEgRtL25c82M6VfI?XELL$&VK0~|a*6KCHEGI4_JD_0AF^x0( z9Uq+Us`om98B>*v0=1Qq{Ze6U&y^tPzT?;6i zySx7+o!ejTj5dEWTK(1+{6F;h8G?Tz5KdH5&LI|cA_5`DZiqPYsJD&qv8ej>-|{+f zY2T}Y5USwnmkH6WU|5y>G^vD~&f7$Nj!!50Xo=Z9r}x3EbCNu+#qyxFpE2A&?x2yP zd~<<5Af>KyiTsB& z{XUwCPLR10t)*kR15x%u{8<9Onh>X^gpOmG_k5{yUMj@=lI>zDE1?7J@IYW?;c5k> z-!T`A$MV+e$c=3x(Qfh&t@k5M$rK7m1?G^`vvwd4s<5ZLhDx$G+`>ns0krRodc%#r zL4Aq!9P(dQW(fOieD10d0$%Z@*6~}_&~#TMPk9Oa-4Mw=2|2iDV}`so9Qgj7T;)Xs z0q`{18c0x(F+)+VThJ->cMm~gNpPu#c#&_*%YCvI7_!>S)vM!?ANAxdmT%fR!z5NN z3{!3vjBQ?TPGc`Jdx(W>$2*IagT^I|sSK18?`wK~@y#1z8x^tLo4@tqlU>LPkLhk> zNWg&fd9v!M0}&2dIbNlsDanpyzR8J)J#VHVNHoMg@zLBw^Bqri>UWE3iZQGwo|kXpOBx+&vLr;` zHrCg#g-i@Vt1b_$0$Q6jtg=qdiR!yD&gwI^p=3L+0O+;{W1x-f<24GVn} z3%+~dIQxG2pqsumK07cifgR_gxl3xnSqOxOot?Y(sVg4Z*@B_+61NM!eOcQviqKJ$ zQ7Z2`@Y-ie-`(F&c_MU`a!1>Vj6w9Nz5R*OSN$cv0luig{HuJgCkF=;)y1!;_ik+| z9sjBzv@Usdc~Z?AdChv8`u@!bYusuuV&2P?HJ3>?oGn}!rA|Q0_&@w|qQ3roYU1&& z=jQxUGgbK{x3#nCgE+qZi`~4?Jn8$Ft@*?9KT#Y=Pm$eRB|c*z%VOFY+=$OL{O%Eo zvW+B*`tc_6_n#da*}{$Nf28&Agl^)F%e9c{g#}Il@YPSZrTV_>x!yJgTK)^3FL&{U z_xnRwi6S6XanxgOC%p!4Q_S6ob2!5uMm24S=AYGIXquu zHg;%@H6nL)zU1KnL7YV~6BR4ahc16uYE>lOXDp&ja-Zj~Noj2__0%a6B@kJf^9HO2wZI|^CheK;*6YpF zSw|$V?Xxo9@a5^_{yq?dWPIjLk!h*Y?Ygy;>f4&uf{Nv%{D7Iu@dP{LI8I0O5Jspq z&ED0B2EDp2*6fuvGJf42HKLx^QS&XvJT~bKiw>>0jLM$*USn5_%vXQ*E1#mu7g83c z3u5VuM?9B=BIt_?xwRKQj)yy&SaKd7`sr2=SI$?*c~Uj_jdfhtFJ4EVsaTgXn)?Gp z@v)I0MIOpj4viE_PR+%__dXsiI@?VF!)p6>m*+>STwN<)wH=`E%iM=_R_j}**PJH; z_itYtR#=6ny9Jp-HFNkxR?5-s?zx$5_9F%&!w;fWx^5~GMgir`CSA;nd5TeHg&qPb4U`Y756OF<=`-WT@oAkFXdNG z+jRFDV7Vo2K+^XHu%D!zZI!MaieqYb+VWhaR-W6htw+7#L%#{O ztwk!5G}Tr^p4DUc5XIG+-LM4p2Y!Z90muH!KXqGO)S8{+f+|81%nkMpNSkW!x3W9Q z_pV<%Sb>eVmxBIiT&~E04e3IcTe@1m$GyE*GJVlMRg*J?)Ex=42NvpSPmA2;l;{uDny&b7F*u7>9 zXhv2R;sNT4O7-Yz3sAVCB)Qr+JZI zRR)>9Mj!^&j}!_>EbetWp5R-k@0!iK=_*Bgpp2&VLleMr3g@nahu=;zl^vi@13uj; z>UQsx>I&CcI}X;H=3HEb&kr39pU%pScqR%J7Ft|Ocy9XvxgTWvCzqS3g{<(&huy2Q zuseaB@QKTjoGG=ehx^T&I1aIAn!hu}`6|TYs$%_GGlIM5N)FSIILi0j7XkN^Hz}Mv zJAu{d&wR{UG?_Hj1OM?ZQ|*jRt=4y*_7WrRVnz9Vho4m%-#_q0ZyWl`{`Ni(XiyCa z^SeHT=?8L9$bJwS7gZ?L%;lEwKd5E@dme~~5N2w>pB-Mn%i~V4)WvG^w!@_0&rz^B z^+ZnD_$8OA+j$3)Pb@6QW0T>mBx)7ju@F=c2IR#=AJ0^HEVa6xB-GirVUGNsjZ8O^ z1zD*c?|6!bKeGG$2dhd19uep=2e$2VpDmC_W*-;@K{b04Yf5RU1N?oqlVgQf7*G?e1yJdYb7l%`Md3n2@5g5pB zo$Y{n_J8Zj1R7CXogXO5kErEAH!;5XOFM&^Gw#FX=^Fc=y*U7gzv^C3|pN6HV`?W?tVBe1BO@x z=ie`TdDphrcm>qYeQ`OfYu}9jZ`=kj6le|h_4U;Up1kvcXx_~AmYu%o+$9%fp>C^d zu}U;JK=c{~vS>$`Wim(Mq-}ZiViI_UfakB!87s-GN?Z7`H~e=|3heQ9NYTRz+YuS< z`GG^!ftJDY=~4|k7mI&8CPBG}u0MuhezM~iJgyV{v0EOUoo;w$XYeTedmv+InR(A- zNQ3ym<%JPP9k6iZD9?uC)7cYnEp<%EvJxFw3- z_HT%Pb@^H~n{j@a?ywe&q<3X;`%L~Rt~my4bJi?JSJ|;g*M=@(pO-NPmGtC;_{ddt z20j{^QOD`4Bv?lJf-3gJ<7U_e{ZhUVzVKj>315*cN{>@XaM z?0!E(f&)s@tUQ#S|MF~^Y04`1STc7;G&sKXBM7gW73flJlC1F@8_S^g(8sPejjOHJ z!z$@vpL&FVCHq*LlF=L^XH&bQj=%0f8@H&oGlI_AtGt`5>D3(+zTohacErr(HQQZw z^mzwu1pf#Fj)raWy*!L9geb%u6RoAtV-Eus-UXes+cby9w%%-owSKF?3m5Yjx?EiK zgkJbyS&JsVxt;F;Z+mb}oC|w}H1nEQmBOntOHX0X!f)v4=^*gpvgIbpyCcylvs{%H z%&v7`{4-Qnd;PO@!KL8>QuWjMK#-zoW^4xsfOD6!QQF2)KEE7EBGLF(H+YWPq)e+= zq&;yb~3&i&>0Y1{KlJc-&s6c9u|o2t67vln5#pMK#PmCLTxoHjdI zhSIoITVBW-AO12D9wCLiG5(JvpR92J@eC?8^D{>G$$;C044c#NHvfu6@Arq4n#`-+)B6x|gZw!D|U>dr?n6}3Uehb4ZXaVVe?4L50 zGX?5ULt)L4yW)F+w`wpYnZ+R^>(?Lnd;=V&W~oJ1=VsiUahVHm#ULQ{k>!MUh(&Ti zn?rLI)NCQCP4onO?}&VsdUy(_XYu0<_IvP)=uxaS8;m);}!1LVc`juK7o<< zi{$O|p^u!}uFTc}E6EolOt+rM9LQkqwhA5mp>7Fl+F1}7rnmG|6)!lnY}WKQ$|#f8 zLDz6`ylO~@*qQGbHz=tnlcE}t7>9XunXO`$5^qmMQqw zrklj^hu0_dTyX>cgGC#)-+L!*c|aLnZl_hzg^(*t=v~Z_nU9c@iJpTMf5Og~-p<@( zKH=asbOsQt(d_j#NHpS79O1|0l+QDQv!?ap%^!hBnjaap-8lmG3G_GhtpSV$rleN@Tvss{Y>OO%FbvOr*GIV(QD4z6T}X# zqD_;#ebbT1XRG(@5AkBSf2dA@N41Qor50_q?RGI+#+^?X`e#281g=lIN}}o z)0EzqGdsf;0UVMb5ksow)9tCOHIViwKX z>;KCOTCb=*(=ll}&{=XO8u&3~3a$e)MVjwrA{DaA{r_x2|f(<&BiCv`|3QFik^gj7peK0 zbF?v+bMRS`n8{Ie+nRyyf7-pbML#2)IhGfAS7Bu@h|%#b_E@wjSA8^q;xWy$M{Vlo z&H5_E=EXBI7ZpIgWjlScu+80WTVjI`|$$+i$PaSM5yvvNFwBPzedieOz@~`Py{@^j&X!0WKe1l`Y(|(mKln zHQn|cwSOdWu~#*=sAwq__+CwyDd4p`P&Z(>=(0L3k=|&yv2v6_f2<)6^iovPmnqND z5F$8-qJf+kA^e#xNKra^P=2$OQ;wjBg-TdMU*Tw|(a8Q{@M$LNUn!u4#5pqRv2P%8 zYPF<}QkAlgRZ-fKO-=O_SuCT^?>}>DexVHa2AVbMmv2_~#~C;~>W$yuV>0@HHC;L8 zK+UTrzqTU1Tj^>y_sjK7UNA>rXuzfYPMTrY}n?pLIF!I`7-HYLK6^(KK$IF&r zy(_PNeew`K3u*~6)Y&9!;_;{L3cXe)#g|t!{gh3AcOI78>G^ zqcYa>>M#@;x?eIx4lJ_Y6y`#u1h7{nh4CQjhzzK{P)lxSdZt1~b=Xu|#dlYJdCMJC zUe(>{bYVJ!=~L>ye z6kkYM!rTIpU1vw<>65OtUm_!VEC|o`;7yT0Jx&^9EeA1=8KRYsmsfSa9$7LgYa#UH z;(99@nle>Yob=4&Z8d+meN8GuUzPrz6r8!vIBsq1afda}I_eUYmThf2E~vCAd_6eG zbQf9SU0|Qjv0}R?yuYcHf*KY&o$bG)`&j(OZeaY)&{qu-gpOYokVI_!&vx=wV+w;v zRPrQ7PkZB7c?*%ig6*82czz0sb}>yP>I1ia5(JsSp#zR zmXGZqW<>ee&PyX^rEq(N*7+>rl%7h}$T7G>Jl$;)gV3GiXsT4I3m z+r=FCrp)+tt8Yr2$GxWgGvv5CyVT>(VcPJSttX3J$x=w!Q;@V;0Cp1 zVcagr*q~O{H5pYP>UKpMw02Hw%%zl(eTtsI?0%&d|Dw?=R{oEI2b4V-x6oIC@uLE! z))+xM)9btx$43I&>MeBlgYbDCE~H~A>Kar~9e5GxxJ^3J~IaW>?)-@BvvyZ^LX zwlvP!|3#U{gzxMV^%r@J^d{iQEXd@orBM-e!->)R&~eASl*9o2!PnbQn{}@xs+~s+ zo}0NdIZeqOnV9$}6r_)?7}lx7EmC%Tes9-7W=UzBvV}(eBKy^h*RnoF2Tt#$F3qwe z-8Q=$jd)1=mgoE1{rJuNOSp@6Z#I)py_hswbLqq#oVIfO+4rL*qesfzHh(4V-GJ2J zfh?BZg~2l0r0%N&zWIr(36f7^3z`A$*D?F0b1tI)d%=BkS9n=qrW;-yGYbTy-Gd4R z;>3Hg>0^9}gLEc~W%)K!ti#1q;bx2UW#VTqeqDyD)piu}fIYk{Dtv>5;*9ipc+`lj zJcG3Mtknvbj1a4q6~S&ve53EdWgo;p@5Uw~`AE+AS`E+?8&dav9SWNP^XJqIfMkmM zpPh#J$<~CkZ@5sSjB9*J4KnQ%!bz?E=Fg;{ccMqav=ATI_@YimT*c318@U%I9$Ml# zII=rf5K4O9l}i(vbknq;e@8JPTzE+`+A-;E8xIX*x1iLvolmI#oYvmSScowHMbLb+ zwni(d%eF#=+4S{h+M+jyZl+|kykNUnh>i#aGEdeGxb&GfCrT1sM zfzpqc<>F^qR=fYLOFvjy9~wKT{l6Mu7Q4+M&iu3zwajG}yEn~DE3BvN;TgBe7PrT8 zU>Rl}bqbqS!a=G z4~Q9QbR@X-xYR9=gzcY5{Y5Vn-%tOaME8)-ThCwp zv(&|d@B0w~+WaAU0~kz7ZvMB;!|D|`TCLx5;kmv4Y%SH?FdEsY zYe)1_9e;|IHn{xN*Q-ck04li)&)83CJ5(=5X4AdZ6y!~+Ez=uzM|Rh# z{8j?z_D)++ffmD6?mlmsvI5+~o76jX)ePP<;W?e2#=7(Y7jp(vka4HOwfF5me2xWv zuie5HbrcqnTb+H~zI}ev9^8OIF{}xXNBVa#QkUNU4)w(n)2Rtk<_x`9jK(LiZ1Fx8 z!9c4qw)>>lv2^Oc zWY0?Da!wh3YF!?euhjrD;XcgjL6rl4S$>~qBT+zG8}Uy+nIqb=0~xV3^2|vt9|-z4 zs_0>dj|l^T4gd1@Yu@F}?0#Hpm+NS?I%!?HBVT}g4AW(Up-CP$fCJ-3^PkQtcK4so z<=pu>Ky=?IGvQw6{55{m^|q$9;bceS0NTt`ABh&cGHHM!4cX$VEKeRT6-GRL@|?y( z>wEEv%(u#D+jc1mI+C+EN?rj|S|*qCsAYzyhjK6G;OPcxofiGTCXNaaS&&Xx$lOO2 z3CTzJc(=`t?Yvg}C8JqH#k>-CZ!VdvC^Cq&lW~P#cgFntdmWl@2fu1cfj~rt-cy_$RDw9#j57R>rMzlhq?D479hS{3qSu1iAv}5}8x_Y5D z7Fs1IfE?Re1jNfva!YiL+y?f{82#0Shv_6r8i2-(-x74Y^1x_dbsF7`w7YZ#Ve^f; zj+blVd0$Pw9o(}8&KlkZlPZy?iO=O860f zcz9`o1b+J7$>rdPZ-x3d#3$vG&{OxS3@327kK9DWnEc*I??mMmjJaUq5)H84f}Kfx zLfsH!|8F2rH{qdOdEmdFPoiGLbDT>25X%RZyvuYJ^DW&L6M+1^pp^OQ%HE5yIRpTZ zZF$K|FhZw)ycZO6rqz!& zt9%D}20u)`N9I`r(yqF#q8>Rai9M`}~J|;Qw+7U?+T4|86UfEHYrlE4G#0 zAB$XoOuw<1*8V3O=uBy;ye-PrT~6(LqUbsCA6TXiH{@;pi|NYw zeJD}t`;m!>iGqIn`Vt)K6={FQt?VnoiQ6`CF^`bf%9)H8ig(OvAI*nVTXr?*qx?EF zJrP?o7lXZZXcmZ&+mm~SnUdz5(;dRJ8FLrfG#b;(HuMqr%VPz7MjyX1uc2HOvfHKy zaco6bH!i5uaf{@ac{P5O3Tk8gaqu*)R(GK~?uH_}l1p0hrGkj2rHp7DthpEPjeI|A zUe8^iTmoL=oG2Z)M_nn-b=UxFLr&_?!|*Cq7Xa}uhtUR>GMSgEcG|VR^Bq7`OG@u4vnoT1A{*(n zPu_jY@9l`7pGDIvlE_RAfc%JVVLibTamUO$*&NrM_40hBY#Qhk`8^%e9C-!|ueX z^Figyf%t-%QM)fU(@PuO8{#~nvXjtp_K`5%ACbJ>6t^aa83>y96u?phMj98-b2l8X z#iRD0JQCpNtAV%-vg<_3gDLH9f)DN-h<)XwTo`1_Xu~4WJ_HGNUzn|eG&-3sXpk{+HeY1?-g|m&O7j{Fn zYGU=Y9QSFw4nU;?PINuxO^ ziYZOV@thU>pws?0nYQ?aqGM&`8;?N1t4M8xkxjiqmw!jYhh4NEIt=Tvy@Fwja!xT} zk$s*f(~#$WhroR_I&@7%WW2IUJP<<1yC@I9LXA^>ZsUiDT?e^@Sh5>=oUcFmmoSbc?Bu zR=#NnO|WWK)XG;hD*&@uxLD;VAzcR4&0NqE#YWh{>*SU@-#L3f!1Xl_ECC%QRY>;0 zHCvXmuzp@{c3t$9+HxIo#W6O9>d?Kjtz{bIpDpQj9lW_LcqY}23qEI2!F$_QU?!zp z+OFP(%cd|VQ%wNW2Y-eG63%0v@@BekrFzB{&HaT9+=leN8d93+s-6g6>N+iA2_;zz z6WyejW^5sf|D2r;#RGZBJ8H4(r;#>%tL)`DhPlmmuQV7y8~HKDt)&LxN~9PLZb5&f z%KccX;7c5{>#oJ(+T>ESlqu~5rBD72dVVH6YT1zfI#Hqi)93cuxBgNHzX95nyo^$! zrr^Z?W*w8^tzv=wN0jh`cEkpq;U~}}XQke}qy%{(%B%eT9~D7q(=`sP%a)K?v`B>5 zeg_;6HiJ2s0vG$zsQML9{s6yv+MUARV`$@FDC2jz^?wNE4=dK>@EsyS?D61}I((xq z)>0Z%T($H{pfthE6*+b+q`bH-zs!W81l>sm0L9A|W?4U6u9&!Zw?J7Fhq$=7$C#mU z`mJ~8T2qD0a%*Y~g^{~@^_bE|o8eqFpTY`fx#oQHrz~ecUpORBIucIk3kn_nAPKvD z@>=vyIrI+$;< z*9SV{bhlq*M`O_sz=Xv58oVrOM|P!*m9^$TENL@k(V0bTeFpNnlmc|`@oA$Gn_yI6 zOsY}ue4cIb7j>(|8K+|mCEKmCCKy?Bdm%en1Nvx|rzqziJdJB<||XEqLm(_wbx0Pe);pVej3K;v;4% zS|Y0pmF;P^m-vjcpeKIyK>K}D*Fgg=oN^kt;lmqZ`sKWR6cXOzPX!Tq)D>{hF-ASZ z-Az6}BH6rsX-6`#d8{CINxnvGt@3=0SFK>}>sqI-eLXC9spU_3p5{)$kC##zitL@- z`EzP!pJKXbG9W-Wj2N*Q`MqvHZG&iAMRNwUXhEZBjY_*{@{rRO5G>A3KW>X?zHbiH zfpo-K+F9|`@RR~tWB2&t>b9X~CVPvm%!Sm6l)^$wzi0M3FVg*lijZdXrF&oY#A8O$ zc%YE|Es^=9ZG}PrF;Qcs<(ugZz8XLUv2LvHOy4hrZQ6-@3)!FY;jJ;Pvg>Y!9Bt@E zwFE@_6fW~$&e~A2*_~?}N3oBUF78_SBEqrxtw}3Z13N(d6~MWju2C;pJ#e+W;JbCC%~Vm zr@xg+*2^^3EmGXq*IroN`~JG&sA^tTykr_9@>t8+ZqCA4OV5f$(l@@Dj=_SBiN^cN zV8|_68OpVL2?h(uv}HZV0^fWV8d!J0WiygJ+5AX2gXQy6{Z=F~%YZjp2sX!dwrvGD z>3of6DhPO&ri}Bk<1+_?+JL0xVFt^Sa1CQKgxFC^s8Hat7RM}-{YWWk&-%eR&=}V#=f!>t^?M0vJS`Em;ct&%$t;+=lfTtXduBTq6 z77|F*qvKfY5%cD7ymOfsy)ePkpE5gqq^W9}H2w#z_GU>%^0h|_Jw{?0zJB#yl4X?H z7k zJ@DpTE1HaQ*q4|J)J%jYlj|g`zg($NRmrWZQ6UV=|Fp|47)G@9Dd9e9hDY_N z@4S9myNjZ9pR$E_!%@e!v{7wq9`cqG8h8;^vN;W9@Q;tEONFVvB|$1*Q$?_REKc9M z*N~AVP+9)y_JiDW^Rzm0>=_cyqR_pcQFAaBao!*(@AyD40n6nQKo^dc?4En7GEz7( zJ}^KRVC#;N3~zGvmE~uI^fooB+2oO4hX$49h3!NKurZcUfP$$H8I-Phi>ulImG{jR z9k;hq?ux807D6Ub%oR4>Nt5t1yCwH#uO;tW#SNCk_TC?lOUypQ7?0(fFaehEiYTar zFtx>{tM*0Oq>?GGW2ll5i8+d73nB`pHPfY4a;e_Td2IdH{&DU1P6og&=40(!z}81~ z>F-3|@0MaagA(YLj#0@Y+oOc%d+TGhm%C`Pi$<@3%}z^U?K6-{j*W{Q{;jIzBu<7} zm%~%E1^V&2j!T!ch@YBn@~hfxGii|^!-dhuxD_+JCOgvc$j{}{r3lrOeXaF)hTjx$ zRZz_zHn9|w@Ioi-$J@6VW?+!BpkS$-YiE5oCf{qf7*;-RJ8yQa6qTpWk*Hc+kia3YK(V8A{@S=VrydvDNYcYP_sB_M-%sHIuUBB{LR3@aJr)qQZjr_l zOdw#&5P3?co%Tn!Aw!?mIV@p zGC<}K1IRMtm_$6h--~kK6&SDo=;j`=r5P7!KajRWJGZVnY;=32hwV{qLu3TY*zeo` z-Mo3zc73$KNArWQU!8WYpL5CescR1*HL*S>Sc8glFXumx{^i72q+vRxuK2m^6iH=9 zA5-7=Vfr0{vJPR;@{}SCrYFl+?e7%uJjv`NR^`xydUOp-@~ zm7HY-r1L$p7(=K@dU}vs7NkFA4wKOkGC`FofgE&BQdF7IEz6m7d*#h~-K=aIE7=qj zfoZpjTaY_@q-qXj+S6lnX004dg;-RfI@bM;LXEkHdKTL4{MK{!d;%e>*0Ym8Y(xi+ zu#(uKsrd;*1_j7xLVQY*u>0&A+nov1SGJlr*sNSu84n)q%jag4rFu1#AINygXAoOy z^38xqnvv3?+%kBJCI+R_6tGp_xzEn)j%;UP3;nzwmaxh4w3acuXb|t9mcQsJZuzn# zqkLnfSB-M?ccIHo9sGy@x;pvZ%k-P}lJB;xz8l+6t7{icp3bhnj66p#zJpVHRmAKG z(Z;sbUMt^fgj&>{BB{Kx-R}*)U|Y#jz$pm5EJ*fcE@M4H(AD}adP6iq3x3!f(WJvw ztUW2S9l6-E=Z~`ldqLHnbcwcK{5n9{Z1e+n=v5C5zI*iS-Tdf)Tb7+d$iWp!qP3qO zCr@%`fA1lM9-S2GFR#>$wV7uAIiF(*-jv@) zsI|>Og~u`F$!gvZShY9$0mXWr%<`AZi?ezXh>SgN@h2D*yp_g0H=a%o&iR%>{kY3`d^@MU(;P$p-dS$5%020vNV~seP zYnyXSTgW;4Ye(-!JgD@}qY(9zha3qsSUM2*f{Rncr=Q;Q&Or31|9fDy}CXZSLQpu z&}MBHY0i6ps!xRX=ljul1&M)AK*3_Hk!JkdG-<-1chZ_wW#oGaAYUD$Af_jXMrY48 z1wm>G6K^>My?5mv*o{Q!kG6h^7&O4hc_G&cC-?t+C&&9ZOKn2!nyo`5<~LK#r~}KO zP7z#snw8^RhkeY6wSeyHba#-eIOQ^Iq%U};5WSePi`@D00bbxe7Gp(fK#N`u&H@11 zuQ|avkzmdBSQ=-@!MC?)>U#e&_Ag|}{dchnY}JaQaY-}Gj;y}v!e zFp;93PvU~VE5~OR7V&!D!8&dkVf3JAj;SM^T1!LXSG_%>YrSEj<~-eN#GFdKP2u#i zcW=!<**E~E6#67Bzc<11<`&Mwxk|)9*|%HroOi#WLou8iKR(0jwvtK&T<^$3JoVRlzui0}H+iyTn39QKtK$a!%(?gDsyf>!rY*E>!G0 z%>F^OhW!SnrM8A!!s4B&bQ_+$!^L>bJvKIMK1e+c-p$UK>{nJ0s`;)j02sdQ=*_VV z;g}YLP21bZnks`DlTxa$)9p5o*a>us zFOWAl-VI|nP07DBX{t+spCs@ypkL2u^UW!#x1|Z!Y0K~@nVa*z#|Td~e16_NMIP{o z@hfZ~C30Y_PBIP<3la6%ob@ubb|fw`kTw@eBciGeXM*Ft4;ilmkl{<_yDRM|cc!?# zq_f#Y)sxVq-u-zRR(DFW1zv3Hg9iw-6w!zXYcx0;_rfA<9n3}eMb|Vvf4CDTHW{M4Ped$X zI?ezkyu>=YTW1^#|Di#5#HE`~YWGZd)ftk9V|6~fH_xrtQAUJGq)vTKys_euE8d6S zM?{UL6dp{hU6Pz^DUyZxjRnN{6w37Ol=lHtQs2!nAZ%cP#rNCelS_@5GSpn^$A_Gi zK}bAImVeKy2+Sp&iJru{Wm44(G@S^~GJQImP16MF=U5KHyrT+r&}CIH6nYO>PLmmH zuH5(=n;v^lBh6(NXI8~gTWnje8qla(!C$1exVUCdBoyvcX#)mL6{67PzRvQpv3D{S zR`c8v2m@zmaa?Bh*6~b1bnjI}afrYB$jtJc4z=}Xa`U%pinUTW=p>eE#iI>o#=m}` zN>on?CUyf(@>^S{PVyE;Q^1FSko103w^=)51K>K`SXq!EtKZVq`k$YvwfrhUQcE%H zGPeo_t0X3ItMC#!(^AC|^_ps#fwb`wl`%Gn*gV&eb^1GyM&ro*AFKfZVpC!b=13K- zY}JzW_(`)bAEB|T*@dL?H_vJ;BI@v*8TlU-O>_GLHWx&>EaL7FbG@srkszYw80RnZ zcp0PFO8fT&zucLxAkaIvwPQN<$QDiHsr+5!TN0PX2OtdQRjEk@$0JiyMhMXQIE}GP zXJyF6bfRMl8&(G1-7`hjzh{i~@qa4!c;tEXvj1eZ1kC<3Vb7}ZVym!MRALLvWs!ui{2=wtfY8?*k;%&u#(>=|pNnt$A@%5=RU9`7Rlu-Ne7B?LU8 zS`@&r?m{91Waoc*1Ze~=30aoWg!S`x?G}pW%)XAx*V8KCKS1pc&c81lizKUc;_UN` zTJZR^yp!F1x2z;!W0$2?uh^Wr2&*AEPj;WHu*uu)jl*8^Oelu^3V{$XCde=i@OTTa zn;?HEM>YdFub7X${GdXE92D7i=r(&Q!pouHrl4!d)q*kGLbC$JxRay@KNyslj+7gE zk%SJ1>8~%Jtw!9I(iy>930@G6qwb;1UoJ)`%iQ3-OY4mPVvHw z`R}&br3cNcjVnb`#pJ260)fe(MPuc8+FGJLTI3=pq)zuc;L4W$(M$<@tv-3eVCj{0 z9(apJ;YRAtl{6tm>yx!?#Dx=UFJ=}H(G?5>eBL+UDnh@A+6~~@tL#}_1in!VkV5>Q zx`M+PcB>|mE7>%kX@Rg$;wAmI*BufGPl!SJ3~lrBuSc(26mQ zrsai^fgb;+yyYW|gJaTg5(l8)#p5f%wDxY4|HK7dQb=q8i(rn677e=x0 z8$BJA4ac0i!#i(U83_RZZT$*Bv8)%>`(Bi~y^qJj=eGR+U?W+_SpLd-BiwZb}MBk7fY;r0Sl&K(C#`^ObsTHBviic8t$#2oQcQ56?D* zhDj`>Rj%t52$aF}NL0F3s?!e?fjF$a{_qze^v#opVx2eM>0UYehxT=~O@cOGiGb0c z<*H0$bP(Tqq+w3uLfb6CEHouWuWjP&nS(X6&+A~=nHUFKTl>uGGmGs&Jkw$7J@FuC zPv4DN?80H?C~&!Z${!k7WR(D3UQbW&Nu=EAF!aE#lD^SoGPyQwNAzV#4$z#jUz?{j zuhJ!qPZX|E!reDOcbK1kpS)Uo@o~DKU?cwdg_oXv42njl;RY_Z$v#Wl#vZ>D1A$)N z14ifnOg8vkg#+^8nOf&6Nd6bV&FI7ivWffmur?P|(hlPJIj&>S6G~3))V(&szH88| z05n=E&b7E5cE<5!2M(vcE;Vzh=h{6?*~F4vJ9|bd=Jex~5iO@OrQx9pznzu0;eQwK zSYKDxew$z~>(i4iFOuQ1UMk5cddIpmPT`a$xTjF?P7*UeZF^EE(z8)knqoSCoQhrv z#6PZIptee41mg=zq#Ay;Wc;p4`{{kvZ^icQfs?vI4wPxwqA}b}RmNZacLPKlmuGxl z4Jn<@?=D!?3HpNcW*+>0919%wtpB>Szvj~6g)&)x^AEHJ*Y$}b63(f2vkAVkV{1TW zIk4-uE=l_IGU~XBwPGi^7<#uzkoXPM9x94i=1Bttx~t~rxV&m({X<#**$NDte4(Mn zE%}%42WP#m6U5JBSSRbZ^ojXsfx>2Z7wz#gC#90K&op&_o>^*%we{HefcN;>X8cA5 z$cN||)ymi$#XtCUqhS}yoZ+r!dr{Gz6yI4cEzO`^wdb5nl=0|AnDgEy;ZJ*!W-ocje)OY-idk_+M zW|jJ@Y^USp*5x=6;S}0PFOVER>;&U1iq~-wUyelKpiAwNsN4G7I;m@$h@Nd(l7V(p ze(1ZxfQ>febeUhKrPKe%UkY2?S?P{$<5@zwD)4to$cs>*jt?rKW9KSSCmJ3&wi7jO zowvM042U-}ouv(m=jmUPWKsBED|Hua@c?3ZURz~pX^U8ye|*`NWZ`JD&~;&irEVw2 zZ{T-WLW3UJqiGPI7l+>SlqwwdPjOVK+BT?g8!J|)2 z#|!+@QqR23z5C?=>-yCZskR@n*>Ca_bvxo&f|D>b_-(6MJ_{=oc9Ei>`enOxzws## z0y;mV;g^3Jl)wXcX~j?%qiMuaVGQxFX}ihh?P9IhhmfK`Sq1)UMcU<#xSmLZl)w9 z9nP2Ge-N!;X5fc>fT6pbE5{X>@%g)ZXRdR{v4A4lz&>*LC0MNukmt}V^uALhvwMqp zoIS&?19KHcd4pgVQ9-SPjT3~Yvf0t?FPQYWQbzcy31EB{IA{R_kY^TPkK z^!~4F@;@&C!|dMyJpT-ze}>OLgW;d$_1-_z;Gb#m&ouaF8vK8R28o}Z9F6=&&DUg? zPEm8!DGe-@@(U0voC0FN-w%|C)=q#Tu2tJ#h)9Gu?dL02q3JVjQ%nv$VxJ6KV6|K&Tq>T}1$Pz0$zX!Q_{s+=mw| z;X0=Yl#Z_*U$*RuUI@W|0VP)_`TVd!`1nd`oN+^E_@7>kS8}Q zGbIe|GQ~BrUYuy*VXh~NLCp|U3Gi@m-)Rc$2eoCYG*5tEsauJhS!x9P-T(Mj;{I28*Es*gPs$Y9 zJ^P4x7->5m3>PPj-;RvSDz5=(c{gV1EqPf6u`4VIomnK=^oAFanr}q^)MjOnZnXK} z#o~}$A`7*1tVg$yAOfS>FOX$Z=I;V>lQIQWdy_+gYBhSj6cJ>~;%T zokYtWPz_L>1*n>U`2dZR0n*W1xtSd$vmbsQI}w6qL+OIaC#x}*_Et&B>mA3Ic z+OlcX(w9PXh&ahgLI5h^0xeG(=o7XGfgBW>C?yydK;;qv(EIqzBGqJkl6O?(vFy!->uHC=R;?h@$t3HYN78>-%|~Yl^2DoX`YHlr zWRr-W@;HhlO^?Rpv~_ea^mf#m?LQZO`ejmdQ-q{wX;yxZ26dFB9M1~F=j$X&Lu*uo zS)|xu+B97i2Q=NiLs4(HW4WW>1~p{zM{5;5#q@aN@D1Vv`p zgSiwaWcx9nsoaC5CFS~`p+H8|?GhYR7R5{P48@tRC9kE;s=vCB!_gTZ#VqS#J~t&} z;bO6C>@inNy=fkpVY64G)7?Md0^gkR2JGBxEGTwe*~PN0Bp^fw9>!0ZCFNo8v8aXl zq2F>jO)xD_FXQ}irLwkc|2Pd|XUNuL8!&#Sd#`u{#_vZN;aoiIZrJyWdIzzjgr}W4 zU2k_*y4uxfwLnb`OU!sS6iPK{?4Y)ou&=X>0`FGaUtrc{|9KZU++)N3;Z)74g)9TW z)O>vkc1(_;&lGw{2hw5Hm)0e3_S^XiXN0pPbmu=Aw41J~YwvszZS@0ux<@h7)NDv|dkyEPC6;uDmg78w)AVQOpH1PkeZeiGMgk zns{b&5D;mosz8s~Kk>oPjiZowl#3Y z&g9He(zl>5_RQ-&PQ>u5r6r<{)11G*YN+&t>ioVXqm*!AOxIA`gQXIPtj+2Ps~V$EUvTJ;D@UyzpJER~7a~7I*zI zqQDP5+?Gbzxc z73qP-vsLRyG}@(Hx=F`k*$ZPkA6D}4w_}_ zol@!k6q`mJLU?AqPukA(n2VF-@?Uz%JO?$IlltQp#k@Rc=VjKEDjJql+TIh6{gUsw zF1!zCZbfrqv|LPX`x`RuXe^y48dIoWNOsJr6ZX7zcHoVKn&oi^wI1n!#O-S&oXj={ zftgGAMlj3!e4`HLL9T+z>TNzAqrDlr;H2eM4{W}IAVYulvsGYQ72Qj`JlcgHDzcfl z=~~#rQy~I9oPCer>EWTD6T+f?w)woeXkMir-rBho2e`&zG@`A~$g?dL!|6BQ==8pE zCg2fT!xfu$^U7_ITp&2+-}{BPr1$O1mb)J_xvfksxvFfl zlZjJGm8+SMVLT!`q4A`b?picW8t4VS39V;k$_YJKqAxIBjuF<{cLI=sYW{kmZRKGf zNwAYfGm1Jm@<3(jDV(T-w;uAYwWE@$CUZT9G2%s~U?N(B5j*MmHaQbTeQ=D+95+jm ztj$dSgrB$8okx5pnFAFL{%s_lBW~s8wfDDi26O5#PYkvBpu z+#F_yyd-X=u$OnZSL;8rR?-{g4>Kjs@aibwb5EQ6G~odUv%bqmnIc^!fo$)65ngF_451#KxczZ{5Ezir!Coz;V@8g`C?Wt4D)!bXU86Gi4$d z-Q8b_X$d|a!uG+zg#?RqF+L(9W@ z3drw0n=^s44ba2l&9B9QV+3SiYuXR8+JlJ3zIK@fE}>f+GJ;-coWa~a74j^D(2MLq z+m3!50%5@iHNnd=k^>b+!dn`_21Cq3Ygi@VESN$DAU3|$YkZaZL&x9We+f7@4JJ7s zs1Iz;ES^O>U5U53H|jK>T?1dw_L;UnweF=$IKQz+X8=iDe!0#Bpa(JyaNaVD>>xW1 zEd0EGhcI!0qe>5eXh7Z#Os1tTOWsVlVU5GI`gx+DX{B-y_8@4$i;XZCshQ5|3-H}( z4dLV0qJ|O?Gp)B<0i{UGi`97=6_5G`mW{$DAY+CJV7-I4UtdYH5}|NXE5PvsM6NOTZ+b`=F61^|K7?rSTTDq4sB7e&u?!~g&Q literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_relation_settings_access.png b/doc/qxentityeditor/resource/qxee_relation_settings_access.png new file mode 100644 index 0000000000000000000000000000000000000000..dc3e5f916f670199c21ca897e8a965a0a5b9f23a GIT binary patch literal 18150 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 KMNUMnLSTY+RBj~z literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_sample.png b/doc/qxentityeditor/resource/qxee_sample.png new file mode 100644 index 0000000000000000000000000000000000000000..31fb624ebf40dfe07ab2b6376069bd85b8db6d0a GIT binary patch literal 261415 zcmX7v1yEaUw}k`6U4y$raMu=>;u3-fr+9HI#hqfs3baV^P=dRAaf%l&?heIz^Zl0@ zk_pTlX2QwZYd>qfvD%u-xLA}}00012RYgG;06=>K0KBrpKt}w8yJ#v1@r2~5t1Jij zF+qKRc!6Rst04;j)Ffd)S)w9dW4fytc>(}b@IU#NzMvs(?@`eZy ztFk?u`+4Be(BKt{YDlW{WP*K3*WZ%WvL6+$Lap&;)h`ux+icAAY^=L%EIVv$WXx=2 zCErO(N*GIvf%f0^CdL4nHumY;cA48d>A>wwGSc+S3~)xKfdP14-*91Wetuy=FncU2 z3Jn-P7cro;<=#!rlQsJ8E8l`0Cu$>OTCSMjf(~0>YJD>^vq%FS9UXIX^C$yjV`E)iT~kw22m}(6o|&1MmXni~ zmX??{D`1<;aiZ+dA@>+CD z1l}HJ3)*YuNqBGd$6614dXDkF-ks@ z9nZ-bXrJ_iqvKPH!QIw7r4bidZg3jkJE zub=mel4R~T!hP5L_o|xK0-kRcEc|!U$t;2{{_cJIw|W+Qm$Uw~yZ&;r{&HjS@5bU~ z$KnO^=%3XyopHTG3$R6FBlz#$#~1r%-woP_6St3#TiCqqx6y{{!AIjsub(z@s=JJ9 zyrwe}kMPdcPkdf%@+WV$vyuysneA+4shS$wD(Ai*91C+rKYIHP!tTy)UpK$L-{?8- zi!Q>a`y?IUMfHexYjzyCct-r9;po z&>8C0?Ca1y>olEp?NOx$`;679FisN$=4y%>=d_;(F6eKkli34zN_Xwq>`I49*py3{ zBUlE}h7?g1qSxbaZelTR@vtv(aGNQ%lBf?qzM=-Y3rbBfF%AoIE^9F(N0Hj3l9OYbq=X+2 zDt7l=Om7gBu`#d;@r!^sc%(RF7{wVFSr`OReLiEf#j9z=g^@aoMW>8x__?EqykKM# zyHE7**MHxy-_E+;UQ8fyH?`vwli|Y6O-}!socA>=4@vXq2(#wIh}y)sn(PEx>F&;A zUX;sYRvwl~sTev{bt%K%_q`pv?LAxIwmli?Zbrto;b$@fgY@}10}+vVVNuab)N#g? zH=LxtmD>Rw%g!R#*T!gQ0nyRu%0o)7>$Dx7d$+4?zNf2gf&X5ln`ddI08E>sZgNR& zUoXI)3FG9Xea}|gaLG>ghr6fp8@eVPgq;#3T1re{$&IQ5+<9`J;8itwLnQUHm|vX! zjtd9z_;@A)R&;;Q8!t>$l3ivq%7!<0)KnI!GHwm`n<^T&+_eQ=#MvvGZE*Lkr9-iB%73# zm7~zmL3U}TtUFi6hfbAO9(6^GeQs;*K3n%ml8tU=9gYK{>tp}+i}2S2j>ca&ZChrOn|mM7yj{`kJPqgXX8(7$zqwh7zSzb*gw&6$WS?--9?LPRjd^jS@Q zDO#A)t81e9QQj_`Jh3S{xOw1-@cw-0T7SUfNt6Bf7u3}b-}Mi79d~Pi|DNv(?nDt{ zf&cpH{7=&BuY&dyY~O+|m+ZgEuAd{W@N`&N-TCt1w9g&Z`F#8Ptm|KJPZ(-EklVP{ zW@M)LEkdz6J$BUg?c}sbB+90AB2Nzo6RAYq)^I*RrfyGuBa{P^#$Tw;nV`t=!#uRNML#8*;@EC)_i6YIq*L=!!dTvSDj^q9su zQU*U`SgUU6uDzxH$1k8N@d`SInJZ|{UCks;)fs;V1| z?j9cR)f`nF9p4)ps|Ez9Dg_vU?Zm|-E2Y>fk#jT6Qofn!WLX&LYa8jBnwcBx8R?jq z4Zx~gpaN7zu)#qkkFb$FS13@VF5I@fW{nW_`M>QVspvULC|p7Jn90zA zX(_9(t3f&TL{H5d8DMkIw_F~c*qLt=#3&i}z%y1DCF(Aj$$q922 z*#Hb)?g3+h-@kuP2xjn$Q;Cz82(`!$f|*hwGdXi{b8}O0F)%Rj@VH7$V&UN6AjZ_y zRh#!wMs;;HVwxcaUuo%{SC;qR%bj>KUZPa*hwFEeAHxC@LO2OpmWGKgbHzQQpuCW; zlD?->z!J+elvp^afHB^>M7KM8-V!kSxwZG?RzG`61X?$6@asd`)XEzW>I-0>7USiK+30V&k zMBGo4UH8b8AcyvRhHxKTN->AaeAI|}0!n2Eo&u!EL6R?q4lV_xtdqL*6?r9!@$*6{ zfe}&--5r8V-OGxvV5OPQ*b~R!nD#?uFuS)HLk7w%ML!EV#Z_>5jK&GZU+P}g64lOF z{Yqs_`y)?m6=u!E5kg9PY>N&l5FrjBRRw^{aG{DsA*5ME#Gfgtq>$i5Cjc%1QDIJe zepprzRX69DI$RKDCD|uOj-3KR_eytGHjFeG3(RruZ?%{CCl zIvByMfNt$dN^OCbYgxqfl0M=Cx@KBdPgHVt|Z8cFLvEn3sYG0uc3@AgCVs5m{znDaU?U1^X zMa|loOOKhWD*>#_IxOtkw{cfLe`0ZQ#W^|tq@r|ObhdNiYOAU;SXtsp^R1Z0>M(Z^ zIH;1|eR=uXQxF_cqZB;35~p%Ai0)H$y{L7=xcO5Up}S+qTu$Q?dLr-6BW=39gow7CWk|H~Q<>sGtC+ zfFM_GZG}f|rH7~ErDx^Um18$*OGD(}hJi?LmVt)iVsFx&v5JbQR%2k=h+U1CvyWi( zuebuzZ<9mzxSe83{wnTD{fq?=&@oATYw|#teuPw`Q~j}SXt$&`3ml1M!F7H_-p7VoDE4Kh4GkJmwdmx*b^j<`i&gNR`P%T&Uh zBBe5pfCr@!f~yWQ1`WA$YqNkAQUAKJJT)O>a+foMK3f$*^EqnF*x_907a*dt^xWLs ztSoXeIxQ$zK|ulG*Bu-j+}-&?jb4IZQ0)Azmn<`wMq0fNW(MNPPBvm=g8E~ybU5YO zpqOD#+CRs~apS^&5XgE%wuzltkSvmy%wLDvqNJrb@HRK;yqRBXUgio@XSAqL5WN#tO#oDSHE&*E}r~nb+Bn6B?-7==q8WTJ#NH=n3n@h`kDj|Y zUS$PNIMNRL3Go9~V&su>vVO?M%lo7lGDNPZj4N2!s4D~o!(2HSRh%e3Y+J`g`Lt4R5rV;8s6oW|Iv7yz7{`gEQ2fH(F!Q|W<= z@i|>5xPtvPZllcdB*BSW!iC__y^+;(&wMk$T`P#*hhUM2&S8MA>iq^l4^+!9@`moD zI`MWv;nCNcs|eXf2Wh4ZpsMhOO-w+WoT97(WOlKnObn^)@ zu#@2!U__~jMLnrms>&+Ls}feiVhqg3IC))QAZr5yp?Uqfqa!g~+?A3NAdj3OGHrE7 z6>g`HxCXO~#tch}mnsVp1%x$av>2;Jy*$~MYnzh=5{h%=QLf9Qqmr@%9R>~jwbgAg zWkv5n#`)Gwls!K4E@PxvpamyG7m~tYt=dh*Cy}e!Pgx_GqBv~E$lpwCG#e{RJ}^c7 zQL!0s!?Al4Dkd`)>XI^w#m_C0L(k1=ik+ygP$<_3KOC<8^hAa!urW4Eq%gyvrJ;c< z3uT5^Ha9P#)p=lysUc8;ni?Y$Zc$MY6CSUfg5YlKtMe)HYG|}BRfN{@Y0lXKKYL-6H&L-+IOS7i8&&!_nYVr#`7?< zHjg<@2M-{xQ7yD53^PjD9l|7khs|dtbOSU^LWR{1aUQLZPtl5|hr~#$0QCA%xnp{Z=L%RJVm> zW0DN^GFP%oj!Z`yZ75)w6_i!pbL?}zDyr0X9HW;rywC})Aw=Te4A!xXF;PT$j6nZ$ zCfBK%Zx5k|LDTx_7Y`FJ^K*wI+!=-y7lBH~B3THVRVaVVH`-&lq0ojo=+O&5mW2rN zf!N4LV>z@@HNR|uY#!%r)N6G;&mp}}D+#?}uq{SMdP{Hbch4w&R!t2JF$yk3vD!Rm zjO1^t7qZkP;uCFd?(XpLZV?=4vA32z%lq#1B=D}J7Z#1b@w5H{^x5MKX*q6xE>DvE z7Hhh)=j)5Pkl&Pz_WmqUdRTsTuv*@%K42c!ZKm=>YwSEa2@90RC{gO4FVMm_LceKSPKQ8l>Lg*(FX2U6XLj zfG1)xUBf3zar?7nXs#8AC=r#B{Kx>7%|9Oqp$j_iiH4w9Ba~%`Ffzh&Nxr7U8Yb*C zzacAoBzJGz`@BW`U%A= z4H@pA-ymyMI{+Gmd%mJUH-gUvd5{L*ml#^~?Fh3-iJSEVwjVZ-3A=+A^IOf2* zJ;t~tK?`g^4;*=mojLv&iVU5zu?eZH^Qcw(EmqrRwR%rdSL}OLp>SM*3l_- zH)Vrn<@}KlS{{&MevUIwt^5S+NRKaKGk6L{+^Ff4+-)hse&yh>HsM9MfaUNRlwF2_ z1+rA7?K9eg^;4ilS7HQ9%pIjGned5m8@rPPgGrpTe}dLrG}5tvA&wo49qB|^p}5b1 z!NaTE=-5UuVi2uxNt{Bmh(-n~F!ss%k7ybjt1;GHUS4jn)hF?*tEkYEa|s9#4cKf> zN9J$Pe}6+Yg5ZNd3TqV=;!q8Rg=z~nE@D-&l>D+vLk_QYu$#iOFEgq3jnCIR^UX50-_tWAu-H4QtdlH>gh~3ZSB!8fsF(mL-PA~-J1nKs4 zbxK5gEj&Zg_iVYwY40{qvS4Qs;ivy&lCEp@o6&UhKTMh&Zw`03z;D{D?IGVjyod`y zrflAr*wLwAoX`t>no^Mar5Xl*Gxai~rT?Gic!Vdjk@^w%2ZRg=zH4KB1uHXb~8VM=!I%nW30-}$J@8V76Y`Y4$OsOfviLFCkv`%oa} zo3dXmLum_;pz77g{2f>bdxV(x(_&AY2zD0~VX=h+i01?0z&&&eU2wQWUQ3J6@xz+K z#{BqtAz3!0b}Y+@Ee3KjS#pN&#oxUbFDKsTq)DW}(dyafoF+7jd{By_cO?bxqYlw+ z;dfFTqjwTU;Bwvn8HcJUn@G#in}o25k;wA!@>!Z-xS{qf!7o52?itdcQ4ve9yQz>s zp)t9$28yG&6-X+>-GdrDrXQu4$bwihK{i;{43dnTi=Wd|#O4lSv+*Y6{A1Yoz^!cUis%TeUC(?@`dt5rK+Xx zO^^hA2r$g(xv%e)7|02+F}g$M^B(UwvTUzbA7k8m=4%ibYfuSYIqI>jyn4e}nBAK) z)|f+I1(oQ1d~@E3N;N@~6;l(O%cKVn$B7XGJBjgoNzb7(SjD)_HmIR65dgYk4zcw@ zgP%?VoDiS6>Hqm6hLlIL(8`hCRgS&hCr3Gwp56yc@^}U7P-=eQS2CxWeux;6la&<= z!KkKa6K#he7M3ZoHOJ&m(}jvQ3Y(WZQ&pz?EF`0Z)-FSJR)kTgc@W7y37=zfufT$T z*c}@PbkW+@7O%i4hEoGcK(p1LOo>Eu+gysxUr!qJUSc#Sn5)H>Nl^$4ke8P?G+em& zGvqCim&dySshW2ZbzK^n_9706WOdlOFfp0NdFvCykTw!z$gMyBx#^+#Drn^|{MyIq zP8RsLIrhT}vy7`QooGu^&dgrz&%N5g1&yOXBi~UC-_g8Tb(Evv=#ROnR_9%Xcb*HY zt%WT+KU#NwTzjYeZ4GDXJk9GoWwf}%v$*qV*9$!(?K~qjvYbd=I#UOL>@+paG|gQT z3k!~dT}L6#&)P`;@JwD*%4nL}!rjKI4I5?`F0+KC>}aY(N8GG1n{yj8c70^JimEg~ zBtG6gh1y)%>Y?BJXH|;j5ZXhTsP;BL-P}oNUmdMu$pKED2!!-XFgwnnC8|~H9MG3i zniS1D3K@g)z?JAu&S0k-&T*2X*+p#>tb*b{aY~h7Cw*u{H#;#(Nj?nB-ot65C<^(Z zZN)_plQy~;$ncdV1)=bbDIh6}b0^UaD~->w!!qzlpE)Sq*To%C9kMw;8!x=M5ReFlKQm z%I^ug9hFHYSe9|XMa=4l*f?Mn8%c}oupn0O%e^?DatH}JE`>C|&f5LJgc*>}+-pnO zUF$d&(Bg{KT~P`E*Yx3>Iv~Rwgn|i>Knfq(Lr8&ul6_LluDJS03ytg^X>nFQ77=z~ zSEBC1FnB5xM^t(oIhe-|;(t){_u&0siv9~!C{A7Xd5Rc*E^THGFf$F9lSi4BPLw!p zU=v`a;P^!1wWC!~3H^3XUp@6k3#ZBzWwn)dKad-6>^ZmIbI#w5UixT`eceHevE*uo z->QhQpaFmN`w~YH8h;MrRKP~J-|UI9`Ofe~WK_m8;{&VT5IZ+Xayp5!-Ncuu8rEnl z%hX6dV52l;d~Kd?&%C;FDT~b*^fMcOS~4i1X6iG<0c2^XW9jowm6q}?#K=@n4{TvE zVpYIV^CgGMU&<#ZzojUg4cO%E?X6O5g&H@KS}})3oe^o7u>@`S5)jDkPyr&zn0qZC zzygZDlvZdMJl)_A7dv-^`jy$|uO}$uIVPY*YBK3q2C$Ge#Yl?L_#JYoS343kIaYYp zGxgP?Tj~^HTnIsCrB2DA?+oO^OMbyJZ?j0A!E?}Wt~SYN5G@pV;kyt7L0nHAHp7uS z{wW({*g{}sC?f451RS1#Q}fS%c7Sn^V++{^`-at&s3)Ly477OeX~qsRl8+w5^wI_2 zTmCB1$hp`)jj%ka9Y4nFFhmHG|0Oepir?Q$TV(isP%s|wdgqG0paqVNjU68sKb^2# zTwJ_g{2s%@Qje3Xnw_i2oLyPp+7w>B>si57BfRb!x9Q|>|eMd5nw@yF4>6=L|eK`5Pb%=Q|N1VzvBw0;O)}1vO{>ySsj^uq+p^ri}C2=PI zajQ`=xKcQVR_9o?bdK27)|nR~Z;M>Tb$onQb89lz!CEv|CVJ$}+5vQCPB99|)2SAv zqza-)5N@Gi4WItbCX&@70jG6FcA8GzQ8b9(9v3>qRSKVW;OmZ)t+o%~2is%f7}NYp z>w%=tl?;;Lgz{O*^~_2*b2knG{}vyff;rmSNHOdfQX}o8>QX?A{27V%4Eb}P{1B_c z2|GEy9yd-SXAw|$Xc#Kx$xlVd)$GSB036c)!?BPbLlY6|Cq*(d7sd}J7Ty734Of&a zPD9*ldK+*J#Z4b5C@45MI2^2C&0>!bIb^sBJJ9Z}Y=mVB1VqG!8b(U+DX$h?Uq>Df zv+wf!ygBq$IbZkXWg`+uu6l}xS^-M&9UYAkf_C$UMf(Yn@f!XxmBl$i0Q$_SS!2J} zsyh2~=Sw!1x{g1-c(jXw(0`{N0)0=HI;s$2Ss=wBs zF5FSyKBd=Ub6*fch8ghyz^P2#sZ)TXNuC)@7Qhh?GNcK8Na-O8ee46cYAJuJT%yg^ zf#^;ZP{~{=_L_8ZgEmcxwL1<^0Wd0p1Vms5OTqxsZPov&9OhN5;ve*KOn5Fb=#gEH zsxPi~$a1G-P@5b(4c30$d@!LAKaNy7xHx4%B5U{#!z_~Z_mqKY#7A{2_9xyI;>t;0=S zgLtn<3nP_jQZJ(X;0&K8Jrs@|{MQxWf~1#*cJh4X9}vLaQ%yC`LHu@pX~gT=20 zFMNJOfs95=*&{Y!5K)|g>1Px07YiM8h=GyrTRyP7x1Kdkbf5D(y9?tbWRkQwMv}{c zEfGwtQ6er9vkKR2rK+g|eWmb0=NXf#!8LQwk6qsGa9@WqHyb~wmzTSSLWqy=fkqpfpa|!#7$|7l|UZ@Pd!^t>w$~g<_5Ltk?FS(Fao%xJ)s&z=QBEYGiqdTnRz^zz^5f|NovpeN!^sH#ePbxg>2beu9M7`?QI=Q&A(ay3CilKXlw_$B|m zSN0+@d?r5HX7T6S=r&$Dofev#wL0I2AA}vtby_FE>N~eZTbDWmJ505R-}BI%w)>U6 z3fk2E9V!2G75!zBZtLsoe(}KXm_7lg4t*^}z1RI`>p_tK5J6F4b7Res=9|#N-|-b#pKYF>EhL@BSQY>vsvODTAsGRp83tjv4ANC{g6dL(R2 zQ>#=t?=b5q6h(+Dr_ad|Vv9ZCVFOXXur9#dJz*Zjt0PE5zNjoDllJ7GiZ-&ffaV*> zSR@C=)-X3lZHL68zC2KYUl&$7#x{TIR+b0>G(?IArb#^8XzFrHnC88wXBH5F+dqSnuXHHM3sSKu!|iZQlgb zV00XGV}Q99MiEH7f)EBzTXv?1geZo=sSoBppK}TPr;?!~{IZ__T+)`h1Yj@`R_{;E zPd}A#uq|P^`tCRkMj}I24y3ORB1~*c73^7*ZCMpD*wQ79Vz~K3Cv2@vnvS9I+T80$Vy4(vAfm#Z z?z-a(BSHqBuaR~Wa(vjzc7&;ls5YCJ8)<(C2h~(_>5bl{sUVieUwL0Cu_?OW4f79= z{L*OD-guMZj#VfD zDUFABgzAMv8M9b6eCnIxBAzBDe99Hdl6`%A-wHdnXf7nGWEj~uPzD5QJb%atMLODm zP7-*Woon;LaPS~#597}t1`#==6_{%CVOC9eb<5R18mG%gdv=1g#*qCyE*uRDS|^9IFw37ym`E)r3_m_)1x244LT#3*GVKYBdt zvWQSrA_*Io#G+vvTyFOlq|i~Z8$e`}`=hk4gP+E-1@|Vukzqxibw2W39}(B2BEHhn z^mKLedB1)AikBA2q@NVdLS?W7g1Z zt>&?dL`}{IE8o%B)%LVDR5stHZ_C_L4Q)r)w3v-gW99=(AAWj1rtjhVovq-e<1eBd zt+m~j@lYSjeAMj? zexe7>G2KH(PJzOrSS=cbz#m!b%S-d~26SDyHA2NYg~=nWq*&={#X47YhWzc0-T`^$ zNk~9D2x}28R5<%F0QB53|I$etQ3KnRsjFEiYocTiL1hJuIt%pBDO4`0ucS9+Y>poC zX9p(qm(YgQ@bx+7qQKQj`s932AR+;X+~2W^5H?LYOVWYJo)ge%9aX4-Uo_wT2&sfC-ELxd?$#Xvx!2*mX3orZ)pipbuebaN(|-j69lb}qD0z+R-LtSZO^4RNJ$ zP58{rR}R9$n}F9(gYrZXs0;ka5Q=Q~gQ1}zWo5zsKJp9@6?M zW_Oju_K8k9H~|U^S5A6!6|x0K@Vl14yT%hq5x1p>zQM$!`RdXzIIxHwR^9dW?(d$( zn=1vo-he-eqWSLvF9yg8Hp6|U2%)l0Z;dT0R1p=7jPT)ASz0x|?uVxJ=i4SkUT;GP z(ZYgbbQB`Z)2ZUW*ZPbMxjRc-RRmOiv>8T7VjX2VnntGD8V_j`1}ccQG)jZ@J?}Wr zh}QU|+OTH4>Nfy{9hpm@pK3BxksqWSAWRr6%+#$GkB^8h`MQ6qCk*%FaB)#&LqW$5 z3bk-cX-QIEy}~-(LV_l>Ps|L42tvoYh-9`}TfkM#hh=6~SHMO6&ig9$E-~JGf!ELM z^_f5J(w{?dc@-!G`LVCCoNy+YB3E8$S zNr40Gc*cq-*+AP!N8d~l3Kp;HOpUYzMw_jVxj4AIBRLm8aT#Z1sIIKj;8bLOJI6ie zxE-KSJ8PFV=s0}?x#^HbbGP6H-5@OC9IPfwpEaDQfXkp%jX_`K_mm`hq-nuPUbvJi zE?#@Zh6vc*Xonh1QCfh*ZDea|qa3A(YKiGikl{jNS)|a<`U_gV9h6vWRt?NX5Y-yO zQIZhFj+k?|5}n?2tP#@gpQV6#Z>nzNzbB$suV422n${a%!PaLc5zWqLgSeFm`sRjG zjFgHYOeR=44)!*dwe0%%ry4{Gg@diYH-F8sM#B%x-dZKJ{t$tnc%P~{yb=bDh5qzI zZnZ&(#n|tBgHS3t8O74u%d7qIwC&*Fz^07Q(qsxr#`|Fmv000{h{IQqL^ST^Pn4yk zq*d3Sex_H_Lu~Bqc#LZ6YiceA6LV;35q-`olb2B0p5*l~R4!Fjj**e_PaHNBl$3a@ ze*5k6gme}by_g6fKJm0m*IW>M;c2cV7tHNF;Y8tSKKkV_W402%ePL`i>1-@ zZ(XL3e@9`w=IcdwOI@#vAtYy*eQg%)+ELM)WLkzIr{R zYTTk@@c9rw(V}zte5caeqTTprfXnt_sDYNN1CMTz#+xfkdXKBd7+%ykI`{me-!YB2T&!hYuy7F`0Ymh#{?#p8!_p1&>J#b>CAt9ta*V{%rPb+UJ-(Hqqty2Tc&tsKN z0xO;&cZ$Ds<)7butDa$=*0L~sX$|9W5EW(!VW}tA_^biV;5FzUB6|f&TcaN{9zTbP zUe=vhQ!LnEB~Y4$v)!a1q5oVs5SI9mNM>zd$EtP*I~bJ5uoW=MR0w0NDuZ(MS70>B zT^J4+s-Pxt8Lm*g{gZ8_Qy}>#n=6u?vQoyC-&)l)FQ3(CS}@a-AiE;LG)4!Q6dg^V z3%~}u$f?2+yMXYOl&U}455K`XhDZ>iy8^4qL4gYh2&AT^{;~SQ$i&pw+l%}Pd5T`v zBrBaa7s-u|SBqB*|EcgzGu*8(pOZ)v_4l*80wM!P%CgV^fV2*)B1yU#Wg+HkF_t|n zcwx>>Wt-6?+(nS=>gr4vWRR5K)U9J5SHEk&>uDyhS;_7IV;?4FpG2%SDd^%~iq_YV zYdwe9poi^?Uu;Qv5`X`U5G!j@zxH?)t>xYlqw>B8ism{Tb+n0WVsxSs_oCwoCI^6zX}zx<6HS@(cxg8BBc3h zqhEmeyiG~#S4HyYO-Ae1ha!%}l(y0Bbq2HZ{cb;Y;6JGx<0?iy)Uao;YKc8(tOZk< z(*DR17ry$#NJt)sbrXw&aFPC49Ud56aq-QhbO)IP!lck*#{cZ+6nk$%zk5OrL0=(X zMxu=^3zPQF{@C{L*!SUt?=d)IRWn4}Y#s zj-CLqT3Yu%yh7rjs+S17!nz;RxDynmP9SDG?rxk2d?UN(9Ist9DidK}fPjeKBIWA| zCVN#7w(}!?U=R?su51{@3F5K~vq4SsuRZrV^FdGzFyj`&YUYw-nw)5=#i)4P$KX(C zHLT1je3cc2@YdSq#@71I#`c2F z+7yFv*9wdV|PiM<0=Uv?P+cS9w5ekE3*LSdBemTyQFj%aJ`K&@8>r zk7$$$Q~g&sLW+-SDQ!`pxcHQVN+4g?yVW*%L@f~svOe+e{?8C%t^E5a_g`JaR7dp6 zh9T(21`rf}cKGgsG(e@bBdrj)^b$L`eMO*Qgo(15+3dBHC>a zxS{-nf@FqPT3X6$+MGP%9HJ?D3SYmz43p`kvzfQ~d1&z>$TrNj`<%J8{*hxdJTrCeBmd1a*{}7F41u@U{&nAYzdV~RMNg)G zyf(d2o}Bx59XLPK<@RuYcPu{XDdQh{H4<+K48|8uI~Y3&`&^0L<@R#Sa$3qOOMA=4 zD>UZc89Z?%O!u#I>(6>-(Dh>>{>N|sHnGL&I zMe-;>H=!1_j!U)YO%;B_mog*%uR4`x-VMQSD#}*bR^jN6YG156(AnzVVThz(Dz_Og z=2bzF;mZgDDexmBzb9O+SZxmX=Fm$*_SP{<^jE&9z=y9!dECyE=6&Z@6gNOUlg)l= zNM*dl_%m3CA6air1Ffo@MB&r@XhaH>Ii`Yd5(cdAR07?x%Ve| z3|vaHz+K0|3YZUM;SfjI94E@K16*0DSm#*vbEAMBWb4~R{UY#onhF!MrgjKI7&W() z+?-GYDnN2^RDt|_1Hj&_l8dN4Jle{T`ePolre#Fc7DLVSDv6HyXXh}4#BAh`ff9Ew zSBwe6=bN!&Y8krmFAeOqIH;ECkvvd7U<6QDM1YENI1rPIi|ei0+p8;V0YM!UATorI zu%MtIbYCvlk2Ig{>kt;t;8DKQeO?#@zV)8?ha2y3EG7xofLtiV4+C-O+gnxu_7Cd{ zj1_`=#R&IUVq#fMs%bgN$fRTpNyTvV>FblzOoUy<=xyB!-Z_j|oqojRHE)kWBl;>G zNZD0L@Ct@$ExUD~w^*w6RUB33{qF3=uTs7~iQw!0n!cf6DZgz#A^gDGvr;8O*VV@N zzjkk^2)d1Htaega-WF74zy6nlD7IrQ7yVayU-ez@LprJMc)A>&8k6lqulf&lf_ptl z-?@~RWFmU*zZ6ahFWqeay`25KZA1817G(HZ@X;T9`;UTWmFc5Ex25LOVI)fEvqYpO zJsJdY#H88x-`Yu_uM$BwLau8j0_aJ=$g(5lW7BR&Z4F!(Ue%ut7$$h$;bF=k*`bVq!SKV>N03cp3 z)?2MNtT^$#5Ruf#M0Zjjlu$59lNv@{5tkSzx>sc3kBvowR;+7`n=Xo=qk!bIIRLe{ zH@33bUqq;X^>u+SuGIr5U-@g;wX~Y)TkmMO4~og6y_*=kD#vg#6m#)QzVR==j5DFH zq<%+A8&x^7!D-Np50v+1e6JlFLB9d=*R=A{s_WB7*ndtw!NsX(*r;|CqBK5<^DlbT zls0;9Mn4pdXxYBk!;g$X z7&ik)BRtYv;lCpswjIEoH{S-)-~nf=6!}Q=Bq7E50I)_dEh=1-_TT%xPcD^)#347+ zzumfSmg!#3x8gCNDfZm^u0-+!aRjWx#lu&i zPSO%$L%$hI%(lF|4E3GzTyG;|fW1P;qm}R?e+45h)%<4G>1e;6@Sk4$nRW#9ji3B1 ze(NCgYiHCI1u85gl%EBfwYWRK{tn2X#0DQXmL?Z>eax?}?rLh{_1Z7UOj=*_Ax46I zeCAz&t~cYGtKM&<_b*j5PX>#-+>Rd9S^RuCFMh(eV1L;EdztjDI_oNEJa;eqTYLAz zc4Uc`IrQTL<6d1RMu+aQknQtfA?nJcMU?aLq`Bk$wc>6m?|jgu+yEQz=dn-;i{XF! z1G!1&6Zhvkv-lROfA{CU<6CItK0B`N=+*tZIAaBX`Q3zybsLU7=6<{hPP-;Fl=-S( zZsbvO2=!Ce_rSLq;O|RY;)@P?h6(?gT(_eL1(N&<}x}0ynWha-Guh3 z+{My)iE(l2$x%?9$b)fPY12A~Y`R@}O~=nG7ruBqrhUq6`m#MMx)xxk(Fq@NJUK}x zskTS)ctm@6uu(jsKw9bP>ZIY`iFw;N8L~BoB=3CMraVPGl9z zm~!T;ZKrUmT~3hrfF9RfA#aWL7sHl+I!iWqf-}lD@q{#*GI*Xd$}kaZ_-6!?KEfuC zL8?})&XcLaOl%3pX$lhRFhIA2U>KWm5b4Qd5H`kDvPEFeQzY20U=P3PU0xiRG@Td- zM{#NXi3Yzn(DCzQ_|(6JeeTdV>Mxue!y4%3B74>_CN%^hFdtK@YZ1)e7e<qT zMc^O=R!0O&7Z(?Z($A))B_aY3wgKdqvMp*>|0rOWXQsy5!8=OE%K6aV;kGFTB2tMA zYs%Hw!fOph0)Ah#YH=-`6hpn;-bqveaz_44nW{Q+r`z4_q^x(HT9n1caAB-ZopO~6j0@O<=)Y1&93}!C4t8rXyVt$ZLUGOA3%+a1G!MAA zS$H|0`0Y0S*Wp(5YWIg}qu)`9w=&+bPn)|VGSuLCOdK9;6lGhzZ9jiSRuP?a3^;m0 z$gc8fq5n!?^h4qYaN+>{&@U(^Govedb?DF!JHfX{7gDDzf6GyqVr@t))l5~a)U^?k zOPNQuOGR@?j5@dym(TUM(E}k0wxxQ#j{(=^>+Z$jBH>50f85!D190aOcTz@h?B)UH zF!2f}4|BGhVJK#b{;WX)u302{K-9xJ&P5yTb|ClEUR+mZ+qXSi3;c*5?bR0Ntv+jM z`ITJu&MSu`mpgh3c0xAp3dU(4b|3WNw#vq2e3Nn)M&>d0W{nq!S~A9DVOX8(Dh49l zMEc4QYF>~wNWN_Mu_=*XL9a_$PYPSo>x*h@-#jFuBbn_Abc}J1vKTkS7vJwmn2=7C z^T~dpa5rI+9?3|xg`G7%88#c%E<~M?Ze*HeV4SOG{>8*7!@|r6Vq%uS4_4H(7S1&L zy*x&8PWc8hf2~9W*fA883PGADM}qX> zvm${4i>?iMuq>p}po)HL?ZKD*HF=VaRYo0`ZEjuJR~O24c0I%kbQ|xnC9-cng1&!i zaF~9mB=I(s#7N-;DU^OypHj=;;6i8ciHwwkJLAOXa~1A62duF=c*f+np$Cv6{_fOK zb=S)vl^a16YJmN+6P7Cb{O8yj#96i@jsgh1Kw)x(RfOAt9ha}J%(<(h*$H5 zZgxR`?G7{Dii7@ww=&)MtH0$uzy5cHz>_w2I|4VAE1W>HGWHX{S>_KUI)%aZTJv)_ zwrSS|Zc*na(&CTusGt$1M(6gdU%!@Sn3Zti;{o8noQUj@-EONQh6qO-c)fA(gkzLe zM<@V9hXX$pX6+b+?d?@79Ra1K$ep;+Ow~~z=S3b-1e?|sPugrQ0)WC11g}8A5<}MTf*x!n5Vdnd z+@_JW_PgtLb)hWns_8WGHb)@|;V;)}l}dooI;E=eTN{|VTSHfvHfNf*k9X`a%87&H ztq&vQjKF7hh}*WA%*qPY1bdbI?VrQ*s87hEIPl96=Ti)53j=}-6SrQCm|zrDp6`ve z%(h`jN*rzhU;i=Jj*c-jr}~N)AI%!9JPk3$i~cn`+uv)ASy@q`rb7Uhp;XMurKX~y zLZtg7B->|aA4eB5u_7<8ubcg^(2yYu>^_kt1s-i%!I3DIvb6n`?ii&0;ta6gaRC7Y zC`u9Vbab^)PQs4X_cSlcWf`T>@+ujLL7PaeHiuk@-2ct0lZYe$(YG8S=uImP3_iVQc`%BJ*>X} z+-3~E#i#>Iqp@D1Ar>JFCMDa99>muLa3up^Hs~SgaY$feDJxx(1jSHJ^k|)_xGnIk zL(lj9{C;-H)>g$S0~WJvmWG~-n-zsgcZPM76EI}uMF*#nLem<_av6oL4 zwlOp$#F5=V&`DI%Voyz$&j8_n=UAeQVr-V68AX_b2bte&Lwi2%WAgsPLxZa&DGKLq`3wNwszznYnT)i|EM0(G_Sw}0vG=23Y$&D}Gio$d zzbAgE;qopxhwuv?4iJ#w>jU&+K!uAN79d;!%yL?gq4(puo3Y~~pvp>6tiYyMuh43+ z1msi3JlhB3>0i-sfa-yz8l#W?A6yfE{qi`vs_B!CA$`5xtJwv@!EPsG!A`hqTz34; zYvfv%txcB#C&&A4yG4@?r@9)|9^X3-;72RY)&PAnH&-qBYf)%y@ggJXrk}QtISNBK zVo%W&_$X&<$gkV}-4AW(Y?tup9CPb@7j=U9GvD>vyJZag*SCw^#7klMm$%)hOW_F^ z-$#QtH&PtN44w9)*Bi-84Mxu{pJUjuLQ?QApQ-vg$cNhHEqeOo<=BUjvi)&-;m%ge z79#|+XX%W6QZB|9u)(sm{rS&jwMgO5dVfWS^H3vjd{W+DrFl1f6Fcy3Opgn1P#;W;IigkF)|OAyx)x)3 z4;AGdRs*Gu-DzcMceo;^s4j>&!o9q-lfQsm&+%b)wV-{VbG61tbqDk-Mr>yZkvzpa z9@)!l`BrKdSOhfr8osWkHAQS|uiDHw6LQj>{aRB)%C-#d)UHx-aMbURx9LR(Ss_Yz zcL=_|{+zjw+WOVAott|xgB+E!5Z{v<@87fSFRJ^&sIT?@6p`xHl>Q^AGs?vS6CGq~ z6v!M{LsQ&Wz@1Y<4`bv)qgTC9Us!GEK6lhQzw#?e4QX=4Hc6^*N1URsPAV8E10_KU zk#=d4QQPkU#c$*((w)yXMe+}b<;L05-~AG~%Myv;ZaCC4W%<>=(5q!LhOxB%GlgJi zHp>maaDmJ^3H5p93lt9BsycQ@nCCOu2o|j5=?)HimM9+5II^B|@b%dI&no#RW+Mx+ z4m3z-~Z20LZh0@;%l{Q^8lOP#wTLzR~RQ zN&w&6YXG{q05|)*w!)HN90{@_p)I3;!_FXKXhrs;)q%`}UEUT09nI+MDCVNySu`>} zd|#_8^7#WDLzRvsByM#AkVNUdZO-QfPsH(pNg%<17xJOP2uM8>cCWONc7@shmVjCy zAo2&?=$T+3Mgr8?zn;}i3co!WnJ1Z!i`_PhW5SH%bJ$PKh~C1=k=I8Rv=pq!1Ito5 z%ogb(NT@X)=Ju<FS1@q$0w=jQrxb3(hr@%Uk!&_|&Dab#dtw39o*NIcTX z8XW|f^+6QyfAKW9pSIrqi>I*xV-)2A`}}nm{O0&dm8QHmEExC&hQGVj>3!pO~p`$R4!nIyHU$G*1^S17BSpI6FOJZK6?k3QUV?7OtA2S_bmUww??IpwB4u1Oo=- zQP|s|Pf96v-<}vjro_vteArL+mF&kx7`n``e`Ko(qKhL@B=a@tj&4 zGbO*ZpiQ8rnKcVydnG)3~Nh+sBRIVTc;`rNzTn~eQ0gk`s zo=pgz1$kL8k^d5o3E@U9w2cz0rfbMPAc-*uaZjr>78nQ0Fo5iEwa~Z7QQ%t!@CKIg z;g?h4jP^~b_W^|xp1KyRAt=>kU>UoRS8d;eGYQ&dUUF;&1E;P%Ty5B*%<^Ea-qGrr z@jgJk$nUrHE@%C9jmI}pE5WWplyMhHWIX4v)-pE{jRr~NhTsZ#%qU5`U)~JlBwxY; z!CL2{pmtA;AjzVhpKc9`v-%eO?j&xKzOj}idfa^0t%55~kFM_>ZQ`gGv4TJBVPOq; zKUSn(wspBHJy{ZTi=l(AM#=q>Ffoa@_Z@cUhIiZC&OuGC;{lglFL$#FTMB*YcAeKJ z^wlEC7{XSsAp%xPzDnlPdG}4^&8O%Aj^s#1-w`0o;g#AEBGKrj(LJFZB{Xt0Y#`y` zJ3MD;UVeZs4VNKs!u!(Le#Nm=Jl`H-hV1jyz0{hFo;iSw<=wmh(+)37hSm7Uj==kxF@e&-l$fVQhuwS+qo!%tS0qOli-1a&68CC)cS{epg8X86t z21@D)Hfa3@@%kG1;Q{{c>JEr_^gT?D(`z*va;C2%9fPrNMC3m^UJdc`^QP@k{$>i1 z?hg$a2o4$)c}+3S{SzJ;K|j&{q)_=)>A3vRtRgGB!P95r7iP6wnmc!iPP?)9ATH`${n`b?HbP60;8VMaX*{VO;0Y3H!!tsIwoUqF%Krovm-d*Qq#t)H6m@^_|Y z%(%f-`-Z#1=$bA)($|Tq0!{ve0GaV=>;naqBCi63F8(ApL2=D#*I3QPxgLw7Ee?eA zWeT6j=IBk@v$(K) zC&dz~^@e4$O!Pyf@yBh4V6j|9Rp;Oujv5G8viL;Ze6F9nil6o0GxkiVOgs#C)COz& zVOR&I^(O^LV*B-;?(u__jdAdELHr#A#?$OfZ2Q$;06$g2Go=c{sdeqCa5O(H3s|HZk66q z=8n5RR@LL4FO|!eU60RZ*$jU#(i_TuT`sj=FKv5kFlEBG27mY`B4uxtlvyd&(EPgu z?`(F1q-L#c*6s45`_*fToXF!!AIod(V3ljLrxVfVX^Uh(^zjoRYM9dnEw9-_Vy$n7 zk3DJBCx_qSa1KxlLpdHf8k<(?srPI;O(oiNaR@D9LXne|`&@~2mJybtPV^D%vH%vN z4#RQAkS!s92BU7T(=k!JslY<9h#*602Fx$HsLosoIy~S zL7}0{zPUuj*|j>=-U!z;_MG?zOBf6ihO-GFyE$TeD@mAHdv71ix~<3en+^{L!#%RJ z#}fv80drUoii)0`K0_O^-<;~|)cjYoEU^YaKp7pNY1Wfsmf7a9FAy_U+fzZ93}`kN zXV(``M;JK_7C3#gPa!(FX$JBBP^L&y)QJWD=nd%U+$Qf2vqV?1Mhs*m92T1f$+P?# zkD(f!01v5Nqae}4uvW}1jn(sDg(i{2;;RR+ha|<|tE)>|$eVcRx=8xis2O<6If(06 zP}WtH=4Q--hHc6$O*NYAyE!Yr8`n>SNGz!iTQHc_jigzVYgDxEgskL+q>0fuLO62j zt{|ne%=A)?hSb;-se3q4`cRS^f}UK0+W(+vqy17{tBAW&z2T~qF`sfxL$1}8(4S2c`{qO)RZIiF zTl%%Oj;`&hM3hR}6M9_`4PO8*0EG-+_)8EZxCE}^v4#i&=bxx_?kpL@k?Bvee`M#C zep!bjNmh$h0P$cQp#3yiFV}j%JOE+r^VO!yp?Jz4Lhk@7Y(M;cfbmldA<3} zb#{FHc7*ftl>3LBCycuXEMUR!QN!SnA`lQ_P~fBC;iD1Zqmfa<;Sr?3v}7zZ!encc z**r+!RlnC*j)(lh5!FY_(}`o}U|p8A*uwbnzRhp)a z|EyprVN9_H8yZZRoV1wbeePz1|J8$E{qPc`1IBDIUM#ECDW`L-EL*9owcV6C=uC=! z`Nd5Pnho-mF7N1lpt=>qa)gu(P*6gd+_oj9a zaP2O2)LOh;XkE9fI3?Q_e~~QrL_}&m{q0itP_a_BQt$W`+ILP^ZYzGyQLyITQ|vac z?wQ#pss(G6KxIXzpBQ26?2FyeXywlm(;_`o21 z7iT#8JIvTg={jZJp|?`}wG5A}8kY77{75SC2XzvxzHZs61u4@>jBtq^`4ZiaZ3)Bs zfJipH7(@KF%wmp!gG?nb7=x^X+8f$Pl3>+<&x41qBz_bqhelRYQ=_J)1`J5?Cu*93 zfo*OKijd%qNQ-m~L{LK1WAcyvqncevM)u=y{9Si=n=uw*yvi1KwfO+WcM_% z1Uv0(q$e}^kARFg2x!Y5d7C37h5leE)}7=}-`nlC|1LWP>1Ogc+&lq#zZl||>!sKw zu$Zy9=I`rM`0&`}qxpD14<_Wf*PRR01D|cyxm`}^v^Cjm)|iYgL=%1rL0f^q;ozRS z5Fvy*N3WNwGadK59p^qrLe%0jzt15q(31EYB?%<*X(`-bJq*bvVSU~2=bJWu+yeBi z=cB%5J@0Fv5^u9oug>wXQSNu_S(oRr^%Wy36u36wl>X*9gP^5ZbCa?5$OKaGWSV?8 zQ|WYlb&CA#H*+y*tFPVC0eZT$4M`x$?(0ia&E?XMM_L1y#$PQb)85_PO&y@pT$HT~ZA6-mtA?9QC-{BwU#xwLBj`jh%%e~cS9}o%I8DM|z zk0r;`qU;A5_7`uw|rUyq0bFmtzd^Pfh_g4=B&1(eSo|c8(p3v18K(}?$ zkv3u!NX9b|#RaqeGWx8Joi)|bSlQ4SxJgEVR9>g)Z`M0gCIMrUJn;Flp#`~)bWE2?emD}Ea28Kb zWpciK7A`cXSDv;nU~Ke3_0K0G@!H!t<>9#K7yt(ZkQe2Y!=>FQi%I)&lAZ#~fakZD z%dcn)K+^my!Jra|&#bq)7}HePtTvvnwK!dFbcCW1(=juLCIXhj)h7G-dxK77;!c5harPbRV1ZbiwZ6 zIyeqOwEU=`BxUcYc`oJYt~n^`sede}prRo0tRRu7y+ECeRfraot)E(iYj5ePKBbuh zw6IXa)Onl7B3!pO6!SBmeH7&vPEUpPJHlU3Jw|)!b5ap%S#UQtLLAH34_Zn2z~;w!B^~N>32Ik1FMll<%d6 zi>jQ9YJeoSp2bQV^o^wW>(tEJ#3AD9DtzD1VPhGSLfEvsh#pYtz~WIofgs>x(iL`U z=ZYW6lqJb>%rGTEwP;gZT=a&-BBaXC+ss*x!Fb0muWimk`eKEKhHO<+#&i<|3ny~< z6*A~e!W}%I-ru2sDmrMKVyAx3(WB2o5KWV=Ppo|Rr?EmUN^SYa zP?2(Z(ul1Rhy>~ACWeQ<0`$}`U&6w}Yoe)MM36d!Ey+kp}}_(+#mhn##dxMh~!yfk#Ndns8~S+IV2^w z2pg5@>8pjK$xMdME*F27+07KP`MhUhs(k)lA5BxLxL`pYgOPt&_e%Q+cs?D)i%X*z zdAjIN0(xEm=~d%&xB%e2>^A=Pp%N}uYIk|wp8z3A5hpPSpbihXy=2V;b69~wz$tyv zU1%G84$G--QCaB~p)%h7Sj~F#=~|!|kEUxL`Xa||BbD$3CZ#)2RN+J6B9d%odHD{#&ZD>9^y4n^r-9Uute$;0qv3@uErR9<}Ch z0((p1&8N`BI#rh#C_a>zM(F5`R4`T0j#hpiVHlQW9FiH6iyf5-msg1T5-uGTK6XqJ zx&toR4q9^k89cK~3E2&=^w}LjQpq4HI9jZ{D8<^*Pu<@kp?SWt1a0$+fIbVu6gx*Z zWq+)g1_V~CW7q!iftA-Pz+1tr(Qx(Tgw~hiHbfoG)5nP!l!-ufGY0X%0hEKNuSoPvtNsT2 zCe-^1rXcEIe3OCHed-uI`mRR0E)h9r-Mq!v!>t&Fsr z49m9B+Vat!Iq4-R56hmU7Ch^H$}FG^yL>^amOzHL!jUPs#uAFr&mDrPYbkyP#GSRW|4~`vnv0y;L)}Suu;T>Szm<9ylQH4a_ zEuRJntNXH%UUqTr4}*Lb{$b*Y%&iN_HScnU^9i<0nTx=p%Shg}%3Vdt7>BCc?Q)}b z21@2&r#=Gd>?87kvm*rKY)C}Uc(NNlF~H2beWd`O<-?uVwIpz3*sj!D0Ngu%*E2Ge z7ID&+`g$x^sPJIPfw7RHG%Zsb9dRAO@~mYzQbLfmI%RQ9X-hb=t$~vk@7mW8EUw~# zMlovJ)kc+SZpU544NaUYMV`kXhPjyPTDn>5xW?qiXVVo*?`k)xxcqc)^S@OPmpC2*GbW)44q0smUwZqD3=u0d+KvKLch`w_;~ zHGCjbEWSBkd})K++6vYUZe`_JAEG&W5*E?k87uquZqtqX5XinrQ35;FCZf;Quns=Y z(%f91E%hD*sN(IpYAnlY=;)1NSUX#i)S!FeIl;(|fahE>C{==BiBsPIAKW`p#gzJe zBYG-H9abY_+MF$&k*vG{b}kY}y^)kA;LGHiA$f`k$H~1XG6Iu0o-AFGsG9vAASmZa zsja>FE5MQKWoT&l&H0U6B&Hofb0bGcWl02c|IVx z6LNf-+7<^q2?03nM=aw006Fk-Ph98wmcKXb!{s`_T*wmScROF{G=i0^=CbYIgVxngOW`~{N8Rwf+>I6b^S4gnaPq}-c}9|Ov3j}A=rlYZwpvFB*LjqZmZpG=y{3qx zxFxq;rTgS!=QxqrG_znbO81g;shIF&5g=JKO$7-UXIV#%t6vQ%qTbjuyJnBS#?}cI zud7&+Whq;3#h#*HZS6+Y$At*w}1`Kygo&{dGIvrpjkZ*)1XZ60qOljrxYJrBqq%cnG=S{EBxa2C(*xoC_;g`A=d zz)fZTNfQ%7GrrMcE1&XXPt0t0!c)alN*pcTC{AcFi~1j^!HD60Dnz4-=Cdew1S6AA z0AU+@j|%so2y4F(=dd^ls~{Q2G!fe*5!Xyzk%hjkjG?%UXn7*HTkSJ(ODzZDccqWd z?DP@2Ipd07mH14wDUAKBleT6m=vW4|vF7vK)gdq?O|dx_k~vt>hb^T75z0My2mnM( zDb~4G(o#-BN;E>b=K5+r;SqO_4m-yZy8zTVOiTQB76A!#Y=AV0*ZqMz6;SQmzWUM(5#n4?W*M zv;xlFPj4?r&{Ge;8UJYhgG?rqNC(@0I=dEQu&m8dOJ(XU6M@oL=7iP~HJd`I6nQ8C zQvE|iqF!g%xf)~`{?IzIY(Y~F1Pp9RZM&y%xn?o}U;wEM>#cJ{siCH3v)+nL=PW>i zG<60QO$3L*ivY7qb z$6)=Gx%=l0;oQi{q8^C-nbLj!bh7%#`7^ff)9Z~!w)Vq_T{3ViHS??Kb>w^JRj{x2 z)!#0^rjGl7!;@fQVqy~$lVdK{T9uh@-qK@U3;b}}V29`PaO^(?f5`0(gULI=l#JyC zRNQS0+-(BPZ34ngc`RLJ&EVGhg2vjs&Z4w|3N!B7wDcAMT`Mmy!d}p4EZHgvWf%JS zb(v_HQYAVL$5bggB^MV8Tk12#`ley%kNDZl`?uX}B&b4ke0-{znOdo1C1;K1^&K$c zT?2eetNhk|p!~o-b6ysn8DY5aFx}LO4wwYBV(wB-K9eRbCJh-XHU~4aP(@&-nUFSm zb!TL(3=^s8$rzO4DzAZGn_61xwzM|a_fJJe-tvT{G5M{`+0X#g(7Ihsi9x^mqLAu3QW9fhW#Ab8%oCS}YtaJobsNw!$I+{7{X%Ni|O zy#vqMA5T+FP2MwhPEJlHCME#^iq9-VfUfrC$b#nDJJVY35JG&L~SK1CJYT-PUhexDqcqF}u>SeuNEq88+qtE~YJeg-S&P6e!oT_q;hG3hk~9 z-T721=ki`8+X`Ys0(#fW_AnMedeIz^|1 z2U~V0X-aLp{kN(Dlw#-!u?ObFXqlnjx=|dXE z6Blc+)<>L#Re&M<>9GkvEE9#8&w@j)31=!<1Q0U->_Pa%hvGrwq5((9^pbB;f7|ihW^yZY4x-AJ&K+0pHJ7o@c3r*6R^dJ zSbi$9h_fTzc#hQl`s(8NO*MJJD&xN)H1i&cn9wyA22@5?=hi7Qr3|M= z$EA`6DgbZW%PXga>=C!6#S=E}B@VWzE!!PUfae1V!AI19(D2Yul3j8zV_kk|rl*#} zC)Xk2LV0KFCHst_SJytbv-6r1Iha7*{5V-?cbLb#-uAtMryP+k`io>=0Z;ee4>qTZ zl{&S?*MQ;gbHa6QH=UGvw>0-%%X^EP`j0yDJiPU}82+&@E+^>;KHy+LX{xIZk%08* zniufU(MTA~`y^5BPE>#x3{hV-8c~|Gq&~VIv||PgkfQj4_AAps)D4HP0~SaixVS`n zbDcl9a`d5n2@94E4j0Yja*_LGTs!qwnrry;@TUT@-k^hIl_uFzA5Fjl(eQ@FV-bsI z5Mibh5oTj#$6;ir{ESOUOG*5dl9-j4n3|ZFl#!B@n39>1n4b1C`Bz$Ys!~d(PDZw# zma3|*y1I^g`kon^JGM*nFaN)PEMcF z8BICEHKY48qpiO>MP__Dvcv{k%`Yx4&dsT-sr4-~RhKZ(Gcz;N zGt)BC)6z0B(b6(8(SF3mHi8R|62X#E^1zea-#ef<+%q;YGcz?Y0komU#svn21x0>< zqJEo-26#|W^&1TUk0mTZLPSz>bT$#w&(QbXopj}8Dc@*9M}vXRCsI$>oaP%|fz!rP zH5yj}e%IJy8ezKkxrtdMK3>Y%OAI%hYJ~PCVl-F89=hh4v-( z0tN=#K0ZDqBqSFX7r4y20sy!EcovYW@c6?V6Te;$p-07l?fquoXalq(E8@0_|M7r@ zek=*4T}o33@XoWYTAw}z`~Sn!C%(yFCl>nl5d39+qa#Qeu&+iy$)7#D9T1Hh#c8hQ zwi8?7NzkX?%V`H-`dg%20BJe|`KU6{3K)|>P`Yfs9BAzVkNg_|p5+qzFF>teVR%6^ z#$@V>_Cd)d5$lWQ!^jcVJFC8!QT`LlaHMuDjyr^G4&QPG7NAB%{+w>dJHe-mRVytv zv(w^U>l4{Gx2*((7uiWfI%&zh6+>1gNs(HoR5sBFtQw5_7T7ke7p@&L=8BO>1lJsH ze9ef~b23p{rAn~BQsee%1Z#SOuhlcxzGxownxF1@CMK*4j;pXO2~wwzhu~K@ZqqL3 zWYNB)8Siqex+1HUj!S_D4QB$z_48qn(vbj>4dbk{TPYSrVZGeKZ~|QVfQ=j+P?3DS zG(TPuQPesUa?;xx(GJA3Bl4nt3WQ#j?+tNa`AANeA1QJaF$;u^NxDFe7sUJ^CiIqg z=)y-pxq?HyLPESlLcJ41`6w=0FNCkEgIW**WvFNpS#{F_7}!B-ySo*k{xX58Ea~c7 zam+6U5|W1b)?3FT1DO36xG@Sh&qlTiI~Gs~FVbzynGG+WpLXzfD7O%cKmB;SGuTtX z6f%4K8tSq~dDv3|_jDA@zD3%DJ4wQHDXr2MV{wL%- z6xZZtNt$i?ec}__8g<5Li+z~P8MsH>HJPKvnbdL5$Ynp9dQj1lF?C~_XT?Dli7a#T z;>zM8@ngpfdP-&&4ymA!)0iv#+XxdjA zi}3jHz~g%{_U7%yZS%)qzAOn5mnnt+v0Ono_^>M0bn@*3Niz{?Q;F~=VC(|5ty(Z> zI|{;TVH(T5X+?0bymCj7+Gp5OARs_RhOsOGgDQ52eg}@ayO6#CWh$S@&`^Da#xOTYq=$Uo+VmxwKZLh_82!d(K3lPrP& zDj53Jo2>~ot!9O=OLctl5hpEnWpY#a16COJH(DwxDl9;<8x#@}5**w!!}NqFB!><% zrk)AUJB!FdgO&<2G%_*}1%j(X>^gC_)sfTWXiIAg(^6~l4AyqIF3w^4%NtA=M`&*K zZV17KxnLEl6O2e|olIqkQndUNb-H;=&eX19V>V3&94{<%fL!{jU*0fjc+8pVi5x=G z(Ni|B$w3gQWu)XkKV%uLmVZ8v{|9LhBEe(Ikoe>rK%L_e6A=*-wF(M2dKurvjGHrT zU%?f`q9CC>q9TdCnv>-Y8?xZlYZ0eW%M>cpB{{Yb#VAG`ntXDvEk{JAlh{Sz=Z_!Q z`ikLyO$3sd`v&4dmq5U%-*!XGQwnvBcL+_Oaf1~_u-?5z*9}IP28&|26wc#Ef8Hyr z?((&_w>LL;cZV8C$e%i+-Cp^un;v9Ei5dvdFMy6-FrhFoFu6P#)^+%uZRkE9Tf`vR z{KSLT9h=t%2L}O^5SEO3pcId~p&H;|8zu3U%mtT>g9@PRQ;OEaRl->d%>jvBLyj12$&iR5>B45Mz{GX+5 zg>=CM+8c$5;rq(*6|$et&;0ZnW#?#cZY0kIo^n^A8PES7aFJ+Cjcf;MFEJsd+rwB7 zF_`ZoC;}5@@R*fB6BoC?c5^f%5l%CH`lv100S7BB_d;E`=2mOb*D4#!5}O|w?8VFE z@dFhe%8Lq)kdF7x?5A1Xama)kV)&PZzve$wL2?6}A034?i70uLgy;!1$k6c}WwToJ zOpvV#Vupcp)^eqNZ~XR{QGRyF=%If)^i_HF4U!&M}6tzmhz}^&UaS>aXS8kO|lD@fjw2SiCBOREf_C5nGdr3oZK~_a4KhVaqb>x&x{Eeblrkn<7TYk#H3VK>-@LsK-h@s+w03z;R|Z8vCZ74r zibJXxr4jXJmXL>{>>~e;D@J1MrwNz;7QwS(aGEGXvP*mKH(X<&K;kXHhaiiU&B-&h zxVdN*%$_&<8M{`V=klXaXkUn#g8N-yF~9rgz(bZB)b58vrjtpS*9sub%ea`A7M@MM z*5TEjqf?8RMoTfH?Tkgx5dw`&5a!G%IyFue!1U=(Fg%AIp*OH5Co}>|=es#Z?xEGC zo;YmkU9!zBikf&RaqGD;2ObHY`eeUlHwC8kD)jmf#RR#zj8)M0^dZ@O5v-JMiXp>R zxM_Q=9^zPR&##$?=#Dp|5!VY=)k;3EE_>V??p~%{@IFrebV0cVS~lhRI{Lajz>?

    %`r7J3xKO#;?0Sw0EYtK_t=D@ctvX+9@7tg0 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%0DnT%59X;H)FR7UFo#2Mrtb1W4_6ERp3z;qQs( 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

  1. `U$Z~Da7A$w5Fn-Wb2|neaS(uu4l#=8CV70y8}we@b%Ri(1HwK?=ng|oY&Sd z&Y9b0dPi@y@r;umaUeCLT33yS^SJzd`#zzeym`F6U^OWW6SGwa{4yR=ifM;`(4H7b z^e*ccL@iAbt8~BdCO;t+nzGk8fC*xB$?WOLk^J+wdD~->e9AuY>b-5iJk9j0Xf9Fz z?vjO-|2F0n;nx=YrJ}MH&Je_5c7*QmunVy?k`R?PT9{vw&jd8LUM|WcYUewutO0}w zhp3(=$(g1QLd;bSGNx?phI6I`);VA2GuxQ+$y{9nJ4Ed`e!K~N30R|+H{F}I zuM%JwG_(mZtwZ~rKesrV9aKzv#=2Ct7k{_L$H(t(Hgy;tt{zSM=N)bKw7h&hw{R&b zb7ts{+vQG5flQsY<$xKFjgF%$o>M+BxJZ&>D2f>zDu>X3c;l3wufVdu$V&hdPXq|U+7ia*uD2Xc(O2<0YK zP2rz%6H~+vObyaUU_WEVT4voJF!KKF!`9R4Qw@fAdnOo;QOrw0L7BYcM}-=iBqV>N zLMccM#iPQ2*6bgP1C5`XpfYhlNy*CQF8JvDeq}6gC-k+*nqe-=zZzR%u*~IG+jVZ z53G@|eViU4A4p=x&%mi=^qi2S8-slEnQ!a|Gv4VxV%-lm(b=PTXzZTrF%^*!w2?BE zTCIW-(Sn4EsdzZ*-CDB^nH}fAVk#nj$+=`xz z>XuH*IeGW9F!)x-e2XY&eE(A-&DK&sL2@fwhSvZ2U{?K;0}#pr50r_zt_Wr4pIY z!t*1m%8M7EylvUx$fqu{IBh6F&*O0Uw&*b~{af~)$guyKk&(&>@saax_ou3=st7Z) zxX}UEC6VKE=9LvkI)9s$s*Rfwa>Th)DN1p@8T@BwCeHkGdU{>LFC(Puz0G|UYMK^d z8Eil@MYZSo>k7R+J+I^9h(}~(WEj!eg{lNEFW)x&a2&1yoYUj8e+pYe%wI(nr_hHw ze8lqqQt|5I95f;DK`S1D@$n_g*?Y~c>by*IWTflP8ykz?Y@hfpJVl}!h^az=i2hT& zEF?%w1YJ#w-omuoMwST>3!9sqv`9jrqS{AYYmAx^3+n3n$v1DPeh0~%cmpVNa;SK` z^J{b@Q)9sj(En-$3$Y%Q&?28bfFnni@evp#Rc8%E(6~R%N$}_jK%mz=z&>PyA@_O& zG39vLS3$9f50z$&c*vI2OH=!xkwD~eT5g&_t$L7g8x-%POHr~4FSk8r$5|QZv7g{> zFQOUsPEvS#wpAkwxIgiiR;hflfcX@B`B2m&cz_wKU+jpRs;M??Y)RQyakkxd4KiFk zTIXM-%xA$;=6jo(c5clUu_4)5t5vW9N=SMmr30a+TZdnR(><1Y%fn^>7+8dzFG<71 z2cJ%)Z8E`Z%9fC&=4c7`%u-)4rT+W7K3gt)p=J#+wqUcxhqGmRXYo=S9&xNc@d#QW ztkBTa)ddRpMn*<-Ls8SaOMqh*63S5&{>o?bYS%d~jy%Wgc(WRJCzX5|E8!s-njFGc zh0pZ*Oj3YaK8H#Gul8ZK9qaWf?{W12VP8T0RT}dN`x-9`UE|P+*M)t&o}k=DtrTWF z>T@^s8Q43gskLh1c*04|*-X$c(~OQj6E${1_tea9mvQQ3&uz2c|E|hNmn>+SOG`_? ziB&r0U6|4SU|MXfKyq4ZG=m#oHwad-Pt=8KU%!rI9>uG&+I`81(l8MR6qQvrtcS4c z@9s~adkQYo`}bVG`ns&%bMW%=*5wrjYkQ>QgE>+5#XnYw*2JR&(X_*flOzooq*e|C z{BIve$_D-f8Y(i_?#`cgqTU+RVqO^PA8*4Fr=Lb7dlSn=7Seq{LKz`Iz%Asv(qG!2K5ihEsYY$WZ$! zTs;9ZjiRK_w7$VWeAW(ooUB^4F(s)iU26-#xDLqwN} z)e_kwAFd_ByTi^Zcae*0*%R8|q^cM%doMGRVHzJ>7jacvotV{EKeLKv)W}U9B4{Gl zT$Vi^aGMcBtnj1|t6Uh9PZ<~4I+ELF$V%sGk@xBZeFFk2^1h#=CN91+dtO!3&in*O9DUg)OS_>7j|UI zyZw%J=O6fMo$Ow*aBvuFdCfavV5=>N4tlFM|ofq9v z;_Tbinf%6Qi{JSyc#{;4Zov3VZf4YE=}o({Z(j3V4sboGiCW1u9C3>Zp+^`P%!ay0 zNcp`ksdJuVMSzLF*j8*|ep>xJE5hE39)?t<4iLPZg0vDvNm-v$$t}9&pVe-NRDD$x zbv{VS4JAcxlskDTNd2O>m7CzJeNqWxypiCW-GWiON;Y(H&j19Thr3MSi+AWY5OO(P_W@xJ}6z5jge`4MqX%L4jIV?tiD-5uO1{N?ETxzWj zKHQ~RDk-j?@f6@ZwO^#RLXs*Uen8JlvQrmZjlqcW@K`;43Lu`GK;$+vlZ=H}cyA4C z30JfI*~l|>%W2re>b9a{M`=id!()!VYKf3F9nmx=%;v zLx|M$C`cyr0#5iUETX^?Of@|wQuTm>-^j>F&LZxx{!8Wp>!aV5x4L9gQ?k4^A{2MU zv3kLUN!6);=aZl&9q{3k1OPX@3+wDfItImJ)jyh&Hf6<=k)^sAp{ArU0-T*CJ1slZ zB$UUtiVZx-*k^hN&22PbwxeKp3CcqK9?{2rQBUjCuJUh^Iya6z=rJS&@wwJ#TaZ;v z*3$E7SDt|%4~(HCr&An!`j8<=>Hcc!)3~=)zHUT25!N>8Gm(_b+ip07-^@3x(+iF_ zFfyPp7IUT70ROJwYINX3{bvm?VcNQr9qA5?-zIyCosGhO5ABgJZ;awa&?CdFmlTm}ukU6{Se)e543jfRz-~&Q$>Q^sDh&wcq8+QZK65_N zdv%u=fu1IW99#? z(^L4s$#i>mcQ4|wk4hIE^3ND191r_g(tRv+jBl&7UJLTScnBbI^k*at4Oc;#L%AU? zsQf(^u#EyPnDug9X~D1wdEj=R5F@~rW2h5G#f}5To2838vB!!iNBv(tkng!`EE3k& zaM*fDj@F~aN4t-TT(;(W<8GhhP1o@_b9Yc&RCKR-ooj zi(Qc+qoI=F1nrc zs4N_(9INk~taFsd`ZqCRTdS82`Wo$L#@vr03WXFp2Y>LM*2sXX%fU!?drwkYIwXox zg9Ev>v%@W+HqrV^z=e*G7{)TS?Icoq*~EkpAK_81bH~YP-P_!X1NZ33;<+UzriEYC z-NO=IMVKSn56e@pdFDEj@{2YYCGm71^cOuC>D`}h4!cVeB|O9}H&m~WV``c!=huo46;`P`-Mh|U z3xno$?ko|98Qn`O598el89JHt#i!~Fp3z=TK9}Z`)$*A4xpncJLbJ@2gzUj~g0I3* zW8Y9GJDWLxcn~A-1ooz*K4obf5bcI@B<5ukubKInoQFDXoMdggbX+i#wr$(fWn0;9 zz9#Ea#XZEzwUk=LLy{*8IU`C0-`S8GB9qm483enk+$%{0- zM5KISOd`d{|4AytVKJTHtT0qtJoWFfVv6@|VYE05yo|cZy|eY?5tWEO9eec{|LU1C zuf1b+;gdtO?u@ysz{%jOfA1ksr%kN9X>R^cTbCMHYD`Z+>*MlJ6^5=?U$weNa)<8x zvw-$Rf|sy9r`w`neYzo%pNB_7<&C#!ereee%>w(QB{qKBc?F~XeNU&O15L7yWF^}t zb0QB3pux7$A{e!WOlw6AhedPq2%OE&E?Iv+U~EwTGHW*oOEr-#Dft05_04)S!LB^R zetYCTeqV_I23h0fnAEX&O|R@*ZNm8P4nP6%)-_1!@vSkJ@UX74wplOLv3XUW7_Gwi zc=$sFUc?jq*mClSdrRGK6e#k4SnBN+l^NJjqIkxh`IB7q9xk4VA}2sB7FS8Fvg09h zn|y4m-shh}LV!;rjwk&Nd~IWsaMVlVDyN3^8Qk!P4`|2j)2ZYs%<2zR(DNVs;0zp0 zn$Kcmj#{YzPM|RL6arGPO$1>lK)ZfonEt!4`W4NCCRZHw&+p=85wI0#5CGrzYfr)tw$OX$7Y7>2GA#QR&-%Kc`m2P;M6A^u*q9iEfPg>}+dC#CKx-1Ko~=F_t0Q$Q_AjPf>bB3iQep2t!)ZpC>otjzCw*b$EgABFFjST zb?fTY#yIaCzrAAj&qviKrPKaL#XKjAQQapVOS!~;2~Q_I06a@a=Vjqnz1{d`Yc@5M zRd5t_0R~1EBy|%r+Z@gP{Pior_pAIBq_tA3#0o`V@^Q6@QwC^{hguC264)ZO?-ET& zr^<|a;7iwCMOON{Bf@Z;$I{0RS4?h#N|g54U0&<@b%8I)&W5>U;fF1J4($RDem790 z=Pwkk;BYw)uWM;xfET%S52{F&L7sGfT05-Y_Wn8gcq)sF^`WT`i8_O_zM=*wWA(mv zJDj89Z}m#ria)G+Jj6WoZ9V<`!855=>pCjr($6=<6m8!9+8O&audh*g>(4+#vl(rd zSu)HnJYu@BL{q;wZ=mgStK_m_0Tn(a7o7?%9+KIefAA7VBh8G_;xz-`n0CCZDfyg@ z*cqdhu&C!oNU|^auyrRw{}t?EPx}-Z9KC$RY?Z*22|nprMA#VW0)I z@*il8)+t7h2EyxsPn?1x@wJ2P@eqEFgDNJ(sYc?~r$8xCUIV0w@IRQ+hp?;-M#@${ z1LIiIqxo*Fk$w+axKKIB@T;LY-E(pGm^E&XyR-i*6V_142Okn zB*RmlOLf;D)>m=z$h>~ppAxP!>%rX}003sO@$mycerz^A8{UbGO>WE37O;b@D4Xps zzpou0zKe&jz!wrTq5gpUErh*g{(utIa8G~lW!9C8_1}BzLR_`C{a#mQWr#>LKl50M z1^d7rTB#6AT2&_70kGJN(HCNJ1@L2*a9AI^QFF)#9r1QO{A|5MLd1MN`D>OU1Hels z>kGmBmJk|eMKBZ#NEDd5LXg;zM_Q-`&pz54ksOwKU*@OCL#fwmGdY-7zZ|0nLa95@ z=I{NGLPCEs_V*BS?r)3(S7kUUl-JWkrf?nw>O2Tpq=+M76V)@-0>y zLUgm;DdB&}+Bipg}5~rNmCUB4pu)OwLFE zJnaP-5eO#?JSPAq*@LdrDVFfmyBgeqx}txFZ<5ZMfPxo2aCIr!E6}YdfH8zT_2vWv z(Q95-Wb@82kUs(h+Sk?Vj{w9t41yC{2qw@Y_zuoB<-KFBNf;b7iDI}Mng0A6QgfkH z&q?C$Q^i6Nt9Uazm zCfSux4GS0#lRDnI7$6=rw2F_IxlwJy4&4gz4ubkL5bYO zsKl0gSD$^PN>7a>WSSSc^I!01*^&Ph{(O6^{{iH>&jHtMKiv`Tp?tx@ojgLkwLxahy3j;BWo_!EbJkg6ief1eg00Q|x8g8uJk?8$v0GhfTn6l7c=Ao8c@g zYJ9)wWzgoOf*4ZYMp7X$k5{I{{-9WyVV$r`VWS6!1E5+x>qEH^PvTEPBTV_{Mnbs^ zX+xfQJ2XTB0jY&VIB*lRNxKEzvB?s4xMom|okArcJU-4!mWt`S)lCSY@QYm(AbUAP z+la*=_JbT2kE`zdYv8&(pRt|?N=bx>4TI@6t-dGGhG;yzthF^q+4r~pFG0>yQe7m# zKD+>2D_};wMx4utl1>1-uH}_RhEWuMw+i`=|LpZmAp8{0L-4fIm;LyZhH&bOPe~xB zz6L5oAvbag$G~rVbP9VFw=S=9do`{Di+~wk1}MU4tqU|^1vd&_z|h|NZr$NGMNtM; zx2>$KK(D~j(NR@Z)!yD7ebd_dqM|imCZFm9E{|28;6$nm+^9_&0Q^VE1c{=TJa9aP zQ$@M(mVuN11A+Lfl&%1fGT@T_|KZX<0u4e+2wJU%;`sn@oDdNCZyaZd(yQ_ZrAiR; zN&E}(iv{rlsI3jbIme-C421ByK*NuwlLFI$LeN8KY-|(=%&fg=Yrv_>4e+)-+)?!p z9bssN6~_w@&Nk}G|MQnEnJKP2_1*wt3h?m%Cc_Z?8-fx893Q~q07?xJim>q;;CsYu zUT?IP!DjsE9de!hYy_VwAn&!n6B4VOP;a!GZX7@Q^qDV%a-#Jtz-1~TINh6o_9&Ws z9zIn@z66H`)@0-uhVrw7N ziQ9j>T~I29=GtlyBQ5Lz(>~_CJhA`{S3tHg?2L|YhHKh;>XA9-Q&=JvTp6SxpMsQi z*W3iV=VR^-aQjlzVoA*iFpxdbX*)sJpe5pn+tTS*h;m?(=KM>LIj>ail!3i!=l^*O z6D!c(W|5=$faH(X6fXcc`=kX~8aDIrItPVa);c<3bgYzo-SjNCPHEHX$n!3AjV#R2 zvjhU#P@@|B1nrf7;UgBs)+8aD1HZir&VhK6>Mu0nY58&*PX3z{)%zF@erF9b02daX zf?NR6B1+5~+eK#(iXetifQPVa*|I^Xp6vecFEx=pE! zva1qToF2Rvj2~_$zjy_Ti{vPt)*M!tt*QX#6j2GmnzQovo|NftEDt2aMZLLOCOLB& zC;!Vb|DZ0R?CVe3k$Dc;OClSbTWTT{p?Q>Uxc1tumI$N!+|ei9W$RtpG3^*p)0Hom zdJM)$@h4sQ$3j#SFFo5zsQDn(BlGZT^f&vitx(^0?0}#`EM*ngkr+MHl}dQFBFTMx zm1=^F1ah8CgEgQM04JUm3^3hL)NP0Xx6kk+31G8CR4(HpI62OV4cKO^KezavS(mVm zv7n4!q#u)hb@W`HZ=NyIHXg4gl^hFE+wmH;Z|dP}{p^S9fCqz7ILnBTTcXmZ`6tP`@_ z7w!w0rxl_@H3(w->iQkVzq>IpUpy$xI8NXK70|6-bFmFq53}AKXJB$)1y4>rd*U3+>1_0Hmh+oY6wX&}rfVO5 z-G2U=Ed=%CA$4*@eN||^zdS2VNh=ZN%CvRSiPt!fks@BpiSMr3l$b)7|K+D(Wx<8Y zaE3pTf2P*J{y%Rgz!jQApGWcd+?i^5^#vPnL(BJNeB71$7hveM=gmQ!6kh$t{8opui2a+>z z{MYP}-|krk*gPWotl%vfnR_Lz&M!T~@l~=n;EH*Ca(?r{wo`25fsQXazHqYhYdj&M z!Z*5#$z5<>qU{i66B1I1XB&s86U4mr%S>C6Y)?8jXV|cMr)T-ci}O`ieFJiN@61rD zzcFV%C)EGD_bx1=I+(9o+%NS@wKXg9sFefx>5ToBQr&P)ruk2xr*_I7!=3``At-vg zl|=c^;iQ^`#?t`wt-fzi{>$G*L{+XU%H3)9lE* zXVS_@10^p_aWml1q#aUt6UM#T=>h)N%`@P80m^0BzqL{sZiU)yjdUDeHniq9`_E#tqk1 zHkUwE7fNAsZZnqCP?nnTRtmd%AnTW*F6oU7!6leTL-+Oppg7?~#I|GnaXTy!f3Zx$ z)Gv6@a+gQ`Ssv?lVutU1rG$`gqYn^K(x`^_v0CQ+5 z_XRlrXQHq8PnvMrP9Q;Ed+-x*JU<$p#mgFUozgMb3p>k%j0J#?itYL5?`!t8@2`$q z=!f#a`;Z{HJ^Wc0hlgx0#>25d^(UaPIgY3lctiZG^DxA3W?pKfs|y1y*2z{C-siz- z1~fbNw-#t=X)!ncP`18<_X52FA_M|Vnb7<4b);DXa+uZXbt>EWO!6kGj2zJ{ENKkU z8JDe3B3r*F3XMX(l-1S*HU=g*iitu+_x8soIA%AG`==wX{5GBDjX#TlbnBk?*;;dQjTe4t9Qth@3D&T787yXJjU_yc<8wvj1c{M{=K z_i&{u6jcA8of@t7pMva2RTsM4_{vY~@IX}<1B=AV6u$X)LNM&PVCn_1batv>TSs3? z!7k58YU!+wNIX8Wz2jN0OzsfkUo|03~InNRcjb!m*KR&Xdln z(@eu`-G)Cx1()jcK@?5}OI;sIp$<%c zX4ThWF4}btjW6MH7*-grjrMl>Dy!BmETuguY%x^Nz3`=KH!Tb~!0Q-_Hn%kFjTITm zjXGa2i%q~siut}wQ*IKiw&<)~_QA`7!7{svF#@}mh+WD-v$S(KP-ySy>P&nf*=r|$ zsX%ux+~5~@(Vyluuk7`fCpblPfVO8;`|+7ps+UOcPh-Caa2gy`eZfw?F0l_)b z+fS|mm_j^jeZszBFs+t@_k}V(?)W&DjBB3iXjAZ9_Hk47^IT64l+!a8@Nwgt%Z*-a zubewBI{wAB{8!a*+Fk#b`fw#x- z30WRWNM<71A`4txxdfY`f@<~(zyzEx6adtyuxJGyQZ#vcB*(P0w4l#)7|?6WkNh$nFTyF1`9;YIr+aYcOs$o21Xs{72?pCxtD%hJ3Mx8-dBMRysNknglKp$g zmmV=}TpMuD(Pmeuk!n3U@YkJ`+djIp7qq>lnyJg z>)SG)=)u9uP&%t4+zb3HMPDT!rFA=)U}(+&dWjg5x2HoH)b~4{&06zwRfpHNs<-w< zh0HsGt%HnA#E9SfCK|q6?RL5|WYyaAn*#A7O+05%u^)5RmJWQ<23~#1G5(<9o>MqK zs@GOV=JWCy4A&evXuZ3o%ThK)mi*2D7W#lLZ}2t*@cPRSMJZ8#uK_zi4~yxo4A^(PLR% z=J&z(aRLneNdtx`d@1}SX{3;%-KY_)7o6Ov9y#>8?#5KdNR9TPw*2(D>179{560 zbi7C>Zi%<2Gb+%l_&k=z3>co2nB7IA8z*JfoKTxa?(L5X)+v~+9e9Zhm8Ez{5=%9nq*#h!B)7}Nz=`1fO85Hem!zN7d%*#K=Z zumg9zuX|p=4yqDEfP+8hCLKh8v9zOP`XeS`l^98lv}oIo7?wuy1iEAYM)|FyChlI{ z1cPOu?#8t8d{cE86PQxxJ$gg3VJ|w-w3Rpt z@70IPE49UpFLA|Wn0N=46^4(CK99FW7jehsvdW;Lf8!ocQ2>%8fe;aPp?e+eTEkNS zOikVN1|}G+9Toy>e_%f&rjX^OKlJEgv`O$ zQ~TGPSyf~~65PEV&K30g{*9$U#<#wQsm>%8F;xuic^{V;=n-MEK{r!@LecKH>KV*< z?qHTYjDs_3%eG2`&N{-WpbN{>>LpyqVKCAK9=;Y2`BdP6Cix{iQB(? z?`S=t!{1|je8=zb``+dt*qSzr?xPmONa~kdXo+-|c;!nWG}XxdPL84eQl46?m&n2L zHpNr#1MNJ3_1TWYtD5ftnh>LuP`lhPxC3(tW8jWPXObXGow%d2z>1Uhc%o9;)L%fd zL+G?cb}BLPH?Ik_L;hdD+8eLNsMW;aXsQSF;TeKEtcd%`i(N1VcboL!+-#d$h zD=S_)`jnh(+~WKt7UpbT)XdFkm>}ZTGgxLpMPA{DlNI)T4-4xEFoMSKPok4e!0=E{&Gqc=4a)it)eH z3&1)(zpBq(^;t9_!3B8$2?tLQq<8-_`0wYyU$ugW{G zS4CvAjg~lg6Wr=cxIDDV7FaAQ_`ppEdu`Yve)hD#3QFu-H^N|OL7DZDfSU^E^Upgn z?id-5`!vU&S$p^G;N!ff;dWiofwf0S?7-orpw_^U*nOXGYgSaK*p3zYPKPT@Kvz|U zqrgU*9R>gyz3F>qB<C4?UB|dgh|loa5Yd5{L|Yo}O+$i*^l=dyjKUq$_H7g-OWlK&r|L0mnnDXm&WJ zdmh-D6TlHw>w;O8lO{Vf&XMP*osnzThF4B*3#!0LV%_v zMd25r`)z3XyKEWg=Gh1Y;tK%0j0XEBh7 z4B%xg$P6}J%3UwHpH+9x;oJ`XpTyEK2(9Y12F022V0?r)U~NL~>-z}9_#7doF{TN# zEwIy)wUJJhRQ;5?iE2`q1lRhS<(}-m+ zrcqfZ!a+txq>#N83MG4mqRb;9`ygb6tgH}H_Q+np>vhz7e7@iB?;j6&q|WPg-}iN2 z*YkQlpO<|&4qS!4mVgkdq zFtdzk2X^IUwwSX94W#Lw(H7IKbJY zVP?`hox^*kq5VLksF0{fqWKq1F5S;Cd9Zs+!ZGI)VY=*Y&?h;5tW7L9cmu|o4HmSQ zJ%VpAqTNqC&rGJJES@?jfW7}@@8LY&mJ1vhV3-*p8YCEL>Be2zT%&;jkFQ^JYGw5b zQ`^$U1TFdvx)TbH5{P(S)xj(-`qi*w$l>S2oQjsQodU*_>(x5VQl9Pd{^4O<4BtD8 z2OiR%vA!BN6lJ947wa`VN!Z2~6A$>IR7gdL+>KFv(|9UK!r>3mKCLfsFKVyXeIMWJTdqvlirF zD}*7jbkG+xvhhA{T28k7xwEbl>WCfEt-C!=_D(nTzWwBx6hcqvN_k?jKist)^{n+r zaWI4;pI0g;WbloSG)@Uh{1re|dUFl@h^-^T!S+1oZLhwxSymf_%9q-wk=3Ci$VLSn zB1PvN-jvj?V0e4fQ|#Q11dJRpoPJFuQ+w;j3-g?7;zUu#6{ko|2DBYzAptBHZSh>S zZju@LlAbvbV>C6U>Kaq2)7?qDaCz(}^;Tl$ZfLJP+K&G%I}=~?3x*Y!dXb)cCh=}m zA8o$etJIwrIa2kM5hOo34=a38f?0qvqO^;+{bx=B8Ro9HW5%HeWZa*@H_W7JrK0zg zOtVO zNRGq9!N%UifolQI4^3IvGHs=9-2BBd$un*?U2Zg)?)iY}0=0?rWi{ET*vRl`ucCK? zZ&E}CI^HY@%Bt@Wx=0XZU$wEVD1DXX!xHs+`B~i!q<7b(f(l{K1j@xhjL7n%^zAPr z6S{dKyFZM4%;d5yAF38y)n#m5*wIxONYR_nT~b`EV)&8ykVff1P`^O3%Pb=oY2C~( zfc8E+8oG5`1z+-9KcTcL8>z{;xVKWztMbIuTuwZXwTMFS!MOFp-Ven?c}sgQEZmy9Ibrv&Xo!>5L z>-$B0ew(&pUFCJmupmtRl?VPsVMI;I%E8o zF634DtNc96$L?pOnq+NTy{V`11q2q~bUPUwd=PWIAxHap;oZgp4U6Ol{J&0}Tkel} zS6XO=v|e2Z1P$Hl8Xw=i^!O+PbU3U8Zny^I>uNcYMwniMq21OCLHV)smd4p3CcN1t z06QtJ6OIw7uUZDPsGQ)(vQSGI-_2xeSU5x)d(r!qLW6xcxtqFR$RXaD8`>oeXT2q4 z>2-jI0WeqF0+AQnuY|KG>!NLn6=eYazSwE4NVZ6O3L~~@(AGz=2Mp9hElZICQ9?#V zaxzvfxBRz0A8$sQtO!iM%Sq(=r#KGW?Z1cJil%q(Bos4+GPR8+lAj?oflCbV>3 zF)=2$HOeQ#R*Q2IJicNHm$KB-hnKaO11H9hi@5LFA04@WEMp<``BN7 zf?58&QaV(~=u2G#leyK9^w{HiXT2lIn=_L{^gWNyDZXZ{Jn40p0kjB<-2T>%^`LD$ z_5+1B1d>fBYjRig&*F{mI2oSM(RY*>;#f%O-Yix70S%N-YlVDWRdqSWa{Qwp?wc~% zC<`=AqD>mP{Q>&-%;6uFq{ZYL1(^|GzX)khxCL?{uU6W-%bmmTZy53)*b2`#fV26V z0f=Xhf=`NY?SM~4dQL3%{Q2oE(o7kdka+YRM>W^mH?A9ZY?N;oK(<%#mR__#s)evQ zU_(ZJ>;r%C7!i5Lg3p)*b(sCpxA)YmFZuj_?}9(B*nfkZ5aaAiA8gTcx0JbSzuEd% z@XZbsn(29*hl~K%z_Z8AdT)#T`6^ec*9t4d%jB~Q`njD{-rg=G%+9w*MM>MVuDv{w zxe7fPQPK_UIqSq!)@?(b1mlgDtQFswbV@d7$VyjaroZ`-O4rj#=9vs=8Afy)$8D{@ zo3km}Zn`D3Sx@s#KI_AYnp=`ME72!gSV!%$wi`YZ2_GuVo#>p$IB#ZIIdAb}6==1Z zFXY((Pvweq?b0Q&U^PQe$C}lQp;R%!Dp!rMW{95+CzKM1np^7IA~e2z@-*FI;X(LM z&<@0IJ001ze{S@LsIu>Gg+IiI?;OIyh4)VyOwrcNgz?FbuO>KB%T_TcER6QZ&wBT& z@I@teS*A=*s%YU$@%;DWO2$vGq3?6vylVMknex+9=B}kQk`S6X-gfiLG)E(>K5Kmxf(q)pd7L)ag2^Y zkPqiD96xl|)Tx|H_RHNXPUZB_15g-a2dH~SnaqOkuJc!qA;dnQuYrbwr$Wh_aUpa% z)Gdd!tiu(npWPgv6|(48KNP&WYqP=V9NV@^j>X}Bl`;o_L0N`bc`g{_Drmj^B8M=f z*D$5P-0O~svnxSW7W2oU}M2j@_)6-D`2zV#C}FwlByQ$WZmvZ#>xO!2tnnTamR zTfIGIsn%9v?cEYT$B7HKUstb>)&_ZZ67UhLQ|-SF_Fq<)WIkIFq1%tds`5rOUdvzU zJj!s~`#6(iT~G%5slFb)knNQRTvcy-8tk4O*0Uy@n>NImfz=W;`U6N{jRKH`3OIoM z@B)BKAQu8pix{n1r6y+cJ7Rr9h5RhG4BTUf5li^CWF4$L-rfz*Qiwc^+;2z+Y@a2F z_i)9pJ{~33(?(GqeCQ*2oc-`53njH323uO3mzq^}FqCPqVP88?AG6`QjSt`z8dQ@T9HSCyb z$UdHdWZj3Xd_Dt-W>FD?_Ss#t9J^Y?F_HdaWb51I z-N%x?bZT${HmoBNPo@wQEa54_KM(Uq$PO#=+o5d`2%faaGU4!l6~Au(dKjqiUgTF* z^L&k7*nHw7g$-(C5@sL&@}TpJl-&7o0aLZC!BCoGf|Aug&)zxS!T71a^4-oul#ajN z3vpi$^tMKU{bsdELjyZ~1}P3^ke9VSbLND1HzoSv z4&6!Kzka{FgRTAghK8tSm_V~0%s~8)F7>x^1+ShCK8n1pe>>zJ1x-J)KlT^(UqKBx z>L;r3G+`Ck%A`kRIUiWX@z9YMuvdDfC6>Q%av!mDxsI(J8GE+I{(mmz!Pc6*u$5aS zKsY&B3PxW4VVUi(9Ac38$Ef!h5KIwKH@=aKZ{`OeB#eI@HT)0%{-_&^!86 z=lyH32$S=aU9l;Bhu)uEJ9*ukxb^8p0hI$-|EK$Qkoc$l=k@>bgKa*SRS}W=QG8ai_K^`2mj&Bec@O$z zIzaS~<{-%I{$u{_o%#%!NhUEoohhkEIT@TNT^^L_hXR(m+n%qV{3S}@G&UdGAe8Ohh_ll+C z-&#Gq2k8ExMYz}aR{*uziGT87#L|C%H{#o=|Lg?@qt>hc|)vME?^8@hA454+_`W(;4K>*lz;Aor|P- zLo4g;URyW3gln^~Pdu0j(c>{|{0GpI>FFYCj|Ri*&x+F5k?0m$dwOlnR^Qoi6XmtvDBYTu8mxXZN;&Jvb}J(fm1J`&;hcZXf>y z$$L_G7hS};8Bavrzs~SYPq+>g*R?O0tgg4W-~ZP$NfKmVRF(D;Zx!3H+vpGoea)#B z#T@c#8gmNaD?r`>8fl3 zrpn_dNuZL-d4ve#@qzN6FjYu~d?e)B0wy$a0}D1I9R2-D^Ph`1@y2GUqpSImzf~=S zsO-NyTG@7#7Ra%K@Qaa@nEVVoZBSuBf8x!_;LY*Ha8wV#m|L5?@^ZrVR7LsoN~jR_ zWniTSfyTG5dq`Q@>;6HPvZb8jubx`@ldC#v6cl3a0^QDpgbugTKd~P--OKivcIq&g z^+2Zz$fiEC&VF)LAx4F|8BTwGU!|HqhTkZ|9M?0juN@RlnBv*o&&a}ql5^fl@uql@-B1GEJL?Gv3QCPOK6>uKA6c> zOdu%h^LA@m@gq!?&);f_v|i1qsE9T`ymk5j_M^yrHyQShszKs=CPt0na+mxpzF?|c zE8S()N~>>r-TK4VJzbkB>+K4Htogh$x5Y=hG$NA|?^paqJ zd>;e*Ry$scm^?y7>4)GiyT5O|f9`3#rT`z8 zpb~j$NcQ)d;vHkRQ99ui+0q0$;3FV_l>;SkbgDum<_VYF#`C5)>l#z2s*TY3-+<}1jV?d*3pf>bc?IB-5kGl zX)sP?knhI(7+)clw1;Qwg=hQjpIog%F<*Q}ur5FQfOa{N{(83_S2k=`Q3AmXG7Q3& zEP**^|2FX6H}%H{GkhkA$VoU+Svfg*uU`}1ME{udCCpe}-bsj_yniP*`JO{UZq950 zN+-0^v!ZhL-1`@wsqe=!7pyoYpnSS)Zxo;emX8wzzVBl09KG5{X`M4^?Zs%Jjd?Xy z>8K|s7XM6YG`Ory&v7_|-Qc*wytTy>&vU5k$+<$E!R7os!VpEPwL$Zosx9Tv+)r|2 z1B+*N-f>`Nze(tjL&~l`Rs4xiaCtpqDn~;UNjIj$s_rG^f2yx{vNz~N$*I(eX5Y4& zVqswk%vd!5VyNlYELw~~m579=qbPq;Qe|I9A0raBvc6XCtQHI8WRQoq4e#nu8QtCy z#@*YnYK<=*-Zf`D9g$`_)zTsM>uYi%Z8P5sF4M`KpKTA;Rvnj`inBd!a~?h+a&~l( zGJNsrajUN}L9dj93!sa20P+{7QbA+|nc|c~L9aF zQ=UTia|u_I)XMW6P9aNKLu*#aH@!2ODv}>9Wrb~!5E=wsP#JPne{SUQy1IDTPYip0 z>#P>0!mD_-k_I!nUag;Oei3qrkT%akP0cxBP^j-MmR&f^7H4h6X2s@ayrI8#949JQ zBbxnlZjOVC>!I22qpD4Yh31yX8HGcP%50rq?}`LdJyTu^O6G@cJc_;)+R_!>13eBt7z zr=4t0fD|x)5+G^@#w1fv*&J5 zjZjW!=MYMOQ6!2*F(x}t``v?UK9T;)WDyVYwN3oEr}6~@CnWvbN1Dl?+fEd8Kfkud7#5M@wsHC7yE6arp)srK_W}x5s&g(_Log z^Q$Ymd4>VDGi9{6qYdTYi)_-g5&ePBGjn^~|K4$FB~1(7hR5U>X~^fJkjn&b*a}Vz z(0o}9&aA9Sz3x+2^rp%0yf?Q$J{$`p4-GLn$_RgU@lM@HyN8(oBh8BgO~y-IAH*eS z;0&n_DWTv4tuAJ7IhSN40Ztv)j78k$Ym(kdG>C5k+7P_X zS^sZQ6x0S#rXIwu*H9by`LA-<&RA@cBn>#^^xnv8EXta4TI7#5xA(4F$DHJLJS?`h zW$2$FB*Htnegl};5f!g9g@T9XCCy-MTJ@$;Xb6%sq9*p zq}e^m;^nR*?igka^LD3gF2$nIs%x#AX@ltn4xMtxLq2KA`}{1{(|8WjLm=^A(ymaZ z*SD${8B`HFP+=0b#kQW}g9?#1chdx5$_}q(hN0;@O~ut~2gSN?RcUPye$LXBn3$&$ zYrIvpV7n}lXdv9D_`R6e@&(>T!k&&>$OjW^Gr%H2$kKPfKhj_vd# z6Te$a*JEYGq|M9ECbGs8*AnL59bW=iD+Id6cTOSG92m(kcdzp_=eaYY>y?97kCURe zwXgY@*?j-dlv(_Z5gkZE3B=wAL8(t3aZBESt`i!FJQy!4{K|5kG!qBv-}=PhqD~sj$>JZ`?c#G+*Ods2u5wv? zFZ*^1ezDW;+B;{}WsdxML!XtR^lcoDLU8r4_72rO!tCC<(;!0sX{7tES52(09rFZQ z#cK?++Mp?BMB_h-Tb}@o;JD%Rw`vBjO_(4@)NuErLVoZU)DCWW+YRlo<-tS?ef`$9 zxk+n#y0tFzy-S1f*_Q@)K+EDEZryqhl&2&l=pLOTv&x>#O3?8vwWU->Yz_tt$gW&= zcjjg&jTm-sFQrV8tRUR`(YOirCT~VcU6y%mOoszdOw!-M4xCQ&lSU}(pTi8|2|c~( zc>U$Z=4$)X*#&er4`2uNVA|?0I}@hNspIERJ=EGm)#K~u9t6vJcg?D5VMKs5>XS*t z=B4&RK-wQh(gA8U6;*>0^ul5U0mx@B*$zH{Zq3;UZ6;izV^e?VXyOaFh5l2#*EP^S2n5yh&isIv%3TVU zqDSoS$2vVWBSpiMm^d)=^I>GbNKoEq6cUEm&vMGU?q|2H3_T;-CIsm435G+NV%L0# zo1YS9&)o2SwHrtrL)1)oFO#67JFVlVF6`vdoEKt}?FnbDzQ<&YT$;wng#a~((RWmc#(fZ) z=G(?4onp}`%FXSJH6=?)Wd)v9#E$xeqj4j^{b!ZQBHgHfHy#HLhcw*fY8Ei_=?%ax&5E1bY*9ep##knRBW#g2|s!|Z{@-CGKY0rSkVREc!0;*Zs ze?cPQ14op^tXpQKhU)Nc=9fT}&P6b8fJ~p}@8p>2p9vW_R94*KpW2qF<}+ZiO_*gF zWeE1O!a#~=Up5RtWf6SS`o%ERbMfVI20Z-XBU6xe1L`dY0v!+Ab0#;w{ZvO^+|FDT z^=fI`{37LninbP#72*Jt36$)XcadzX9TnAQXKAt?m-@Q@J9y2a-RfRBJOj$h`@I() z4BVf5pntjeX0UfEJZ)@;lq2GOJXL}|`J4WVT}0GLi_wm+m#=J{s|O0H;^=AcToJ5| z%uK?#t*8Un>-`?)s&}LiMc!4ZAh*!l+qB79{WK~PI#ITC&gd=cVRq1JOY`1s(5Fp* zWbC)l<;<+K8dh}v13Ma2R#M>N47k+)%+0unEZ|l6^1H*q6w6)&iNcAdjY}dt2x|It zrp0T$maFK_(B{76%jT30LDX`Scey{5eSuNL&~xzMT&>gU}JUgP$U1bA26qjtlpVNtvvK+rg)9{oI^PGUz=(B5*2@R2`V=4=J)c2+_BeYQV3`KQ$qlK`ix? zYA~y@h39fkG0b4>>+O}~@WD8P<;kuCXy7b#yu#yH`nhH;WO zx_cNAPo5fLM5Fpgeuy|MGU9;N2PzR{Dh))ge?;+kd;n^N#-~tb{pbJx3igjU91gD6 z+`o=|;ec5&w+gO@-t;VlAt2*V+Cxc{#b+M%4nCEAR*SYoK1c`x^x<33&?Z(d&;r#PiV6|a0-~-41cDmr*IE=&6rkt4Mt9y(`=Dx&o z!IY4nKW|h1RU+>6^YdRWb%ojN3x8>^L_R$*hjJ^gfmoFqJ`}Ja}?DTz&^XD~OT{ZOdG_0&N92_*RU)L}(X$zo~ zpKM!WIp0=tPu}9wuLOnG#PIfeOSjsB=;a&2*4Slx?)|cCd&(pKka~q(?#ptwuDp?F z-*O$ZJSp|fmWwfQb7#lO+_-9=#h`Rp^e+B+WPU+!v*Zu*?%v%W=jC22v@s#W4e=db z=mxFE#8eKE?)Sk3?;|3-%qk3iGOZ0Hgg3RlK8<&}#G~L+hIQND(En_3q{H^Kkn)FD#7>&uB1QMu1$MQ!cSW?t4wuQku$y_%+24|`O@XBdaiyf-U0&k7caQI$ z18+eAU%^{mF)=l5?~Y zeDaE3e@8z?cy{wzpqSxAAcz=Qho2_#iS#~ zH3!L;Qg#W)nrrKQ*aZrF*SqvOBcxr_y> zgy$MgNFN+O74!P69^o6Zan5iXj4wgSfpMB}VaziW7sEKpzJrE&^MM&n@1p%^QuJ`Z z0@>*o)sMmld)w8$iS59FYJGKwcym+#-SD2F=B}1@&(EJnJ)WW{HECsWbS}#JO(O4o zt7JOlTyh*;3>%Qh&`gdATFSEjU9El2I)T*u&^utq^LCNS`pRRjgIA%l1x5^Zn;(S} zeW%zP`|_Hx)ERiO_`AUB>HmHQrw=qOybu4o$&L~9K*$QTWy{Gvvo}5Lm$z4IYH;dQ zTA8BQ-qbvfCwX?hxv{Kd@$`e+-^=#KuIrb9nW*?%SAw2GYjtl4IUBih?zteUcurTB zEpwHjC|kML?zV|wK6e4VOBU?#G(Iur6l=2DJZ|KWg#u$+0{MAgu zMu%agIngAO2p~#&UabcM6oq=KtG=1LpPb};4fCapn(YrzdlwEA=cLvtW@WYod(4Zp zutnIe%sR$@lERs6+V)UXrq_sTwC);go{W&6b#u!41ZwCEcV-n2Uy*g%Bn%n$%UJoU zRT1Gd$H3w`NL7hcHUg6#qoxHOXttj9UI|QhH?VN>UMaP821|N0)^H>4)9IKQF0B9*^x#W+t8=|hgVi>AM!orGN}A4b>^$MNFSl@=Q)||xfV@!j7kM68;hO$ z#cVKS1{5bEzwQSTiY_^fdEcWOU&+B`@eYL?_#@tRiw|GpL^-c@>BhQT)Z{#a-43oe zN|6UrCQ^wBDD6!*UDXD%Su4^v0@CkTEPNaoozPu)mdbH9=@mlhT78x&hdGc1b0T!# z$wS6j@fUGa{-tYUF|0y*UN11yM_r$~B*rZh;+_j^4QQx8<v;rd^lbY?@t-@4wN@kJ0P!~InS$@1T#n5a455oIfzT!Hm?pyi4AB7l87FVG(k z|5JdZ4SdEAbCBIJ;as&fcfW$-cr`3@ljiY`-@2&^e2#0Q0lGjg8=Ggajel}J!1!z4 zCnKp7)#uE#`4K@U9EoDLT*2pX;Q_abY2h&^Wka|>Z{v8xEDcAfW1sz*V}CY zG^=;-m~c8V23x!-x zMy(QijZ5eNFZBNLtXy|gh~$d*C@1_*f`{x1G0>=Bw(KoDpG*WtTjLrbpOM)nBJCf( zNFfO3L>A3?SDg==GxFW&(R`41s)ZVBj{JnJ08Uf-pz#Q;lAX7ivEVsKsu$BeKmLpy zrmc~bnAi%8HMR^k{}M>+V6t-MJB!jIJO!<=gO(8u)wteZ6FK&;Ehjd{KwX5<44nnY zu|)rT2Pn%+UHlwR6~LtSLfB5jN+;D5O-)TxQ&Z&| zePdp44J*YLJ}kr7!}gC;glPL~`e>;jqIGzkqD-pSj)7$+GsF$T?=(A;3TAFy(ScN6ewu(`BSbk6$8<_e2O1zwGN>KS45Qe z0#hqyX|S{g-1QW!m_-@E0q{g7%GPj{0N4))nBc3YrTL5LF@ImLf0~oXH4I1m{-3)B zX;OXB)Ou`TAF!${D$Vep6fX^f%9_v5N%Ja?+8Q2mGTk#CT!+afJZWjsPM!w@7Q4Sj z55EPwmTr{wXGfZ|s#lKTe7gwmk&|Pc=Fdz`_cd^dkz?r!8Xg9wk1j5ZHp0t#Wp+us z`!>~+KIXE^qz|p?>n$1+ox{+WE++KaXLC-Y)5FYDT!uB4qWO=k|^HuK`!6xQ#{-0ZjT zEE3>byMHc)py8@+GBGJ;PviE3>8q!I7$27F`Z()(0ZPaduJA6p-4QwOYs)}N4AfF4 zB+jdE({puKxUS(PGHBNkO>8gflUJm~t{RX_OS(#T)}V=3j*nYQoca zN`rf)#t|ltwGFr_fk_nb=#XqcsP7P#>zGs1WNeR7J(0LC@K8vQzr1Rs#1wn z(S`ZKG#C*v{q1g27(;ujN@qh=+h`r+D#Q24xXT#`K`d%W4hd>*rlN(XIRC*0DO}SX zb{E+~s-3NOMM_S6)L198cU7^USIr#;4qqCy;1B$5;sZ$=#IoOZJ}~z#mfhe#E6gK{ zGdbfpv+Mq2Z==4wYI}1%ByTK=e%@1jvDyN_Yz|INPItH7_NsL@n|9aJ9sTDl9SH>Hl@x@&2d&fcsiCA8ai53G@8O)WLle^LpmlY5Pv3bczw91t^z{O{8!zo zy_NSycRokNeVH4YiExt~D(DVJWu>G*)5F*uK!=3EPEZ$yTsf`P`?B~Q6Nc(J`^87! zmC4fdP^-(m53B8OC$GLqdg>x7JE>*d;UeX|8k*`;Jq6?!YTxhcsxL>6Z@b%{Q=qC{ z^)NnHNsL@(3#cpYS-Aaau5fa`d#40hxcc1oe06f#k@wCyOBRW@Lq2VkbMJ}4|CaiVP-y9pYxyOSVX=2?W+ux!eXK$R-q z@2F>@`MbTvlfHZDg=t@B6s$vacE(a1$g!?&e96+f)=tT1+&;z1Ta?K*Uy!+scYHw( zeD~3P0uR1BE$Y$;D5lc2Pa46jc<2e@;WLji*B!y7NwlDEm% zo4A~eSUETz{NUQ!UEAwgamMG|Q(%_;0#ihI<5$+7puqW9!H2d zkgRPmf!(z%iYfh2xCjHo%Q;UWiCMI{j*{VSUvSg&o+3i&$BUa!R=sFT_E8obBVczM+g-wV6!Zof2EO_G&#S)CdG*%^_c zuEu*zHAzm|<=cnx#j%KnP&H0@8S)LkmyYY9OKRkpfE0X_pBBY*LHCF8D0lLX0QUV> zz?M4`Dx~ZJi!X6$^JGX3A?B|0`V3$upfGkQCJU+dBf|E4_n zLa%}>L&~oVj(h9RNc-3}@P81%0-^IQG((5Iu7Wq4LsDBw4QLcYbK7C@k*}LhE4?iW zo?t&_1~5JER>F8e-geX57rKLiZIk}eZlbBe1WZySNC-cGmWV7NjxAHrX#Ci=u54}E zpJo(w(Yw1-Z1Gn~`nPNi$^xfJi*m1HD>rX+xFGg-g&$!%0BhyCE0!0ojY`)O=$N*lXTP+J27k?L78{BX3-i48Sdn66J1$|EW22cl%yXHh z&#vcYtaPWa*07snCHw>0zga*~5hGKA`5E8~p2w_5jk50h`%dk!ee#!{68^Md#dvU; z%er8O*7#LOe|O_lVpG+W)^1_zUgk!aUEW10eFFnq+o{e>BDB4M071_O|7A|^m?r>g zuo92c&|!>uj}S23Y#p1BCCnaY@Vt~5nRr#py|Uhj4nf z_B2Y_@|Z)wc-U|N(M9lGh;S>-mMZ~zZ(h}B7`8rF|AYU$80SX1q4+Ji?ChZjA*tD6 zZZ;LE*_~j1Ba~ji+>^UACv-5()TfPLcRY)}l|JIsLeJ{Sapa_^{5{GP&&d2kMdJU( zx{n(mJWhls+2H`=13|w8*G&a7f2~2l73Qz~>PJOgb6>bRmzbQfm&HuI7m~;-%T{mx zNs?M(6&#;Bvj{zENh*$*z&i~l%C6`^^=_?g5@p}_nAMYSrN%znh`Ys%^Bs8%L+!rw z3@}V^DlmOTNc&JDBy*^H@cY1xSML4anu`weBrL3VJqreAOGr3+xoMQyKLQM3&wVW{T2e1C0#XX?c1^R=?JeK(Rg(p|VH z-KoQ`Jbv6$t6w{O+Rl&6ZfNkjMPmFB;LYkh0+RBO+do?#kMI2fA%0VcfscTU@PHH> zNeEzkbRSR4l8C1S4%i^rlzA=f`L8ok5sdPT2GLwQsSHPr-KUl_nmm8q?cZCSS-kM1 zv{9^EnLM(BbZMZ#dJFmi5A)ekS$w)iRfcVeq);thh2}G%7N+o}3KfP`UweX+D7Bad z?16{VlM}((x3*MU1B-NRfZ{W5`Cl*?JQgCWnwp#MIXHYukY4_okzBdjZ#__CJCJYM z;5J{mRmW^}^SwQ{qGDBQYE6GvhBg)U&KaA;6WipsgwnpH|6D!UbsX2fm6~*#q!lKa z84h3ZCT8w=NTb2a%Ta&MJwRm=XxiYc)fyDEtlvvvsg>fjEVLK0H1wp2r+$Q6ZG*=8 z`9pBIzL5+L#qS*V9Mc@CL@WqmQ0ND0$#7!c3El>_({mGX)z|ETZ61OP()S&s9oL?( ztZL(u6#@D1jO4C0X)P2J6YKd&ehfOV0SGq}&m}6vA#))()eu|&8DRxz_!aYQD{py} zj`cjc!QZ7Qv)Fc!z4DF%ws^dDJo%>OdRcMA-Xurz-fECo_R5s%^n3EW^!${bT_73( zHREC)w8_&e)jL-E0mz@w<;EYab%_f+6*a?Dm-4KvpSUqNW>u$2Zbv27%CfH$36lM^ z2gEEljQAVyDN5gTz4;S^BjIB`~1nU&O0Hu z2+2LQo2x2g=O#2Nb1jp>LFZc!|Jtm1TTb7v>97dJZTsQ2J@+vq{{GyvC;>5nlo#y_ zB3reAwKJjvGK(I%7xfyuqm};t9>z{Mh==*3X-(xHS zBj;F{{xEbvB>a2Zt1x12urwo8CFJ7Y%P6@AGBfr2w*Q9e5ELA-6*5NHrP)nU)iY6} zX)1{uwqq)`GhCIMSz@izMTtAZ-!yhpQc~956cn=BoG5TPk>zqCWya*B--&NDSa+7J z5wqb_M|-DTD!>I|xWE>wkG*c@;_^t>fzj_?^wA4> zZ7*5m<>^5|ZDCE4+vZK0*T(RU%8{moAL|}A%{VJ?&2weKe6|^e_<=Jy4S$ng^74*1=9>jHwbEP0gDs&B#L6x5SA5#zf_VUHYfL$}i9+isxA3V`Zhhu*DJ!f6i$hXsaDBb8S)=fv?nfLs6R0r)3w02$Nuzn+x) z@4f&@cAfNwaEgb4J?N->G|nhs+| zC$FNVq=khCdnE@4WCuIN2D>E&`=kduLNSFg7=rMFH??iM@Vwew0U zcb?B0-g6xkk~~-W!D-&SY<}qee1SVPMlLGPLop>abqc6T3(`1(vxXq)sVqPGD*xmK z&hw_4E8y7+E`+y52$(vnZb$0B&J(@&@mAh?@TD_9Y;d<^wHzOQ;*0V8h!M(Kuq4P9 zk}xYUUX8KYvRNdb7VSHuJ;Yn&)eb?PH$?rpql~p=Ur45F)%O8SF$A>TV{<0%$>?H`pM-BK_S%0%ia#r+NE))-GlRn!;q6B<|k`E$HL45P#%zvY4 z;s$t=et{n2*da2fhv%_{+6Q1{)()VTaRo$z?Bb9=sH%z*ay>nOW;AgK$DjnNMs9+% zP2UZa&yHukyRY7GSzp%3EB>&B(jgb1&_V_N3`tCe+wD8mx6hu2aOE?iQ<&3SxSFqx zZMGS4kFPV#rTzFX@A*~bh5%!#xytZV%Je4dPGn43r=;d!ilJNReu$$nCR zW+hSyo8Mo#RxG@VFoGXZ2r8F#q6y){{btxkMK2Bjr|V+gF+Jnb&O_}qJRzF(r{E*k}H|(dqMdxfyD7b#v8}g1;a%{gCxU{{IB8Za2 z_2--fjr2QqfE%+Q7!ztkR){oHF2E3he|7BMolSIcB*?bzZON!QVOL^Obf9XU^B5!4 zA_z*D)iTfN4IROq#Kc$1-zRQ&mOJo!_MRO3NEv^}BVe(ZS@|mU_>XDN_a4J#i*}wV zc{_QBxqXrgP&T`$tQR;MjPZG97ZEmh>XUDJT50c!Zsp#AQ5kunNPU7IVGe#hM=72O z2AT;}DaqTwJK=pu3j-SZb<_K-EvFrOtdXBv9;shCtat)ArnjB0tf* zD~L`JYkT^s9Y0H{gZb{W00p!-gdO`X-33_{x6_8u$($ax_wDs*N6V@e% zb^}5Ku-!kpKl2w}|HP52hh(7o86Nqv!YY=R;`}XLTEcX9CdA~WE*UmSMqkXOGa=@C z?i33r8?}u8LP~V(qXf9<9Nm&oXxfgQ1{yt}c=!v`SJIo189zd(CWE)uzvC#t1jpkv z_!mCA2d_U)2!22cSG1_Ko?+s?G2%<%TRTx?(heIe2hc{dLy5@?pT24sdG5r=dv2@E z3Ui5PY%hBq*2Tf_^&-wCiq&tWY8rEDUkfaTg(@o-t4{RKon0HMz2fv9d*0h|Az~~; znFuYfY2M)2`OU(~lEIMR%l1RsZ$m6;hL1^-iP+Cu^@|*E0RR%vNA9w4l=bi^Q$(#a+aDNd4i4tfx0M)7kR7=?b zV&s*ws{|lKwnl`=nAo-<1tD*37y>Fqz+@umwtvClTknu%aI;o%K)U_L7Mpud>P>c9 zk?DJwn3J}Pn*LcJQOKcOm{>dVcq+G)>CsVNLt=pD3q581*2#C3o|}#yo7+V-%Li@U zL^b!LM~BIo9?-1~J)o=^O65uGfdd%FHhtRvVqSI`Eyd7IbplFY!GH|O@;a74*wKTS z={~)9=;NXvogXU8^TS_{e7ouuh$7)CJ`+$%gc%K3fg0u^HJ7yO%$G)g&B@2htg*2w zb;n_10?d0De@V}#Vu)e_TIt67^MMVij*tQ$8ow1l@AZg9{j^Efz4GLgVK15`aD zp(b>wvpD+46iFh7??E)A-a1^Jz1IEa+HLe=#va^7Gz6G8Li?;Ec+2%W!JC9*_b?kM z=o0}Ty09)+5J=mA#1h-ikr&@WdRw34AXjLnH0UJg$>+=TMbNE zqBEHn^=bPr9QwwNhV3)1Sz>o){1jsPf0sHN*kM$|1F!puAp92+-mfxN;&ZDmGLPV% zSAxs^e1yD^Wcy=ReZMt5Ksit4zEk@)7a!k$CaBDdn5oC`?i*L%FhiuO+-H(7QeZ>R zX2iv98bMzr&q;C1zUkwotgQE1JXRblSBX%I2MK*f?cLUAkZ0P8PAH%V+EfJxa>t0C9;%fHV1GBuy zv6pw|2&5FfBXKSBRqo&22HK=j4)MxUdzzD?V{E^qJe~in0K^_HLKcFa?$v_CM53{= zdJWIFI}O23xBM$g&uy&jLFUEdxxD#)ICuEhFQ0YNNX3oPR1b5GE8D{Y@pJkGi+PN* z%8S>eLWvbgTTa9LfZPqj?ATaEI#ia!E{amnyy_e0QlGA;8SBqTX#T8GPIlRx%9rn0$6F_$AZi#l&I-e0M+y0LgS0N18Cl= zy%)mN+y@5uaA0oto8*Egh(j8WunwRYrNwQM$-wxID(cm4!;`4#I%etJ_xEqtQ(;L+ za-E&Se9B7SZ*ApW@sA4Y1QcT;Xcs!wtd8xZz zFhYP`Af+^5Mq%#qR6K(K3b3MYvYhy5frX<0=qIpBdEC8QK^_fnV+w91i6n+@n>#??w3hnE1`v&Xj5|oa`Jx^!A4pyA+kJH3PFBiUM^d|Rq z+|DwcoFu~L8ci~yJ5HTn_Mj%HX9YcrGEnFxEq2vW*&0m5@a%dAkgNhh_x8mv0vwnf z5*clc`B~8q6BEgWVbsXe&lZy=c{pLyO!W}52Ii6HkEe&g_eS`b@2Dgp)##yFgud{n z8~pn_Mgr`!h>X!+726J5O~h|*EhPXi3bFdQ&f>8B;=>VKQ(*R3pv$Wti)ne&$9FrU z0NLa{7|sdU9rLEWS{UkzVKyldy0!I`?JNgJVj@Q(v#gMlHeW(qY|M}OAFs=f@{Y0% z6)q~u*98XudYJr35sg{>zUZbi+t$;&gW38j^tkP7(AK6`--$Z{6aMF8&VNDm{B$0c zclh-sh#sgy(h&&>*RNhh-_RcV5T`%T4;aIPI?EVpVmT&4bVqkNDoUzkLH9&@i3lwq zLH`Im8QE}eVaU(m#XLN|@INBp90u<~Y8h>nI#VcJxYfTVkAe*Ok?&P95rsPm&r3=9chwmlVbLad({tq6fa!jhV z-yH?lui++XXPB-b4O@4Dp04|ew|9+vjm;k7WetC$3I8{7nAHt}Z08rU24%Zm$Bxfm zQI$z}=BgF=K6mcz(~eC>aJCI8L*9Cn2Wd(uj`77jIXg#yP(fm@o!vzt zRFH5+3@@s8$3BHdchz1ZEP5U$*r>+Ow3BsmkUtSN%U9-U5o2$^_=O15Cp0}V8J^rm zpz|rb&!f|(dHXCorJrKc7+Tn9N#Xa7VM@?Z=g%-=LQU%s#N%gl;R$s3L-sF9VI3*D zL3LU2taE0r3&Xd=?s=O3w#l12$;FxjoT+ckcrGk!}KP)IG&x5tHKk^8udLgpZLP zXF@Tth`Ap?rwkI&c9MYK;%6=)&@0t|kwiH)J;pfm7OeU3my|=_kJGV@4m_m6mg+6J zxf0__omvG;ZW&JcwSLJrZG82rQP#O$vHYCSNJC9go^H?1o1#6Fr3j6=D;gF;*+c6_ z46~gjp#dnJ`=E~o4^)@U??x1XD{H=z|9@I-CNd0%SSwZgQxa0UitzPUkL)gtx+Mar z2(}uz3Rz_tHoqMFwiGg&+gWx-juaHQ9O`kKe*$9}#t@+PAHVoNldu3ryo=JgGuL9B z_XHO-uzVxx@Jktf0tm0V@BR~G$+1V=svoNm3bx2Vhfk9jZog@__i-1zkLO`0382XX zzNYJ|Ot>bqzEDE!t+CEy0*Btb5mDG~&z7>Ckuf_?8xXKJYq5io@`V!ob8}CTQ-Wt! zRv9VygB*U03Ngr6$VD}nY@L`9rc$Jk#Vv7!tVq-1f>Qx1nvLjfy7P5A%-48`|HP%U zu&)2wEB`}31t~KKx&nu*`?K=%y&|5o(^RWPRg6z0pUS&<+otK~bRJCG-TW#F#Jt&< z@}AwsI|}qYN%GMTA6m@pH(V(EZs9i&$rc_f`1?39{vKZuZfM+M#O@s#FPk>)zA&P1 zC&4mFLrnBWAeC+(JNqX|LzAFmQqL;+!UG>vJq3h@U#Mk-4j0FnI|9Sqm4}9E4Ge0T zg{YmKsEdnfii@a=-yJNtHI}vZZY6B{+hN7Q-Hmfm`MR2wc{w|kML@{{pG>p%Hd+lK;qrp+cm>z!5^QI!1AxzIR8Lxc-^N82ZlqW zHSB{t=|=YKNOiS-gyy%=!%xhF7G8a7Qa>RmW2Bwf(_cR}0CsCXYy`+K=Y-3}KW}ag zuNiabDA#KJO%~Y?R)4tF8}TfR$%={rg}Sm%?-CX69Vk8Ot5r^He8c4}$4e0QH+cVh zH;NEr9q#d!5uclyVW0cKa>sh?z?%}6nEcVNhEWl-vd_Sif-7Yj?05wQ-IP`JfmX1j&piEu4enMdmbVDhHbs*Pi4n4FTD1%|)E z3DefS1NC`tN3Uo{pO%-D-|^ZM2HHbrp241C=0>@$`}mQb@|WoxwMznILcl{=sNiJ# zUhL`jiMKW+P5HEd=?~>{S>cZg+pnklEH>QH$)2v#0@iO|nYm&xyevrj1i_O3XzTw3 zDmakppHv-|#J8W1z&kGfY9-~i%Hpdv6D+{+@>%1(xIe?zkOIsaFxZ7g3c-t4j?ny^ z4%v%W+^I`pFR~p<=Uc1T^f2p8FYHe;XL2?g4BhfvTr(QD-zLA=$G=)lILA?baGNsQd}HJlgwjKlw!wZ#cNvXsh*0+~8frTc`HNmd18#0`-(PCn zqBS@Ub>1(2Ak$pSSD~62BTZ>O2Xk+=wK+)oL!KJq=ltNDP8-kP< zsC`GHiAzn)b$mHqQX9QJ%b##lt~pdwgE#NUf37on%;*d0!?vEBRP`N-QCjN^{oNV`|fkz^X*@3)S30H zXRSN0`?}PEE?-O%p4B)VY4xRdGWIV$@v_u+9wz|S`fp0qg35j)a7(ymVGJv&VIo9( z))yk~zQ15VM>l*pvM??IGE(GI0!B6VM6LGTWp5$c@_|p%o$&EYkfGR%5OHQUB5YSB4f0=ga*#N!+#Fn+O0qi`Cmgv;|H@m7l;>6B zW#+FWzUHilVM^crc@qk5!Yx020cGB3{2Oq~?s)LiCmaHIg|~+N{rbfvWcc_4V93q` z2NBRLIu$?wQ{5n|CKcnsHMN7S77?H?4HtwvxS7DWx3 zIexU*NfbPFJ+^fvtDCTcxLF?GQhJ5-MGt) z`J5?c&Fnu?tp~3r`yv5&qm%X?z^TUKf;>dD;>kb|WK|;-f6`THR*VY4#9s5e5tUtM zN~nlc!%dr@7DL@~yaD$Fb=KXFTr?Q}G(4o1(j0@O#*EB@cYGiHr({>aPo;5-W5+KFiQsocNq~ z^xTNbd~)*j?Kp}DVci@ktV$R>c?z2i#kH%#6_2LB%jOWm%E%OtyY|iU^EBtL7GuNq zQcw9)i}<;;uomXFzw}^N={qP<4W#n86`m&XXzJ0^7ZnNZ??q$blP4sYZcfhc*=SyR zK}-(KQ%fVwCXD433J#|Xf{fbe%O#2JC0g5GgNp9tJK~$rb;;Z$3Yzl=S%cJ%dw9l^ z&B}F7m6`)O;&_;V?0i#A{+{h26)UUwINNOEC1)Bc9Eh6!AimlffU^ITH&dlW)OQXV z!7nm~1~djo6|ui+0m4iNvDcDDAKq;{{K@%7lrrNSqV!YGz&!|M0_~Zkio-|1&t*gP zN1)sL76oja(Ra)JE^TYR_~mm22?HylHgDlR5Brey&kJ?VtOe8ACI4hlo0hko{IQYIpx{Rf(WW*Q8-p3~VYoV;=&4NyDs)D7j;GMRdB#1k@=OD6ior z{+SxLt<#Pg_jFH=A$Y-)Aixi&)P#&p+f(PO@Tc|Ka~K_9v8nL81@(CH&et5GLHLAp z-qqziVnR?4K)Ao*)ZJM5!-83l>JXuJfc}U4>L!qT;CueTskmkH;Ok8Ub2#O68CoqR zIPGq>YEF02yP7iMPkh!!E8cK6SaYTb?$oKc_CwFMW?mwe-NR5bF=cMWGw{69_^A={ z#xDAwUP}(b{LtX`jozw_N+r2D7lP;3VBQB^IY#A%SmV-6p%ZAWSXlUqZyn(OYls5O zXdd$ch}Oa0O?z8B5HH!=+$<}>yAGK+N_QS^km`*L<(PYT3O1;VIj?$sk?WRHS38c*4_(`g4nzO zE|F$(id?MvEQ%U=+~D%(nhTMFD6|E>*R}!k8G+4RK9kCThs-@k)sXr_R116ePh=Nc z9>0vg#Dm=o{Uh?g!i253vLx4?+Lm8P_h|Un7r7amJFmQn9*8i-^QI`x4|Du8?Z|+T zs_!f=YP7eQhpi3rENYG)WUstqlz6A{W$h@Hy7IIh{ zo)5Vn=1b5;xvWrFF8$uFGYep3LGQ+LM37UR-<1o37`3WFb3M?KqUG@QQ%=@GAy4`> zQ3PopE@@-o%M$+C$HJ>$WzS-YjBV9C=v5};8x3u3J^XuI_jJ$0Zz1;y48+lfXp6M^ z4k%9p_)a%pkEI=AhtXkRw1?<>7}P?<#yjMt9g~ECno3hG>PC3ZUhnx$t3VKCW+w0Qst@DRm3$Cerpl5g1Q~R!e$?`F$g2a?eqJjC#e?gR3B_gh|e zqa8z}Fs}$5q0-4>4sGfEr-v9uQI5`e9*IiUP#8i|} z(>Rqn)*u(p~s_csk4Cd-p3|Q+M0rLL%tTVDEyr!3zLSjr-L%R4xcv@^&BSQ30mH6o?PbNJjZwxck(I zFklt0Jvd18bZP1NrC1CY!d+>;=}HzG@XWIA)Bz5%RKmmn#(Y$O&gMxL$2?g77fjIktIYx}H9*Nr5y2#{-TbiV?A2RI;Lk}b3v z|14mZ-z&@fj17mW^wD(x$Z5L~NJ(}6ov?G779ENdI{`pIFpbDjF0r5tYNASDKGTqQ z^V3RCP^NBSJqmw7s$8Qxjax>*z!xcMpDFyE&!b?e3zENF$?K~_-u&Atu>lvgAF5Id zK%_7rwAW-YGHlPwlVoF-eatA$!X!jsH~vg8+KPjH1EVrZDNSA>6O2!&uQAaS zcT5Lu;vRrLjb|kdrLEnwRiII=Pqpuc1P?~)VkfF0+C&1%M|O2 zlhxueelvE=d6R=XdARQ5R42jT0t9xvhEckaDlEXp$=q;F)qa+euLj&c(NSdae zkPqz#Y~KHe`|bC9^pk&D6FUG~La$fYv3=*o<@ghY0{A$O^P1%y?#Z3WN&GgvZn%_& z3+dWq!^h+-e~t8RmPoB%R3mJyXb{+it1!?<%K*j3&R9_D5)%U|ioUlN=M%-<2Zpqk z@!(C(9cUC9mMlpRDDus_@$s6bl1h(#jWCueTgtwqyvh>O03L_Ww?F z>csxUa)7q20OY6rf`D{N+7p5YVb$w@xC_jVmD;{YYcfP;Hd(d)o+tk|lL#JyJdCIX zj^xCp&R*W1;GwaI-Keps@SU(f^}z&u?Y~Fz=OcKk*szq9@~TYwWA5Ltyv-Kt=?z1G z}S%bu%?U^FguH?yxUs`nvF5k?N1HEqQ25R{PHG9;Xh)RU-zHF z0Od14k%Q3myAh3B%)kh(k&=ynzib1lm_8xYre;ebyn;l!q^a~Uh{f70fyf2_RKN~cLj|w z*MgkA-d8jzuq~jM^hVQJXPkkJj$h_h40X=GT?S%=Xg$VuaX!Au`*Ag60h~kWC9vfg ztrQfC`xQLMvZCum@pQ?*k~K`FHC&Um!oT12Uh`Mv5Dq}dXm<&(rG69<6S_vUt=uA%bRZ2-DsK)xFhNk71&YkwGiSHk227dm_qIbaV2av1V&|J(Tn zsLN)NaL-`#sc2&Y!)ATEHcTL}iXm=lqX#nrM6Un@3^FG8Z$xh<_=$xlNNIdEx^KUhl%QA=LaF`s?5 z1|uBSyPb%4BA#I97xdv|Kv_6EoIz1By>L;0FSOw6z&J4&)<5Xq<{RJm0&whq?k=K+#%^Lt{VGFhyKHVQw=fAI;a2Ig#{`~Jp`3(xo zOTOEzEt%Mf_jukjjnt?|THuY{;jZ9(4+a5?zb+^$WJZKDkdjgq?%l{q>Wmk3`nbtg zUpv@hK7QK{MQ1_|?46#1G00}@WhrD5Ts6jTzN7U#yS0UQAWcP?x4z8kJ7=`{$k>e8 z(2UvCh}F=%B`Hdq%n}6iD@H$LD#;Z>Y_HHmo!($og=@k^YF*wW#d|Z+-U`_xlE&36C6Lr(u0?-8*3ef5* z!?%AMo_AZau3>M0G+yV$;ekTC(N7RI(NJf5WIPV+A8BU44Vr$S*?TPt^-n7ZvvJ=q zHJaq?VC<_hj}6Ok7~lk5iKk~fv5WM5)K5E5`qnr)X#3WlcF6RZ@^ojj)(zT8!3@G}4dOuQ1Ap)h{$+h+TB(b8Z)!I1Mjn zOy(5uj{DD>imTVG)z=#Xk!^Aox~x z^|oMQ3r^FfFJv7y{ZNPzWMCSvL19wioK9%xE5XUq1s>I$Ah?^D^UEF%qo%b~g01;y zBun9ce_T^{z1T`m2#m0g8XpzjOM#0GZ}mijarV8@u$Ohvnwm!-xcZz;&zLH~y!w-o zr(oDuq_@~ieI}4m=$oZ!d|%3pl;T7f%Li1j&GL#0{HAPhEK94;%6Nc=V8ni76WlrWnTtYfxqZ)um?D2To6jQSa-DgF7yb&gSy5{3{p#;P-{c z-KfPJ+?^8mt!2C4l?(+Q43h_tfanxeBTlT;A`R2pbh)~ zJIPRYTkFLXTxcK`it?5OCc3h1h6)inbRky1lm;53A8Mq@E?v**CMOs7PAO?%IJ|FE zv3o*}`>CoM*IOD=fyNK?DkTrHBJ0;_5`O-fLy0Xmk|!YJf9W zvV(rSV2+kWz2ZsZcHcggnR%JLjUA5`H6ax`r#U9>Fg_}a^ZnC>ckFZ9FNY2eZa}>O zHQ9ghSBgxC3dXH(c+i0r$Ip0BjY1TdK>hD+Acl$oIz;gos2_nzZF+z?XIzX2-ob2TwMe z{9&%MC?tJu8-H@X-W0dJ?PR;|ps(q!aY3KdaPA^~+!nYZ8#Z!kWwOcMem|fZt+F}G zPyY_$HFw9$8}Q!VU<07W*1<3LK2}*NX#psDB)`|5(RICO*k9!c3*%7A?+gg;Qvdms zfR-jOnHMjx*+#^CAIa$v?fj3Z>vddLMp)o-X<;(c!>mN~9 z*RpK1+l03}%4Pkbj+&0Axv9M^szWMD-^WKE6I1`>MBmZLprSJFVAEpdPqdLqoRLwC zk$Iw#ajcPHbXHDWRxVdgPDeIVQA!;PGl$gc_LO@ZJZ09vTrRbU29(U-1Q|Rz63qz? z?heyGvnV|bG?)>o!o+a${kQ1OfUC z)!KtiTK}n1&51|=j)k3X);^7mlWuAU=;4~lm+lHG%Yb{bXf!{7D=?7blLVhR;Z(m> zNik3(Bk}p~;SYs7mr<;eOW7S8z;l3HX$1zGy_@}V7SkrK=w(7WIq3kO4BGSXj_E%EExZmx}{U0B~og zrP;KMYzQ>eosoy3S+=3RWGyhuuGv=-xuH+_BAw60sm^BUgbSK0P?cTlnCY*BcU5yF z$OWLguJ$lj1fu9*=^g=Krq_c5l2@X>rG-ois4Ucd1}LyP+8c0&haEXe0DRZ|jgNgA z3;yg`JR~Qt^9BG#@FAYr=aqsGG#7ODm5hKB1ul}_08nSub@R>LQSa2GFtKrtzOoZx zIxVSDt21PmXWsRLfa-}zlM$FLfV=7a7&eEcmkuW18*Xl(0JZL0?%rVk&|vTTSb#&^ zBK2~GFf%%syK@Nwan`0L@a_3v(!Qi5hLzP7l9S;h>ki1`yuBB{hAn_0{u0}mKxw{p z8Zj{ZuhbOFz@RprJHw%J+Xs+t-dtE@s9P{KQS+Fqe}LSl7-}gWT&1JNt~S}yJV<2H z*6Xj=_0q^VB*FhY)3mpsTtH;a`vc}7Xi!^-JYt3;%QnBk=D*ejJLJT z3{3h&N`Yrct`|cZ=(f=5To80sRJ5(3k>7x-kg1EdQ9w(|cz!LI+8s##?`w{av88t1 z=I5WeZ4-l&9}W&BNP>)hwtyBT2P8*zNER%4iws;BfHHN5Fb{DOJR!XWO~50Bk`UXq zsoiSDfwe_hfr0r}%=PK#XJrwaW}5jlf1c8+lN}D$xLDLsxLuBxYnk?jQI6=iRWFA< zE49tJYazmWS6eqm`FZ=k@=Frbf!9xB=}@xm_LXg#ZoE; z(K~h0!>d$net2uz67Os<>Dl>le}A|wP&x5ZdVQU~6^|ofMCmxN*Ba*>w(G0yECU0Z zrt(Q&c!HK!2+F8xhx(gm`>}2oO>_?$#|^H)S~!Vp_w#wORjanM@+J=brGuNO*o;?X z=lVCzd4i%B`C~I0bI(gN1c@_U+`>+v@dvnoQ*?=q`VBA{k*?Gzic5{R`4OMd;$k=8 zS3+r+a2Un79smhXKW2slprsXd@;Rv&csCd4KmQ|_;`RFmA;mt)MCzEvqmX`i;`%Nw zdXL10yjkM))jf1bKW&mf^=@FBDXU3O?>t%8iu{kOcpE{<)4{wI$80Y0=Lh0hc>MSb zFwlT#uhkw`R02b>)JXpX{1L$T-hh(uq6|hb%eW7Wtzy9~G41&g*?D%Tu+ONwyIn;{ ztPb!+ZNh4rA-$uD!@_TEk6+I<=(g=st)s(XxQ~!5!qLkcw@ckvGh+lIT8ioj2z)nbAd9WzD?VG z{(J_5l~Mop3Jh(46e+uN6R_EkdhKFf)#jixH#h9u2PW3hm8GR!A-D#XWoR&$B!kQ} z+K%4>H#RlFl6zI%5AVfS4X&WTq0;hehNNJ=-BhF2bsg9XWnV5%_0=ON^2UM;`6U7V z5D)=8Yn>{$nm8HI%KMK!g&@t_GbXr?bJ?8+9GHIwBQrc2ctaEvMG;GPTOk2=EUhe~ceh~VnH>+XC!03j99 z`aLBvFvBv?Pf)wq!}*UhPjPMaK%fLRP=N{^86Baa!mPC+um%GQ!Nc~Gf+q(}JUr(| zadC;lq0WD_*T-6~X&OxE_;m$-7@h)Y$T`r0af($L9}RTNHB9Q?2K=g!obq8T_<=r{ z3-h)IvR)dhk@I-)Ezi2kr{rQG(bR;qbWyuLRf|i1#kWJBEYmff&lC~gN4YK7udc&?H(v5n1|P%4CqP< zc~-&@*+h_>YygQRAytLoPHCXRCTjrNXr*gyv+K{M?B=HI>ZUA-45rP3_d5 zP2r&_x6<0nH*S|kGw3s6=jRVkPJ<5*=~g#|N2lDQQ4vnk^MBCGFu z`uhWeWFn)YbmN6>&KhW&)7h^L&VuRDx{%w9rKT$&8rc05`+DZxR#78VdRp;?0h7kO2! z0!kh~{@v*Sq^pZ*4kEjIKv-_5rm6k5#`%Hx{?|`)(Rmk^mD{BmLZq2Yu_1XmeicuG zVk9dn-p!eGE{}H26<6{Yu~9aamY3L*FFvW~T_Zr=z!@~y*BhX;%ux>9k3l98bYSQd z1*r$A;SrHwZ43z7{em~QQMocKhRffnI5o4z_RPi6w`*qjlo=OykPN!P=`Il4IcqB~ zcjrqK&!p(VG~g9n7U&C#djFf*RtbUlm&{B1A4j7jDj4)Fqh4Sapqg}Zo{Cw8q@v>f zdh<8?`bTu+g9B6oN|wlMCd#$#Z1JX%Rn(l?sjJjr{jZ%!NdZ&PQxCnkjFVAd$up`) z91`NIGu2=Q=SWA72F#*d&qbt>Fjc+3UR&A%0|1VXjc6&Wl0+0d$yD^HQXkA1ERZu)Zfqnkm4H+zkC=Z>_(8^d^ z&}kbpZ}1rU%?a3g@S?sE_xKEqOuh-zQuSUfD7((9h;?7&olre&#q;PD!UIyH+<%sX`SRwV-K`TtjbcV z*%%Nwgn(qf2MjRNGY_C6vU9qOO_4WGas{}hd-2}d6#vS5mxKe`}_4>CC9 z_HAT<3w1+ySnGW(wgr}(1%L>csQm&`qQks#Rf9NgA9vMx{Qg#qz$p{}rx4IfUAYE{ z#z9&Cw{K8coLoZCpe?QUu>RLZeSr)PM-xH!_m8j6KmN4`1zbyDrhMS5J})E(6<0_g zeu7q*s#fjR)AO0~axhw9p{stu{^e2a4DKMDyPxy1-cV9;hNAqu$#_cZ+*b1|_&(%|Vo1 z09B*ehlgOi8yg?qDS`cWX%yq@5Pu+ap8Z1^+npY#e=X^jjCC+wD8TY3g+;H`TteMI zWf`{S1x84&=LG?%068-FukRya`Wy8V`r*0T4sbHtoJZqAM>6eS^8|2GA#M}#t3XzX zbpZVVD|Qjy94i^Zk0(GD#Jed^(|13#Y^AGKJxQX0YE zxTi&cD*Vc^gK{8&yIzL}*Ob~VA51t9*G>n4G4M2i(jtR9HK)>4x0rScC@fqJFg@tS zQJ9CAI6B6XU;Tnqe(MFAFwY>7EUTM4mwxO*MYf^%*SyJOD>tXq9UiIyv-bAh5-e^b z3(C=RssXKr?c)jIz5%D3yDy~A9pdc{85S(&MkF=PH|*xf;=(r#R|xQS@90<FZeWvs{t5`KxHu|E$5H| z3|#&znFcB&!FRnbFAp{Z$^)6E$jUD&Ymz@ zV_Pw3Q|a&B!D!IMMiKBL7v*O7j?VC{LFLsQE7RrzB8ec@&C`1$g$vcy`Jwh|lj~_y z`L?RLgOAxBgr1%E=L_Vf_GO+9OUv%%DwYW#V&iS_;U}jws4R9^_73g}N=wwx<$c@= zRFlzzPvWE-1ytj0Y;WV-+-(KvRpUs*Vo0>!MmLyi1VeBk5Xhb3y9iW>``g>oD3N>7 zg3CB8q+PEL9|954#*H`T=bG_xK;7VTx0!omZ_Y7>vYA;mss9UP)K3j0?s$QuF8;<2 zC|>s411iw>Je`T6(R!NG_p#5h;f9siaPS2X(Hp2N5C}HC2OYG9?MWz>@{YfMJSyxp z7!xSHL?QM62=X)*9lSi0!6RO`nf9+)8FKm^E>E7sb0KCsla^|N<(FDeVHpfdpo!ai z7ZJKKy(R zEhq})`J)?=5UGlW-;9XRsd#l-C^gN~xuct_YJ%#CU1Xwp`;+Ev+U z=JUasf3`1)w%Xv2PP>vd=+#T2j5!AWk|jpuKB3nj6|}%I*roboa`2s(*ISVN_sfPMt3TFoU{6bS zdiMRIIGsaOvyPT!=>lgjXiJTGnhkOy;_fNr^iyZP%aylgG+uCw_~??hh~BL6E+-LJ z?jmB2Am__$&Ay-05c5=q0yP?J9x!L?=yak)j(}Y*;4g1O`+ExC&jM~99xDlV@2g`5 z)%?4Hd5F}--(xm!%=t4&30eRZuj2SAUhP?=<)NVKgr2-gqTEs_aq9%6h)bjMD5RC1 zl>&$l_Lfmt%7KBMD2igq+Vm2wjO-X^I$cNgR5 z<``>5g$)jBnqf_GwhB`?3@{>A{#b&yMl-&ET>NPjD$8gEIL!TrS)4@iCzI?0IfR6( zgZWBcS%Zx7WPJOh@ZY%ci;e6GX-#VG3QJqqqNLFDG|m2uMqNEE@#{DEG?EE%vXYYz z7|tnZnG7Fu-nTNpWtxIZS&yG7xMlUhkhsHJ|3PPm=dHM>ty0I<0xY{kAE6k^fgRpF z=61Y*Hkbe7pj5yb*zeP@wjv5}a8lO^z4DZx3&XS`Y*Oo4>4p+%gAuv5!g-wUJ8{o65rx!@lU0zL|6%fX_N?A$k82x* z&LZh)Hh zv_4{6*!_gK!S@9fCPaXum?}>5YXp@g(DC6z@UVnl=4dPj*mb~UaTJ;DK-xactQrk{ zx>%OYJ2>&(sy=?{gI|vNOz_s;Ju6G>aLKd$P{ibP@!oXO)4LYTC!u$wD5wddAE;a;H?sE1{Ju}UwctYX( z#?}+f<%0?2``>r^X?m_fOQDNoU-qUdshXhY`Do#T%qBH0PO@0S4ypKL+{;YrI~<~{ z8DIna`R-!D91^~v@gXR!f1>)kh~%|_qdTTFm&PB~mXBasJQhvtDx3T@f%IHZ$>jl5 zG?)hp@DZe4`ZT<05Ufh;bGwY-D641$|~>96(JtKsi=!SojH)LhDV8kIy}}>Lbn3Y?DSS8HgzS_%Uzp*VoVbKYyA}^wAx7$vA-)G?wG_gulM>;5`Pg zwD^C`p|P1QK0G_;+UZ}Sv5b0vvwI@h)-SrgkLLh}JwtnSM-^6*62hKGM7Z#AFi&=+ zbegiNCSWEf8$PFuG_(0o2=oPz7II6q4kD%A5fw6dk~R7HXU}MyP9Tp&jzy4sXTPtk zl0{6G(`pm6N8Q3Mbpxwl)yx~h{OYTb%+wL$PhF1vCtZuPq|3=YM3ms+db@H=cI9mU zj3Q}AV?Ly9H5|=Y{6}MW*W9lzqK}y;aR;YK>=BV)^FjVqS!v?7Tj?Y1MoUR~I}TO+ z4~DWQEh>z3wWCD?jm2{xf5RLN_GXo4Jn9aDk_J4iqocBWi?*M5g zY4}olmdopX`6AU~-h|0(qOwHNOLrA_n|BxL1+1>-8ym6aWpyy}q%H3;Hqy6%tBHdX zrrgu%#F$0JtQw-2DRPr!d4Ow#lLb#EE;`;ggYiBHhd{XUUWy#rV?eu!iFLvAVL%Klys^+EB_Hg5TU|$shh8^z8Hq``Z}7K85%1qT1N+(v@{wuPj~ls ze*4G9yRlzz`Ns1Gv^V2V))>WrTf)M5w@ zX@mZN{a`=22}-$*H%**VLR|de=_Tbgck?S4fYf)Ja)gRe`*)Xv+8@Fs|GHR@E-&s# zmlk9-NjZukLjqtoGSYFtC+K5Vt!@_CEOiF^)(NYPPKigz?#t=^FU*CS6RxEGNgCIc8(H+dCftO!D(ehLtdFX-NP-U zi4sOoE;=Nr9-F2y$mE_?zdq)VSTSR@S4?;2cpcWVOJS^_96c);@v(YeXoHH=IaKv{ z>{UmZ@xmNax@cC=TgDBw{Bry19kU=ewl~|wKEgSQf+QK^#OZuLe>1~i@8rIetO z!FR&*kcQ1b>n zI*ko#z81q3l3I~ueh+xU``&Wi>4=)BNhlR_^KZ(a+_soQxpr2FVlNcMTBZ7gW9xba z9-mf=n=9rwi64fqPuVTR(BVuatx6|xq<6-K8dQei?=l3|Hr+RW-PzN z_d^13M+94<3SOvCY*Zg}MA$M3a4scfND6>_nIDz3fDrb=<+> zqM`BOp^{Kn6Yg~L4FOINnz8z?F?+4yun=|G?>awj9;p@m@lL|hS+Hwp$GK!q344S@ zSxi6+vD-r}(O4t;DROQY#HRvofpapy(O|$MjusEM!#eX5A^LG3Goyg(_bm#ad$4niYT7mKsxugux9)QY{udbY+p(udK86t)){lMCbhAV5l%A5PC$_E#SACD zOdd{|NmW>#nmuAE*)fXP8CoULro8ZaT_TbCRi06Sux>%AQlZtJ?*jgMJNospGO_)u zbVse+lXEIco^08r!b0F0^8?qI=d%-<6 zByK>-t-`u!xOj%&gy(bxr*TD=t8gGU?v@$vquf>3)Xp(uw$Y!e&eb36dj#y>JGM1X zjv72zE9&ladi_VUfa| zJ)?^4#TeV_?fkEsKkp_O&QR%z6pO{@+3xO8^c=?Ty4;M*BSH= z1z}W^_^#|dPT}J*+nRY5kfp7G7Y}ml1(e8N@E;I=@7HTeFWIs}z#WaSvj`FMgnv-e zrmASMzxVehmI2Ukc2L|Zn<4DgSKc2$nC&ZbHTwiLm%?SE{6KTHirT@NV`k`|r%t$& zh2dulbs=ND>O_zOJm@H#{o2=@&J4xCZ0bC|M{^-H- zF~@?NV3ynIYKoFji?gZI&ZW!gsN1Xk(U!`5QZ0K`{{bEr&td0ZSsFh!HFekfB?4$0 zNL5}v(4%t=zeG`V@BCt0D$aK>;N29lx&hbddNd4Fk6VcB9)Sg+JE z>AKS@XV7rUNVQ%4B3W(%o2e!FlpYCBed|H1t%t1+zrLDsHYBIVkQ3(oY5EdIetc}4 zRi2caDOAQMY6Wc>nI zL$JGon98G^ZfG#Kz@uGZmmkZucLmO;SOKZ*I?UT2he+tVuh>^M7v#J7<9Yt9zMrI1eq?m6s7CTx74ytU zFlVrNwN3M2`DOS}PV+`Zc+_K7#f)pp@5F!=(5z_J7$kwq1Q`_RFnReTxR&OEiBb|vKRU!M~idJJs z_7W{L@ZGFbFbF6aBNrcVM>DB9o-x{@eeb8c8|OrKh!Fzf1ch2JWCk_@&VERCbhyeCmmvj?20 zz+IyA(dNpF<)w_vgUtltV~3I0=GC0+n{iWrj#|2YcxTThZNk`?$tp)3c&VfU&AaMl zSR0n-s2lsG%lNYyJ!h;=5xp*DrGZiwB^_P-pQR(u*rhI-YKjd$9ORCa?l-2TJ+R^eB@ZPgVya;O z{FNY!w1qh;GwHg@;+L02JN3lD>xztY=EP(RxwJi33Bd`pc95MyYf59Vf5Bkg|6*&w zV&g=vKLJ+iY`YZG&mrSN&7^ZU5!X2qrL-=iE1b*vl`i+)@5rRF;)RXi9aut_VCGu^ z``(H{XFp_=Lks1TK0fIBZELIIzpkO`(osoKD72=LyfdjmPo+|mw<N=-<) zR1~%`S;$&R^-$-~I%g0*B|a;m_J^lFLx)6}(EVcWvi?^2=0gw4e2Uc6F5XH#KO*4ATI{zTi{wmSZU z0o{kjApi@`O{B;Kky1AQXa!QVPqssMI|gICX9k2Ya#t zoRF%V9>$$j!*Pl;z=nS&c7bcj2{~ky7KO)2tP4@qENro9ovd^UyIKtuq*)|P+GdTf zAFzq!j^vS05~$TCzuv$fb3V+T*oI}=9ZvXl_@}mNKs~7AnCIYNC2uii0*vXoIHQF|k zz;eoI6h{YmChg3V6Ua1swVF#wgoCg)AL^z?{DO8TT-1Epzn}i|Cp|rVdV0EJbxl>3 z`emKT34u^ncJ@a>0**Wbo#lp#9d9zP-P|+S>SDX8hNO^R9}}(DD*$2adnVMw%c(v# zprNS|rtv;DKFW=rgIP%Mz6@v+Ws@+`@6Yas{}Cg_t4m>~jKs{8o?oOVje4M@U|?$( z(Sy&6p=`}FYM?*Hzz`?SidXH@l(CrdS_gB#t3~?v@2Z9aGfn=c(oQ>fap-nT^1a-j z>BHk{Ej6OA`0r>uTnK-1{^MV-w7`v#RR}>zMU~nmpee7Y$j}h)$G4|;Zhtt?JU(>G zT6Sc_RtZ1%;zP5G+eC%-=47>`q~zYVjD;hy;cu_`~7Dt4f>6(6H%sYF0=%bvcJK`VPIfTP%KjhGCY2~ zu{N?9M00wu3Rr+oR`Y{tgfGqyr?mWnWor`S!@}eg74U^yv7Zlh8&XUF)JWmPm2o`WiBN!#WF;v%q)NK zvp2e@S9N^MAT1Zp48H~adO2NqZB?fyZeuFfg`rnx|M>B{vs1`+rT^mm^l)vY$YQD{ zzvcu<(d^*hfaxi+M$C@%Y``*Bn9hDsaMIR@>uSw0Z{_SalFG>=qsUt%rW3GdXOzus zVUuFad)234pu{;pRLqbBz7c)J2^oWV6SB0$+=YZ(rb1Y?|FzNfL`wZ3?L&|U8#b~~dRadrQWxISW zV=ihJ^iB7jBrfh_Zq)NMZKJKvbq_|rCEu$C#iVB4Z;#zS^pNDljFe_AleQ_pxO{Hw zz&_Jvq3wcK;DZqmJk&N)s?nxVj~~hF;T5n(zKO|tywhAd(2J0RM|FEdp;BTY=wG& zXR6uNo-c+nY-g|kZdtZU&0sDYDAoT~a;Ja8r>yU#`sP@G)>0I|9I~3Re(bo$lv!Yv z=k~oKx4Iatu{ZjiV&1^A-0Rn2S$!M;TrspVa zCSxrXV&dB1w_vhH6$DO{bVr?-axwnuM1L}*zKm`T$7NIOq<`F^7pzn^Cw z)N)!j5#A7~Gps0*tGEcV8TkM)TA(B02`3 zWp{qigSD!^DG)Jy&O{l*EkXt6$d;c__(-hGM`;!8`H}t@u2E_7a?Z_B`wd^A+A*{( z6^>{M@N2cX^gof}f8oh#d2F_x?sBeBc6jP3f1N#l?hi#*#v-6S+i7Ft=jUfl{`_}g zUei`W!D{xrPGXY?rt?o%EiPo=~s#|5wp7*nglg`4Z23g5*tbQh!7Q?pSG$Na zIrv{LR6GU+XmaXo3fipHz}^gCRfiK_@i!evCu#@Eat5hV8j_5@P>m9YinZ-TnM+Oj zvVuNExL$yD#gSAES{dwL3k0=PP&Gmo0j)pjjuLYYn9(ZUMDs(+<|aO-w&7q^yyE1R zEPWuHjT1ai5!tt)atQZ$?YLYte{Mug8E7|MnyIu`cI%Qo@o)L*4e}k`2}f6SZ{*|} zdOa{KU(cl|H^4)lwX(u&vdW~MlVLZrz)1|YwI8AIu0Xm^=(oKjlhIV#gpN~r^aszQ zqJ6cuvs3jzTL>%+;&Ih=@Cto?6FzS~S>-U-gm++VZf-v486$Sf_ggX^;pn}w;&SE< zIL&=il6&|C*rwFnu)|y|)c^SR1aNYPfCW-q7gBL*QSs4l!pDUB+UM!sVxgGH7*LTpsyo}X zD5>ty(=hY-Z?b^^gmBkb2A+mM54ykG&466gt=FGf5TBm|DFpC*cm947Abcam__rmX zqmZ)T{H?M6&xa|8kI_Pt-##M7@fJ-MAUSus-X4I0fj=taxvD!rHj!WU+^}2_g_J4$ zuMtCBKh_Mu_ZO33KbcKBXr8D1pAR}?Jm_-S)_SKXLv!`fIDQ{4gTY!K$=``OB6!Sf z*N-eMx+gWbiK%>*20x0tx_ePe0e-mp@A>mql-bNfb~pzJ3+p zS0WJbP#|`?`kRpdSJjn=vze{&P)k~rj*eVgu5EPetyVKyL}Q;8?X)FC2&IbCf z=RGed2_#<229n&wFAhEkg$z4}WR!M<{D19jpbB_e*c>w47{&1}=+ogI*wYa86FBS? z2gv+2t`^qtSfe2YZhXz=qTHw7?^!cJm>Xf%KZ z5{VUHvQ&DS?A4b=c2dqXW2^7r>#;OSGw$Y1M|_;QuUH`uktT_3YHFHY3Ap~s;g_SB z?)HbdwM=(i8vi0Kq;#*8Rr$#{S^_?b{#|!|p03qxK>2AdU-q!5i)?4MG!o6ST$21a z@o-`@KP@ZY-Wk^)MGDuV^JgwZAQBSjW4FdJVvO9(k7^jB1#q4;(ocaZ@$^4&IBHq9 zgf*eA<%)7aklIXCmsV|+ike7nhbmp%ke|D}rgxRcTMkuDyRQUuK9wa5-jh9ixN3g+ zMXH%sFY+t9zf7|o%eKEfu(@O_jFWaLZ);pN1stDp%}AOULn}uCCdSB7dfTWTd4l;R zoK;nvjaR!B^HfS&q-L$h{Z0V!fp?7EuHMyVe&@TSwO4LluW8nteptH-l%ZTxa9h8) z<=7d1%m;-Ks1kuIyDo>O?zjff6WF#MP3KmZXKk-sLA*IAFJE3%v=nuYxXB>-5cIe* zM#*J#*~N$yi*4@@e@IZv9l+~Q%t4vwty%&I=4`VaP1}Q3C`2zEC&4bcs;2MBPG(~` zem?yH!$0qEW0=iMTvpKi$oIn3Q9lz3J;==SXoEKP1t&S^SMSIJe}NRTyT9kmaOc_T z6Rf_4pi#eEzGD|M1|Q~g0%LuzJH>O3`;41D!`{;iDX9r8JAT@AIO;<6-JH(?!Q}WN zk04JP)6vn{+~lSqDWGQqk}~8WQ_~WDx;?8g%tlbz9C`);jc@tV(c}itQJ57Pqzr$r z&g-mS{r*P(kZ1iKqD$N7be1Ed*TpJnh4&4NmEz5{M)DYocc=WP>I5mv!=l zRs*umP!#w#Qu+_^L)@9J?XG)R^UEg%o3aChY}fVIbNI|66Kn~8c5`HdLTc+$e2^}Q z+;k7`6t4}4R*b6F_6Gnp8Bi9&5rV_ylmI3LE02A2xU(gDMW$2@)f&>t;tk#BzFK9~ z?%9p2s;5UX`|VX2?>~@-dkFO;+K3w0!Bb(!WBRUJnEJlQ94MHnK*()*!|h%qKYDpW z%f<+i9C3spsO!-;G@9P}7=^ZMgW1p)ioX7t5UZtcmJ)q3(OT#*S<4*duD9@}aA{4jHv2ow-+09X;@S6?W? zM<-O&ZH#)A#9sa=H)OpbRKS`Rx%f1yg)MIO-Sc-Y{2cgrxAUc75!(crE6~^1?{ax3 zB}3Ln8!|zeh6`X1uRB|Fww4Xd98&|8Q%4irMG8VQc|gjSV8-aEdyPS+(}mrceBFf~ zm#YklOG+je78Vi{#VEb?N1#v~1t|*{J}!myf93E7alr6Ij$EJP^@9ft^=P_{fOAWy}G&@2y^r|HF~}Ht9iuYMQ3MXx1<@4N&Bs$!g{sA(1;E8n0E*7JF9`h zb^|{I$v8$va;MtjUZaD6v|`ik@%ELMR;73Ud)aKik%9!U$5IF4nIbM-yl4$yGNLH* zr2ipO>pPO~ltrs`fQL1JAilzrf0HUmVeoH416hMqp}Tt#Iq$cSP96At(N1u literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_sample_old.png b/doc/qxentityeditor/resource/qxee_sample_old.png new file mode 100644 index 0000000000000000000000000000000000000000..1eba8578dc46f353fe4ab8abe3ce0e9a0aff11a7 GIT binary patch literal 271629 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 %*vy$uWxW-QSUevdcPH5=9&XC0js0dMywXKz zDEuEzddf{Yvk_1E9+P2!Lps$biV;_H@1MH^x{QlLR6N=ej3$-1RL8KEN1lBP) z?r8`FA3Y8qhZ~n%n3!adDCh(gT8}8hgA_9V7_NOEhW~`H|Ab)e*<%W#%}?;?aQ2}j zBqAgDKu-E0lQ>o}T45kgfiYr;A$H_f6iY=ki*0w>A2Vh_j@i|WC1WLD(ZAoZm$}Fm zWnxY#x0`?68_RUV2zrKO%ivl}_BJW$y;c-O2x?PjFc4HKh8&p{Hp^A8VOLyvY%h{& z(HUfpC6#ko00InZ0*ns=N!s6QVPXmLqNpF}zBBO1wIaDSJIcDmXzp* zdxmdqYh5+uZAN`j>{}QZ8JL*rU053F9qXQ1gib9liu4ZiGEI;|b$xquKL+`F3bf;s zfDYhnz|_CZB)E&P%?VBc>zz-4;MhFx(lT%>Jg^P-#oS%!L4d@n6p;iZ2A~md-Majt z2k)6zlf5mXT#Y>G;Zy2mNq&~j<%l77>Tq|W;cfS@j#3Ay}N zzZ8^H_PZ!_1g$y}m3s$Vlky~7qWqn#o>;E=t{%5CXDrzY~9xVT%QtpU3@^~Qn zn;aT*{MC5BR|zsd)LZZ62|A<+7!9V5@=1Xn6?AAAom2Z%-#pa9xMonvPAXPQ{+o`c zxT@vm{M3HnHud@YsD2#L{Qdw(fPT$U3SjmicqKp1I66g>nxul4US*u2wj=9@_ zr8qhj2KUKXQA&=|5HPubIUc!}6TKnsyJTHn;2Ea!`B?bss1LFw`L9Xj&If2>DihZh z<&GUF|IDDzr2R4W|(AM;F}cyu7AV? z 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%0Dr+-`&GAJkM% za(N_^C5G#;053@owPUOp{cz(f6KVf(jWVEk63y6fh_sNaMMEITQEJF6LWUG(Nguk} zs^xQ5$v7`;=4eorO;MdFhL9+S6%LtXflA?o%G8I52cKrdFo@X+aAY1kj*+N$7%;Su zQ(F2)44N8Vf1<-wjvvYXnM(eSzh0*^%1S4yz3Jn?BOT3^E-)w@Jg;c?)JZZJXT9 zLUm`cRV8puG>_C!q9a~NqpOH{s7-WF68y8&B&S6jHFN=`CP|)s6By@HwLs#7baW@h z`aJrqJ}wdZdp`i@RZ%d{N9|U-Hgff1xhj2JmeFcC^c>gj$@tRc>&^LKw%vFDg!i^h zF??i?KAx0jW|?IfX8||_DW@&)VZUJEM7LhOi`d$ceKWjZ16I6VP_SK4+}YU|4^MP| zP+qYJlp3h8FSDV^j&>U2+U3C9nNqqf3P|iz^{6e5EV@eVG*OTOa5*)C<1vWD1Ram^ zGerKl#~zv8R2`F_S5IqZKZ#JKR4Oj)!lsepzMQ?!TY|~0n=;=^f)K&%~-T6@<-d{GrVnOjQ1r{WfHqL&>46>DlThVDIld2s@a65 zTg%&H{SO66bN%f~ahIWI_uC6j^gXU40AJhxO@ofXz8B9M{iku}os_4! zY84&YvTRS2kK*SV0t9?XR7wZ#Mg~p~9WOVHG-PWeW}7h>Qd#IHb9ee3hAs1A2tv|u zR=I#uc$i3XUI|{0X@DKfX1ZZ(c^RUial(vopo6@CCrue9WJjotLgoB*`fdFY$)C27 z4PL^vazalWV7$buT%5uVl0;fHw@(}rP-L7IbWSS zV*c+xNn=ZOyRhF3u8ZB@wHFktKMAR$zayxjmX(ot{X#TX#mNmnxO32@JiI^gteAa2 zKK=pd0hC~0jsy)E)P>AteU^RrN>yXEdVij5T~bEI&F$jp>(n!rJ49+L*Kcs^+Cd-? zz?C{9iE;n{{=$!A*OQKknb}VmxeE&@M^})S2MA$@+{p9IMVeRIFhwr;Q)?KePl+Vp)3{|_4Zc8TFG z-M+qb@Jy;8nSHu4)gwRE@rSH^fJAVX;tjvI;%A#9id}k@O!Lk6y*pToix)YCC?wi4 z*gZq*3a%*$A~dKcceT3%gossI8M;zKR#PEcA0^QpGe3WBXt>6xP0xK^mP?C)X0F|R zHdHM_fx5#UEgKsh9nFbEO-i3rb|psY+!)=I)?W2^=B_9rh5`tF*S&cuAV##ew+D`6 zaB~xk`$d2^?zUKkv3xKH=C;+9TlC?gL_S6oVEmq+>+(r4oZmxAk)B@XZ{n2uFgZe(x8d?B+SF|4 z#}{fQg~CF;Lv`UQ)$WZ){l=tmTS{0{PbIv3H-%wZnX2&eufpG0o+T`3sImx>KFL<%|FY-3r z2cN`%+2b#LR`lSb_cm4!6ye(Ouv-Wo?h|G*ML~}oWB8Qx2m&T3ta+l94)PjTn+Q-J z8Pv17?_&|t+T$PsnoJ`CblCXBu$M^UWlY0?uDFfgmn+?^P^TwG3`1C}ZybAivp!bE zJa!8ea-iHHa=>e=dA&YG72$J|sLV)DS(uaa7%13lE-fRV_iF*rJ|pT5FVSiRaGJ$< zA_U2wW*;cwAFxGCT;AkVLgM%lfV5$n{E(KFyKi5Ne|?fmS)t<>t2Z-oJs%LxB~7Q1 zzsPtW2n6Hs{LV!GL-J1qAQPL3eRk)Q`(Jqj#B4*vXYL0zFkx>iPwglRvrz^%rkk!_(8A<6 z9+WJ(qW9oTi1+!XYMSjso7}Q1CYm&Vv3LT@ckClP#18ceKSs#~X1MDlBZx`U7(G8x z>u&X*@0fnS@$w`hfUeZ4dw8gLaJn2vwl}%Gpe8iqBIB_7TT&YY^jo~xl`)ySk{%oy zI^1u?G1daEMKd@qlKolWH?~obogDzE1jaL}nuy!Y4&2L50`7hM4kU>G;3SdwwUv#> z^;EPoHhSFF0%0npz#%M3py{&OTP5gkOxnnrwyH@w)LdF$C zBSL_BPcM0qgY&ctGGxu%zmzzDM?Qa3g?oin z=B`;DI;O1HUr0clkiE8kupt=PDw~{?^qll!kV_$z?PkfMiz#OhU8p1=N5(v>4I4Ds zbXF@|>Cw=PIRkYU*8dDqiMkp8dCS?S8@_c)i|n01gY4pQS`cNB^|z)~i}I==NJ^9ZO& zQ$4rwMYPXg;0`=J599f1yQHa7E8%u^#op*2^FY(LhM8hUqV|C9!cwYm}@9)Y^3Va zxJUMY9lnPpAKq-d>*y?mefUu9#L$wd5SWpXRUF-gAv&4&44tB?EKT8bvAZH>4;o75 z9J%NG93YZY6bbI!q3&g>y>&IH>DKB_(e6{+UMOiZzdT3)yF9dZG|c(hftS19`KW+X z@B&EaP0KTI6aoDrx>{#B4Gp!mwLsDh{=+CM`jj=h2JiI3Js7iVWm{<767@g}uvrGr zqhZ|^vzgm(vqY`cBusO)UceK(cnD9;GK{a8L(x<|>3 zl-j#bl%f+*JdvIPboT7uFVQ&jbR=ze=s~)zhvK%Ld(9_hLj87*sv4HFodju*QW^SH zii#P0_<;SH9O2^+$Uv0QJILYJrC*=arAW_Hl~JXx=2Fj&pX)V_E)ZTbZ2toAFQSl! zuNg8%M7pp7CXLFDXT%`dZmFtuew}Xq_JVc!sd<0(M@{?RzIna>IlDkJua^4qe3`r5 z-3lHrj6%GG^f4n0Y84!(u^S=~5HN9XuaW-YwKM-&V<5fT;?j~Xl$Z_g2zH5~41>Kg zWXUZo`iBqjebXJ^!Li=V%4)3f25f(@5g*^z(P2YYR11d#^2o^W z=@Lc4b1EY-xK@B+CV$>IpBB9Ih^m+Ium<>``Uv7ME)rFGX3mMx5Lb#$jbY8n85>BT zsHlmAGfR5I$fr*jRK3*rKfHXxKKs$EdN{B-zR1#@^O^4?iS+G zrw+p+ar_Gz4=(nrDGDF7M}R}=+CyrjXi1hc>5LfLJFG;tlH_@ikv>F|_V{>;E~)Y7 zQ-kjt=h%IAzP}z0FR(!Za1*dQeyllMYn_pb^Gjq=$0aHz6vmQ;D`%>E?{l#@KP@P3 zAn(iAM_R_jMB8H#YoU-*qHek8-;0VaP;21R%PDP3mdR2`#M1xGK9mWkV544%?tu%h zHSNSWZ;sN|4(fJ%hRCg+#M3KpY#(d#zZ-q!0|udkSaU5Maw`rWF=p=2UBi)bloD}u(kE; zAI{G~$xDo-9?37;R|mqDyuHPK3Q`0s+lKM;E>qoIpWcRQApgh4JONJ6{Y2eOV*fP_ z$=9ynoD32bQir+m((>co6g^6bHog$v_QMBzp6NfaXM-SxM+?i$$*!@zu_Q#pp-w_6 zwnan7Use=JlDkl|Zt1idrd2ekB5XH-E}; z6vSWhT{LZ?vlPnWfd=ZjyA(&+c+40=Mj(SP^l8!Ixr4)FW;&ocu}{=iA4;k-TSv$6 z%z`l=M$bvZqJx5$)qE__mGfPuvs464)#dI)BSi;YY<)*DxTrpqR!)OX3eTDxK)`JoL})!w+&&esjUIph{VK@U&dl^J412J+@{X<}O)XG3$wi9HiYE*h+5It}mVAYl z{HI=2?IDKca?|1^S0|nbH70?ZWb38Pv0JJO4;RTih-C?idC7G+w9JHj{VF;^M6UUA+8*MX0v>w=^gT< zv}YoDRHf-tcyo@rpyv4<^;lH;33r#bsJh^cXIn69k9ua&sI)YtRNt9~HV*lab-8Tj zZsTg{{rk;j;N^|AN%d~a4BCr29OEW;*|sqU4&p#X^cO+{ygp0Jq)H8bIjAx~WTmR6 z7AMbOB7cN~K%hKL1z@y@30vN_$4DiT`bnj{kMq^i27m1s0{BZ6tUFCYu2cWUus4MG z$h6Nv$ZO_m9(~bcZ@v{M)6=haMfciP2gUs`6B^vC@~6Os5Moib-qVbe4#PWjNQNql zXo`n%$x2-%$jZbUurhpXvKIakLnziD008>G&N1)$8Dk>p2>a8F_UI|}Ba`0V*9`F9 zS=t;Lq$pbv(#O(dj>e=)IZvQ03pBWUEqNQ^YrO$y3rL^w|E3c>^@XS5L{eXu+fr09 zwqsq2`u7D>uB_Y?Pid-WA;*5ikS<|9gzRGy_HxX823mL+xx$YpusCju<$Xl6`0*ssIfM4R^%Ee~nq8lP z>1n(xEMFwzCGQXWA)`C4_gnG$y`*%jo&>*4p>~~&GjZRuB>05F6iVL349c_jHlgd) zn5fEMR`9l=SJHf4ftyIsNs`Rl%n>F)Qku}T9~bsyR_vz_9k5}6@(bW5%zO9jkJiD1 zpVn(ZQQv?41~f2jskywXcTg*ocqTa(DD@@3;1mrIb$saRl3y+Ur%*>gP!LdlYF2Y? zWWvwNjJO!6amafOP6H}xTWva0L<~BfHCqqS;cU%R{d7vv=9}5{gVCLN1~*#MvqETfR%@z;(NqtNt99A!mE1sAId&Une0 zaq_H?ID%MccvN}aBpwLg-H(~$sywKX>Iu@8kLUit-=pQG^s$bu4F7`aGZ1Q@gmJ{uAXyFD+WilW`0$tMQ5Bf zk{)#fxGsQ9`K+2>$A5NaEfff$zf($&-Eq;Uz14U;g>9(nf^^{LN19BMj2SW`(R3JW zNQ&ih5i2L`n>c^|Y5O5)=lp2-#TRy-%6sPLKJG*qTEejL?7BMOc;(_aZI>F}U@2fP zU0_VgwxLF00CW=4h5|jAEX5?(aBRM z?S2%v6hfd!_&AEFr60$v9l3)1u(~ZPJrpRphi%+veXTxdJucnEvoBgce5Wdo znY^!wMk#juXfNC6tmhD@BPw<92+sV8v$1iZ!Ih$z{e^z5DP0WnL!O_~^0!yH)dYEL z%LHiuwc_hQU-ugYPEp`Ef~|u+pTI#;c%spfVXH*0O61!Jm3;Mw&Ga)A(2}ohrdFSG zE8WY(Y@+M?x-p>#Xcu>J=M0Z_x|%%v+xL2iPd;nRL+haSGsZV67HCxYv5u_BV}%v zpA0&!sE{Rl`r-PV^&Pjzq@RB5GD_qP;v;$L)sG1f|Eh@RlhWqGgt6_xrM9<|sTWIuYOZEDd? zG%;_ahrwqJ!K@r0kX^ncB;&!{1rr)@YQt+{5NBi0PEZGIIND@MewEH`qt5GsSYztC z;gf8qZ3Dg1hmSo8o^+GJ=IEP_#;`lS)OUOtS<%Uun~{XM&e}W8jyJ~-_S&f$(;|tP zAy(5h;Ab(uzBj>o$BoI~hdJC+mD6dHX}u#W{n6oVxU z822%eq`6OhL>RFcA(|NBQa~8^tq;M=>$e{mNv7#K9@9QJZy6Lz)1jHmeWFAzsS@30 z@vAw2l9Cb?6}1*CQ@!aL!;Ny&?d!euCS-yH!N2oDHim`ZxSQ=q#z=k(3kz#n!3qd` zNbfHUcNkcK+&NwA{E4BWo(0#gaadc36hXAzr*VtLVvNlil=yiNIEhp8&Hb_q`ZFj7 z?>~d`00#B-bzuKJY<1&%cX2$7*3$cn2zP(RxtU?ZCcd)XQ@V)n z75M&svpTggELc7(GmJPXSm_Qe^4F>k-5}sEsg-UU`m5?#D4Bu^6bO*6w4$>E-l zqwk%}9U4u{#Xd=3G1Yl~uSO;c4OB{<)`T1KJ@uqu#bfxOc)!jIr~M8w=CR3uL{3+{WsMGRU30gD{lsi}KdH|sXz)lo^L zo=4AgV{SD6KJ;PxGwf%&HCaQ%a=6ouLE!V94~!45?8IUZJV{Tw6B$=7Io1>IHhv;D8DllQL}5iiqFXnlZ4-M z18?|1&O8KW3L{=G;2`_^X%Uzy82j&><7{9Mml-#{!S%y68CN$qOiaw6prHN~!Q3oj zymDGV4`2M%dDT34jnm1TwOdfer5OQhp&~9`WF)zXlFnq{@^Vdzph#W-eH67FV-=k2 zn5uGSbEANWOUD>=2SG`O|#jawwS)oh?g1p{DSx0xj~ z#LUqC&hb_NV9Eb@2K>|_d*#Z@Aa{M$xuU-QYOyoM!{ZYF8=U=`+xPSZ^X)#2!}vP_ zBGN)Uavm1l8g<3R{prAR5rMh<{h!uCCEHZ$OQJX9-+rioc{aWMIVqDq8AULp5R(?J ziB^XL8US}?i2JKyx4ITLg;0ge|NSh47jn|1=y-nkP)A!^S^MbZgn)n`GBQ%X&ROMo ze@GTW_e{k8>fU8r!Iv*1WiYMs*PUyx8nnEQ1#9c`_Eoz@XFGyH+~HArIT8tK1&VDJ z0?IZ}$2c~~mJ~uA?VAEV%ac2Ean(?XHz4{H_RF-|rV+6K>Klf=> zvt|k&Cos?u#Li&g{i`mwit}I3-iHl~Oik9jJ6LUMl2rGp9+2ULc=@JS04h}pT;T$= z#CTjB1NS4ss;UrIxGXFz9M=0FH0#b^xz2~h4T5Jcmw7*j4fj%|)MxjFg1pSuHw5gZ zsi?BEV&PrIVz6ebA&7!H#S#yMR3-hxKO(&quDl^) zPjf;xA9dFx6C1hcs;B6wKbX&g?~2Z4>tpoNBYW!RMhJ7r9QOg4;yFY#9^lFTRkq89S1?kR=&VW!&F7 z(Dft}mZgOZyxJE{84>_22d?|>EA$sC0yo_ zi)3HNQmAd1`>_yf!KJ6d&fYgMuyCHAKUduq(ORW$0yomfy`;WQ7&dSSn>W**Q` ztczLy3~DIrnkcmT{6#x+K937J{L|i|KUKFno4;za=jltIGP)`&f>>!5A)%n$8BL+? zVOFDF(-}@SbN-F5#q*yFU-L6_L6`K>;AuS(a3iOS?pGgM0j1le zcCjtQOJOdz@pL;wg3{W$J%hq*$4`z$IVk@4_@0M{9~&tbgqxe&(a}+-%EsEp#>U!O z$Z`EXSs`#CQZ&Lydod%FWnN!eJMS{{4`OOO`XbJq8caav zi+@d@mVXq)$unfVr(H_jkmq&9^3cb(gv33@9emd^Fmt9{GTfX4Rp77`55=7W3b!i%kKy7{-BTz5mjOFT7Xxq59j=2>%F65UCTo-cl*uEA_X~BYFyv zFWv2$>?;LfSNj2Z5;6rVhF^o+*EDF|`Zm8{Rx&eeafV{af-^GKC0 zb*Ss*=d2R>ArmJZ?Ju1TuJ5i~b+0T?ppz;Y;sMxkOiwvZ+W@ejT32o}oiQ_WN@NZz zGb1VpHpbMuODsbg9=D6$rTJEu0egmsq>h=;77KTVIJgQ8V+Wh-kx}+GHkS?73j|1d z33wn&QyYi9cu^Br-MyZValalrS-0r=G;7M)GHhcnN)$HCH!E2_`>uoH*Va`N)2V|( zRnDm)zC{x^ueT^zg%zf`jN@B!GoGbI#u|#xKj-tf>5BS+1|`d zmXz6xfF4-UE+v&P@M&vRiALG#(}G88chikKLmr`BtO_lRqB?3SHD71iLn_cO^5m@E zYnLnac|X=C{$|@!a@jh+Un$wC6?b~%LZ>*#vQW8S@A?&v?Lc>H{D{oKT+qs*>T%uj z<*ii|`;5p<%Fb94X5o>LKus`+*cKKRr1>%hFv+(76M&TwZrgYtm7%TeVBH;QQs$HH zk8hxk>D16x8Ex}^cnHF=-l8w2@ZnickLe7Fpm$gRbmez0J!*L$;p5|Dn6|g~cJ&?? z#B#K_C_g_Ruzprn0C;i@MZ`st*(zV*A$6Ph0@2EFB4C<@gOxa#zo&V`SPoT}1lrOm zequg>Go*0>UT*%8#Hp^8M*trx))z+Gfby`>u9Nj&@gS$$AWOw)U%n6k(X=X((71Y) zsM#ob^7kR1!`eV6#Z8?EOZ^8f7RKnU>iW$5ev`)9PlHtM%jY zybWQ-%x_X~5`y{Lx}5iGPUA(*VTVsjz6*HuoU#>8s)ugbnFmB>m4B*wYz}Oi+sGmd zUUM|#2cU<9Odm?DbIEYHICOY$5Qth4w(%oyHq!Jah7{IA{?U@!`RHHcK}m%VbVpY4 zF`8N>UWWfzZLHZ&US#>)sQ*o4#kQ&Mn0MCr1GnXv#nc1p_+ELCHTad)(ldOVd6sMQ zJgo^@EPbI#EhH-Qh{w08i$ltsIhe1GFq6RuAZ~}iKZsCkiJVyv#RW@9w@O>-01yZukr9}d9~Ab*dU9lNaPY?ub}lY10Rj2jnGYt4 zL%}R4kwhiZhkGxVOtsy!0AvMzX+m0fu+rB5iPVRKtHn#4U+}pAkKOjv>T0#hsmu0M z2P5n5Y{zOV&ch|6mWrGSL_9@{(9q6s;L|<{GsOn|X&JL4cmM%7*ddJ%3ab%bbpN04 z+y{mmXAVo!NsfQ+wl3315e8zbswSQxKw!~ei|qaIxSIT;iMOz>D9AG^E2+q81_VhI zX97E}aJ<%ca;NWV*R;908u|6@=l9&8zE6+H9vybCWt96#JW&$0c=^IBClpxU-$Ct! z=6hSL`>lvz3kY2{NtpGxpbQFii1m+z&u6y)V;Rm0k6S4^=+Zmr4iA9hAw30VFgzc{~}wksZ|KH}q~ z?QZ&*@$K98b>Gt`qdOaxuRUr@Db}p%pxhF92;ciJl>R@1YG4yk#oR)VXMdvCwfs_c z$gaF;d!S68hut1gJp6~fgkNs|r$++Z4)cq<@Zw+hlm&t!flJmoW;FL!))uFF3Gm{- zKGX$cd-FryFl2;d&2{Hjbwxui;4{G|%Y#4{so^L>fk% zWeOmpxO>%~NXf`ncc@l3ympj!@Q(@HFMr7h<<=+>;NqtUPkZDnG-tzsZ@viT5a4jX zIlFm~L1^mmWO0ss~*7ER!F{(V*7b}Ko=7PECxY510pg^e!sj~v0zE?zl+^=M&@jSY-iE`sQQK|2c^hT^*R$KzdLhxF85bH6 z<57?LMpu&1+&g7ufc-1(V|h%RqlT2}D1`~54sWwv^R%H5JGw^IH;$Ewb54C_KSZY+}~%%_HejaCyG zu(O{3i4YIx<~|F09gBV!n)NuLdSq4?lo06lWoOJa-Tb1d4bRkxH*LBer`urT8za;N zS7$#Ul0z(zs9BLLhX-4i?F;)?@gJGz=1sJx*F&`<8h7%JKPqS;#&t`){HMMDO|AXc zv%78B$D+R}79`R_JF#(A6@YG)#|4(rC$?Slscwx!d>dWlb6V3yJJVJwqF^%Z4}75L|<=a_HHoz~@)v6$`|Gt%6D zp=x=y>LLCx%o4V9tx|4>fm+h>q=}Jq$!M%s9`*yzu^J7vl zPb7}XMR{c@Od^MH%P}W1rykcUt@^r1G3~>rk^Pa=p>22>-Q-6W>D^dSzckDL4Nnk=Ipmx&myp( z!<>V|{1fBc6OI`NpzfQa4N9B;QS_ID)9Wcrq^UwwTd6Jl8 zQ;=qqo}_10#38YnJY!!-96`MK-!WhzBz;TfAy`Ea2ZFKwi5LxS@)gES1i2MWFj5Q~ zq#}~oFRLv?*qYWCn-@}_i+L_^p@^IIsKsvi&_Kgf#!Km>Bft3l!NbRW`mf6EO!RF} zA}FI06mjMwUI4_Rh-oxhvY>Ya$ja$v5pYRtnrV`;zRz?2rkY%;UVe3(f6odI7%c zy`T&I9t&%YzF0SlAq!+-<^fGSGe97t-DjGaBGv+#rnJB+-Jz95NAT4#T#yk|TueO8{nYA-?Gjz*(4?2(K(p5eL`Y;tI68?dqh= z&(gAdx_X=6G^Dlev4or2XoKZJ!=Q%*FnJx7|Hs&S2U7jM|Kml7ilQTXB#~ogB{Pwt zl5iZGj*$^Ev!i5Faf}cViDMiyd&IG4*;`ij$X?(3d0Mac>+}BnexHA=I-KWyU-va0 zkH>Z0>aS6ly^-sPG3Mb)Pt#@#XYnj!?I)`12jH#W6vB}?()OYzLTc%a)E#ks1gOWu zZSf>!LBjOulj14*{`x!#5osb91#pdrM8@ilQ_NHzyrpqHIXsWy7gA0D-iRxS40?ZV zxB#Ve_K5uF&Zf>j`i=y-jJRN!okBauz$NrRSZ~^UsD*KjI25paqK&>o1Yl z^})@uR?s502yq^xSqprO1r4Z?Ld=q2)-Y-$_j0^LQMyY5HsLjt(kfbGHWr7JeiaXw zQS7#enD_+#6Q3QyMJ;zs)F*5noGJ=DZe%6OSkH)>G)>!cqE1pzU{BM{PrWL@<}ZkF z^Ih@pi~M25uy90>x~h0sUq3^oqvUZ)!((w$RIcD2K4)qgz#M+L!_ zh<#lmr-!4ki^jvbj#D?z)giIl5yJ7t!ChY*r-;vyfF+G^pg?W<)z;F$H{rBWd7zXu zr>KCp@7v-$V(Hme`2MHj$DQK7t*6m^_3Mf&S_vt&)E%NG;b_dl3(iSE3 z5=;7)bFR%$YKA9g@CPm}=A$5@Q)1hn2a)jXA+>sjaJyI5HS1W9_aJBOnBn=dp2Yh4 zj2UKYczlVWH+6}}&8k<*#DLp7E(u|w_I2V%;ubC-WQGq_P_OQ`wuxIYl_ zivj+s8&MzcKbx`&9rrz4fLPD6#VwCBMS`tJV-h55VezoiIM-ROeml%o+b-=eWoi(t zP^}u--~#F|CnCqyck10`+~SpFMOErJF6i?x427W?hH=WPOQC9g5$=8KRNk!CIS(yq!yR3(zN3ixb3-!%h?7es6YIxOXhs7c zx7PJp-CbaS9r@C8IZK)w70XqKlA|n?{<>$1@DkM}+0#v_T;o8tc(t*0$dGba>NdMsnh(wJO^*)no zFhjcZRDBh9x-^YZ)JkLun~RCSP*4|O_oUaN+xEKj_q#q*NOQ*= z#lNR2^9vS{m0_<}*S{V~U}^U5#9Mg_sosd+LTVZb5nIMjSfi`?FS${Ui``770+ zZB(O!zly*0@wvJbJ5wHSe|EYiTGyD@Ptfhh^Sk=*=^6wOTXRRZc4Y$7>SOEe{ z;GLJB5MpP=WS~@_Ln@*~!OTK+{bg4g`WQUN+CX74Hz;M6r4G3rf1}=-a&PFm6gA{_ zzqBB)g=~IQc2C1%Rqk3?j+F1W9n816xUhI}q(#j_3=Hrx>c`8*8|Je70!%Qy4+hLm zx9N;bA#>wkvze8B>ZEUe4@@@d*eD1j7Vs&$=|@t}FaFX;4g?9ug9A`{EZM zkoX|%xX@3OoA4~Ql_aug_Q)uc#*TLC>joY+@XY=dr;cYnjyjb0+6ngResEDi<1x2s zOewsb{>PxU5&UHj(s^jB=lER65^)gvV8UVMopc@7CQimJ1XS_l*DkDRgedD-ua%1Q zjk|HbEjoCG8WZdZqe(AgRn~LLsx@mWM|fx?c%Ty8Le$Ms`C`-HrBhcu!@Kma+y+q3 zZr|zKdiKZO+J>+;3(=dx}c3m#=BoXND(mtL9Y4P^LKkAM92kAnN%KdK?Db$XP%^kYuZ4W8bFac ztVt(AbvK(5^lMN(WgZ8y4z5LYy08d6m3tb-01v3@ASWWX<&N_ZyF^BweHn+m)?q~& zEjr?wgC0^yuShpfx9TQYm}@gUSIETuwomCip?euyN(V}1fdpj6^z*UN&O-XTrWk<(KSlM;8)c3sECmQ1XIZ5rC?_bKMsk8K< z`&C#@2_vj4_6xut9kgp>7ew^aaA~#(BZTVH+|<(kk41O=FJhfj68#WkuM1G*BVbv- zdG3V*&4H~}5*U0B&ESrl&y-<*|J8u&GqQsvJxApcl;+}-e|#wdE#^;b?>~Qr&%kQ( zeDKcuTKHQ@|Q5;hgu@`%~>E&ioTsvM4O`tQ0(?=ck9kD z$fCt@05M00f;2caos1tb1^w2)l?46?{=#{GO1|UO{}+6si$4AB+qcrYxzgZ*-q-43 zFJ8P*cAr&!d93j}l0cEG2ib8teN}}WEFT&;&u0;kswE3lg&@hvB~V{?O}%=-JmA4xMh!U3N0CtN!m$K$L6t5Cf)6 z;6by7;4Y*Viv6tt%p8N+)vUL}gcou}K-d7J-T9Yd2UH`98b5)w%i!RiX!P(0O0SMYfC6e*EW| zg^Ku}1-tsme`6W=UmE}=r4IlU4+LRcjeh#AJI2UxL60+lK&y5@*C}Wi(k=%+)?ksU z;o4&OeT47_2XB=40v&IPetTz!keYiGcWv&TZFky}yADA-E*t66)CI>wFNKKNc44BO z?7s*oodsHqTWtab3mNfN&h9H8Kfk;{g;rAk!;$>0`TzB2pws&ce25;Whc^|dpI5?h z(Lp-_lA~FfXV0t@E3_?$nZH7YCJWX`TBLX_iK8E}Us|~Fp-h;fAY)JLwro=D9iV@} z>$ALmJJA2#uh|=r1))FdXqz;9*uq~wtcjm`5U4zm8GFfY-clo)dRpk=34&Sq342HT zb5gEPKkpvvj`nFI6N)$ny)RNx#1ZM+HAV()6haRb8m+hQlgCq&u-lJH2Y{pt{^=+$hGa!57nsuHeXvhr|AT@gJOlJS%P7A29rqY}j~eg-Ya z4y~2^pK9U~l;eZS_}@SKxmI$=RUcsFn7Gwny^0+_iOyej>&sg9oU~=oiP#M1$qaUE z(r42b5oL@}vS_j)n5(&(n3bJ(Z$s(q&G*>XyKd!44Oi}Og-b^-EM;jhT3vx_s|fCO zt$6OOSKjs{qI8ezB8J}d?dvE=fYZ^TB6jA^l0V_2^`;ve6;hphTgxE|zI2{qpT6rS z$Pis@6Ooq6IXpFUWN3mwo`uH4@0RK*$YRex)$`xe06;KuLhjnjz&8J=*?vBb;2Qu_ zT#iAD@uYl|x>3@h?T6i5tj|KuQYPgc)@7|lbi`wK!Ru)KG@IE+CxyG1gNd4WUPhPg z&M^%>M&||asdaI$rEh9vCoHQPqkQ$?*wBX6UXl*?DUaC8b8?DleZXP-{fmW558W2$;IVwAt8ItvKk7;zchgHB;=FWUcBkAZRC9^D*pL05HCqxRy|0Y=t|Xkrd711 zp?%9B?&`tiWM2rMJRgMaA6sL*vp3pt)gVT%M|;`kv*xFlu`%v2F$I0aEVk0H4mII+ z;0o%02qBj33=Bq{Dt2{U#oxEH%IrA(nCTd@=J~F(Yp`}g`l*$J^`48(E>+!dN$yw~ zhk@;;_L_LlLEg5Hr?*wRCU3-vP$9pQT3i=Dx)cP?u+i!IU5T~)Iwo-Qe9r}!0ELb1 zQ_q!Q5GRK}CVwK-KaA}c_L@BR)56qoYIN!Iu;k*9aQzFZtG(?3^MR33eM58_8S>MwD?->?QduN|3e|<a^|n;kQ*0B)xUd)^iw*T0)#RzWR##MxlC zjv{V&$8KXbwxgp%(n3$db9wu%@EZBo@(U*WYRx-$-(&CJ^@o43ywk`L1*dn|JgYUW zJ9)nkHY4fJkcJoAKw5&(l16o01w{_N-Bm;zbNxtIGJWV(!Ce*xuS z-gWc#{0N|o3NAsnnGVEAk8FM*YT?_rsbelLdfZpNV_H$M8C>!v!o6^#P(NM2;ANT9 zVWx)1*B^U@bR!-}UW4lO*Pqz#NoG+lgjE)xZC^hcl>5%4n7(V#$pqkJuGjb~{eP8q z;;rFl3Umibfo>?&DQ}Rgyn9_-$$P=+V&VmG_>wChFywxzLb4l<8ttyApSa<8U~W{$ zmL$%^3WEQ}DE)M*DZlCD zw%Y;-Fol%i&Pn@-z%wwJF6hMnr4aDB%3n{1e?!e@|M9*8QuR_GRn31>i2tRpYwVu| zsna^IHgKHd(Gm(}xkx%MlD$?xKtS~%e(T2}#IJrSU}A*2FRBfg@0-$})CIRc*4Fyv z5^?s}Gd!b!&)pw@H}XJJ+VC3qY^hm@y#ltNQ50brV!Zzn{T00WrzJfC`7SRtuTF1I zj}s^_a*qkzGAQ(lBXMLc5uY~GqJA;F#wV($b$+22W?>_Sdck$&-?Dbw@Jz&k zjtKIw25)7z^&$Mhd2Omn>~R#!{iEVwEh*Q)vNdH12ELFxDZ{KLn^J)-e)7HZ6K;d? zl&8+h;+tZ0dQX`~&)4HbT>RS`yPn>fIaDgNl?CZcz+|}Mu@5MawogHKfnI@NVeys9 z08rq{2l3>5@;rmW3~FEQ-fz~nGPMG_N3@OlNsm=2F9F2j9opTZK_HLL78HeEGGw8- z7JV6ESq>;5qG0qVb{o*RV{u8KvayUetf+OJDR*00pfaE&O`K+L!dg2B3$ALa)^;9d z>M8G*M-fR2_7nqhz{0bmg%-80Bu)rX1^7Rf?|&4rcLj`d;o#oTkkgyd!r90LG`=wsA)&!S$wHaDAsnN!VWK) z!7t+pA;fC#MZa6JxU?S$e$Got`8S*ozd0~xW$5Z_NUNWJAr>(y(=9-HpZmTK&q}S&N?h%bk>Q_rdCe$7sKWXET5CN zpV}p9oCBBEt{K?9zQI*{DSIKUANg@wUGuJLCGt_^buUaU9l z1BVrdVD;2S;x;PDKpJ!eK0&M@is+W0et>yv>{tCgMwX`i|&O%%?i7@7q~a4f*t4!dLBK* z=xXl{_ZM&O-{KTrZ_$1?@FG|^`{vW0@;60OUKhp2sQPc7CFykps|aLuG$V237fNMv~wL}8w8W8Y1~8{=z5KlA}T0@i%=LNdC5A(VOUfr_-*Tv?DBf#{JOPPcj)nGseiPbm|X)i)V5EYYYhpqw6;1}w`S5j z*s*KUciW>-mi&>fz`WhE#N@oaZmRcWQKLd{XA}gmC)@P4-PRr@d5=v6xhXlbJcR99 zUNhdJ`*`*P@EK5~2L$?kWxbvt7|?kV!FW}&)(hKOz}5z{~G=R`yt1Y#Zm;*H$Ge14wdIe*+fJCR$D;O&KL1>MZLs#^olnP8sN0W!;1g4EPr%Z#`0>Br zYLRcH0!g8yVvW#}@#0-Qj#wYvueu9ZLL#N5`Z9flr~yf_NnjdBW54Zzo^=3 zYU&RQ(CA=#3fGRBnXWMUe%A91h2qnttGhS0^A@BPuME8Sl6Wnqn9x>W@AL%8(}jN1 zX^O^4Nm~uoiz)sf@3cy+SZQ5^!ZeQ4ld^5i1*G42nqq#G401Ww1JjOo%n78>T-6(s5uY2jH4 zKGaR+4W4f5=R~HY06?`Oh5&rf@MG2txA_lb;9&F+=uD5FBMKnVxU~%Z+s6)dh9+mx z%}kk)Ng0+$8>Mjve2LQFm9=&t@HY-jZPsyQ1zM9DN#8rMqpX+JBbC<%vd}BV0VpxP zouu?D5xLE!0_{5@VbIrW3_~!`t|Sl~EsJ93s=C1SfvBW`7>g_jt-ll$dm%NxH$#=Eek?T})$J~^=US>!EE`TB+ibA7j-db|dW2<+C ziUZs>O-xK|^Ob(UlO+`EoxnbKZ|&L3;iInvo&Cb%nx*Qfnq^X!+kAg;>WQ9k>GrEYM!s=h$_ zcm1E96kx6;r5RbdM{hR8!Rei2EHAvi%oq%lveT7xWQ;Ejk*7!12DYo0`tgxXY!W_L zsY9;p9wiLbot_Nm97OteevcgG@#;iw%U7_sivrCplm?b2WCqZHm+>Q3BV;&1K;YZq z@w!)Y9rL7*hs<-Tm~)&JQH|VAzIFCd5SqIhG~M_Z$LN2hfpbGxceBF+c8=*VLPljd zQ^w5?21dlnOVkCN+mDcO&|o>c&x!y=LSoL-XMU-i6E8lBq;w;R!Z?#^l`mfTlrXHI z4@21hDC(0VLU7KN(wgrvcZ9aBz;XR*@syW=r^8~g*N>e!Rm>%G*P^oZIxejy!A~>S zm48os z^8I$g)7VXJbKFv`Cm~Z^?h4aA_aCiCmX-(?a5^l#_ifXLk>88Cz9O&f_`c1VQ`@BX zbVGqAyExqsQd^_$Kt7M~ya0Aakp_<6l_yFj8;Kcx{@Qvqp7P(i<#VyuSf*uBt(1MF z=Se~EZuyO~p#Qge9RQ)9)eWI$SJjYqw#!jeXhN%VMX%C)KMEtbm@8c#2Hw8(GW)Ai z*39!(;n^Ls>${N@LI|5{H%|*xs<2uVL4Jtyfw_p~)xN&2fq@fZLtyIXJ0jl4W7Ga2 zA*atI`7%ywdb9eqt;g784%f|CHX9{{mEa8Ts+*rz zHYkxgM_Ldbnv{TE#=7tz@DE3+*3 z9TH1t1_ZphE*BxO$9zT0oAD|KEQZSGY({qbp>FF^mVoyj$8 z63OE~eZkPSdHU-|Ro3%2LPd0P6YLtgFKCITlJya|8-aDzD6n({uZppN?u`MXi(4((yMnJ%lAJN zYGgRitw(x#CVrbxV0OP(;&6%%E-)u7Fed<+26u^ASdq(^iQZ7@@@#WUlM!3NN>OjP zxJLJrmFQ&QWdH8eJUxoYc$no>AC7aPzrXp4Z|=J`=%{whj`x3uD^lkl4q1|bB=@$;Z# z5%P14cf@H2JZIX+zH!`*on2g+f#QEG~Joua64UGI|S*5BAtRhA$K%c|J#Y~qtF z+S?u}-S1BCmA5|?09Z_8#5&fx@b2-5$X64J?DqZp#A(-?5Gt7PZ-m1NZf!xgm zM5%wsdHF##VccSB`4-4DY9Y+?uRa=t^O_%Ht2L90{flvHj&#M(73I-W+bz0tCA_f} zHY!I{?tV;9J8my83huv9Y?H}mvaAZwk$X@6&RtgZg!`$Tpm{PoyQ0*CBl(0o5Zu{DSv@!_sRVP4+e_p-@3^TWJMoUX>2f?oIG z;{}%kyP>OF+Z+83_YOW4OvS;{hst<(vTwd>dO@UNYUR@U@I9qA9-vY*29k^>t>)Jd z%3+8L;K{}iHH%MTcfUFjnL~B!{})vo9<9U{NxnCA{J~F5> ztL~tEIji-^JC36cOzn4xs9ZU4g&~_2Uq*Ip;8scd6~odlF7|G23$EzEriMfg6j8{KnfVJfJY6@?;HM9Y;SW%Hz2` zY0T%pI<8bSSsT0Bsnou$hIL*{xji+zD^h>yymWS#!9W!?#smcm8$a8FbTAD@N7sQb zYA?XQ|KUjRa16vWKx`W)QREHY1!o)OZ?QOciUoQ}k=bj_FT69k8j&BjGGw24;S|4f zo%u>d3y1Zq+qYR@!XcRdzUkBUu%}lkWa#_KI72oj*GhnzG+Xyls#vSBt@3>E8i0#A1fCE( zrER2H*qhkkD2h@ZnCv@gw+w+1=hA*&M zB!L6G7kQQYqSG0u(K&ML@G7BXnVA)rYCe*+El;AkbMN1`E_);t=4~+Y zgChG0sumtdlOjB*J~X~6fY=ng`LK6Cv2zLQLrn)ytzWNxb1pjp`+8Wh$)LBa|NL6C zv&JaUIlAhg0D531) zd0q521AHv)t*kYyUdwws;1xzL`BE4px%m&ER?dHBq1uIqG<1PEW z73PY;prOAZ7=4v6HB3eimE1}^{=9wXwCCyFMJFe4A@B8>xw$!dCMG6&`jRIr=cT@+ z5nN~fISqgl$Ve_$*=&v#s8?Jqof=~k4h0Ocyuue(TVesUS}3;hj(zX5!TbK)zS8Yv z!Rk}#nf2Ckf!3JIg~bvXq$O~EP?^3$NW_d$9!z?Z$pH}|!7?|_W~J{){t zdD%oM1Zo5IH{gLGv-3F|rDa)imWd-Hz@;rMNcFl6covm`3mKDv!2NJ}c^M2Y0m2Hn z;0~UmVDam`j^n!YxlCa|<8ZvQK@{iI!fd^>vK>L`_y>xX$6aYK*7g6Uxk1zf133g4OKmCQa^NWUJno9h}0y>ly4G^_cQ@hHf5 zo;sX8dD?~Y_*;?(ZNyY9RD;N&;ZOg4as}6s#sytkh+(P=al&K`{b(&=1`JN(AB1x0jKP~A#uT9j4z2(aLT9%tp=7gM znZ7R^&kXhL>qz^L<$45-9GiCOAn4Fdg)=C^-=iljpFWIBPxr3!;DV-d;4_HhlCWcK z#JduM-Z3JN%pX4N&9~6d){bE!y}CLEwg}PF}1*n)xdYhqFgvYUtt8*P{OrQh(^R$)h2GvXBFMry@b~ z(;74$OU7emZJxpo9Zx-r(K$1acs~V z%uoQAnhgUpC(i{VahSo?aGnQlL^zrMsh*O>ah-a3y`Xf5f=at{cvwHQ_Si`(HmONNbit==X^#w#rr)-{q`FI@l#Ov{)g}n zazCjTFFwMkEtBJxwm6w!P3l)Df@i7{nCSY~XF`s86iuDPBq>z43IsgQcov)HC<0rmRd+ptH5O1)S_eQ!&x8# zZH$>amKzJK7~MTXdWu?CWBX(Cg&_6eLH4Np_kxJ*I)h7iS=%3p00<3!iWgdumvq(5 zaEi4Ku>Sp-%n?|<MgdfA;e@+B?)g6T!o{1Hp% zFM9jF@)q&>rDH7$n}zdC%)ao!d*Q6Fsbu%Jf(MV>vN%>>)E<>9m&)4N{jwtW?!3}8 zhIL)(Hmu78^B^a-lLp>wAR89-1njg0>D15E{*SH$(r~EU9Zfg_yp-B;4HbzjC{qwh zOiy^*jPxOSB8emUT*~zQ`m?ft&3CN&^820ia0ij);I3kaxn*1>xN%z$2S%0pzIYLi zt$xHK>Q;0q2J5ZFe{ExyCp@-EBsg{Bfk4-sZ&?&jy6|5##56iEOd97{Ct$3jAVE<* z@{adsdj0QxD_^6hTwTq}OHBJBWAr{bZ$^NdyWaw-DkC#H78ZfSq?<`n3n8c#pI8^eU`|4u1R@7~HbHz8)ML{1C%`vK6#b@ikoN=t1@q zAzOVkpQaWGDAi{1v&w7U;)X0Z(X^s}k*;9rfws-vW)G=k-=|vJM*7BzBkEF6k13E= z-Y2(qD>B_xy?$piXyKq?cv9co%1X*@i{I$Ats~PY+2@Lyr!QIAUlq@f+9iuNM#C*i zdG==6jZU$~sb_Jso_J9vQ1*6#cHcws8X9!P$#D!LYmG#pXZOcHyPB-s+5aTc(S{#? zK!XkLd#{KEW2;9Q>8jU=ZDCEz|=k)h2u zTrils1e#_9t64VHzw{s{bzBAL~O6Q>#i>8R*p;OXQunEo(&3BwXYP! z2c={BaYsSQ?7(&W?J3(}^*eV?(WOU3M9`XG2;OkH`n<^9o8zx6{uc7`go67S&liuQ zNy6SO;PS*YICe$01{yccT2uCfN@HCMB-yzOc-#@He%^K)eaOE|KM zL>52T>VFMA*L5oDE||NC>V4;xyLI+7Qta5%QLnJLr7Y=z z@hn2GjmmK?{X-|E9h}e>ckZj73pz=X85ZKU{4CdXk#=Z%f67$4aBN+_n&V!l#YK)@YUD?#t_QxI2h4ua%q6ZvUZb%zGy6U> zLqkVL$H*8*ic@q+n4g4M*PFafOL={*jJJVzyv>K&Gq{(sD^o*uTG2qOg^<9Sa(7PY zBXu)so69CPt%6QX7J{7P1&-ZyC&xp5=2(O)-^i(V4NGj=LSI+=H2V(R6ma<; zg*l%jA)h*J!dEBv%FW|kY9(w4_&($tos*NL?y1z6a<4gAPFHbW|0b__CiOifvtO}g zFDN(|sMB(GqIVI}xd{is$;)$*Tiy*6XZQ3^Nh8AY{2Nh;m!k|8Dbe{fmz30UC~vEd8j;8= z-2)`VFA*eOLI>jVdr2Z~Ys}^D8aO6Uz2B!Yb8kqt9hNnj4Ze`nhUrL&WOdd zHS79c$X?~{11;}pnk>2}VV^uQ&SgC~Q7)X6cyV2m?4gQg9tD&r()4BZ2G$;=EqK}X ze5URr-=CO|EpqYH{2V7eJ~MiXF0BLeobiRNywY#TqqaXbOR661&|qriM}IExvX+4=v9^6B(Dl6_w@Z6)q;e;W>fyF@jw02 zwfE7hEa$mM26=7S)OkKx0NLM18K$5~XSQOpSye*}f3pJ7u0d(fbr1g8y1Ii@C?C8a zikP)zxqI9Q!_v04vL^Xjtgw42c{NLUxyr>{*K zKQ7My+RlCg*bm(Hu@0j`rjCTOZYJ0J3T6QUYh)sI$L!QnVwMQ%u1+M;7e=-o|3=%y)ffX4Dr-cGWK9okS8U!*-@LCo2N#|X4t}IE*|Jmct;{Av@iwcvF~-|JE0J3aEfT4BIO#*;r>Ua>tS=;f+?vX22}@Z-4YD}Ugahb$DhReO9gHIqr;_Ap4pP%rd{K$R5N zM0#rcAoAlfvjkyHJV_#iLV>>io5%lE{75$;7W$oV1D)UYty;XEeb#F-(|Z%@;1J@< zQD@uuJ#ctc_@yhAYJDC*qUvm2y>;T{7=ycEt;pK`9Dc- z8m&2oSNV1Lh}q zfJs$z2$Be1;cE81;R2gqgIdmH2`d8jAvlfnSQ{bMl<+b_PWE3p{dV7Q=fQH5#iFNK z)3)CaIa*!oZa9mHNw|#HlXJRryb;Otl;^#1`&5?!<^g5g%d@^f zmZ)8|-7u135e|HTKxnA9QEu-+ReDpWSF$Mhys;r9`fHkkYvoig-rwh$zTT`7 zWtVI@9=xAkhayvfN|1Zb*HZD9j`E(`jc9V*?09|s;>aFx z+|8yX&yh0YrrwXLRMV}_k-RImD~a6acY64ovC;RX$mFch$YDKA7Z;=NixHDwFKHG7Y21AH0) zi6QRe<)&u8s&soaZRqk-4x!FJXoi6F!s-{6=O z$kKTPFY~(&Jmm2zR78;aahq3PM@9~Ja0Q_vOM6cOxtB6@%94wUlFihW_XoWm1-3Ek zi8wA}JBE~A6ng0MQp@*JeXsel=pEc}kD^hK$s_X7CUeC(#jV|sI$RA%BEh$y41GOr zqif7#c)7^TX1rwA!ygkKP;j{l*DcuIb=A*HVw7;?Ju4ZAxIfWZ^%x>9Mt+6S-Omt$7 z&Ci}0Kkz>H70f~fVT=-b34YpV00iXKfLM+fEGt?9Utk8rBt@zbDJi+>hy`Tcagpu< z5@v}nJtVumKSw^RXsK_#r9Sq}?mQml*h8>N2nD3eyzN06Ua13<5k9sv1ud=!NP&wk zjdLJR0MP(k`xj1IW0obJ0vwk%T`ediBz~STQI_4oOZVpDZMj)RHX zPZ9S|n6LV4xU1AM`1W*`%lQbD1$Dt4r|5i%Ueg!UGp7Ju>NC(in6v2QMrc-Y{)6F( zeJKrbOdaY3w}sb?UD%w1;Iw%x$5ge7E{XWD2V(h5F-yuVwowt~GdMCbC-qIBP`w7u z+f*2c2w`ZjBauUvDFmdAsA|Kix4y>lb&I2!T>U^>O18a-VHR9W^FX7sD#}`Ntf(rc z&gphVXwCRY|*s6Z+l)lo2?)C5!o5=3nyRy99|3|;tV(G z^oM(kZI!;HWr$--D{LJ;XHGqf(VH0%TC)F^c+lc>^3hKB$IyzeJDEI@4Tb?h=*8q? zX{<)>Mwm`PbwIgda|khNwrj8BifU_{U-0Arq3e+K<8y4CbemH_4$^B8ebF(x%_hCF zO}+WS$T zDeWl4y^yiH=wIyoVJAeog7Wes!su$dPj^7s-_KcWHy0lFN`CG<>2Y|+^SG^@{6$ti z)3wK!_}_KIxeji=ozFSgJ+l_%bdQ(|&lE~DskEi%#4cQ}ZJLr+aMFbNg1oJM60?jaQZyRFd#72vHRvZRG zK|6i(-3MSiNop@qa^7lhD34Sppvt-*-S!ND9fTJild`mP?Ha{0r$FQt9!?ePN9xki ze^$r{Wsv4|P*;H>6AfhSct;fQXW7BGxFhL+I4L%NjU#JZZbWB~i441MzA7jHgvY^w z$MFy5kM-qQIL>uvY0Hm>tY?{vxi^k;UCkZ1eK1tenv|2WS(Vt z`u&$@iaWX&RyopJ;_8A!De8|n&j@OwdGv*wZdk$%a!F(O+QmSRG z!usFUDW0+wW(!gW>70YzRG!541qP07C^5n>HCHgUzuuSABvY@_9<@Fg?8aQWLcvOi z0SdO?|Dy&;`|JCsSnr%oQ|V_dgST3ARI+zrzz8V)@DtEGd_rR=n}vX;e4y6VNxFEYK55*$Px<;+OE2aU{y zaotkX(L$P8*+J!h; z$c>x*T;^nAB$&4ybQA$pqo6?*Z}0plhuJO-!?i~TlYIYU@;&HNxhn7R%eTvZ3K&lo zMbPwf7z39N=+rAt*ME2$90om-ezx*fEZ4>!#_VDI`BGjgZZ>TW!6e>oH;)vJ=w|!y zj>sb|A~W;o!(W$0$UTnkz?lyfq^zG3$6}EAE-Q z4_cpar*M%R-%kZ27mSlu0Tfq^x^VSI1lt<@RP&TSw$hgF$=xvb)vH4O)5vI?qUY+> zA7|RIor`48@32qziUzz>m+-H`4T52<_KSAV7+3UXxyI_PNa7utq??+(IvFcXRwAm~ z#~nrE;fxn67z32x%mXk|vJzeG&-qr4(7c&??3MB6V@iVv8tKam`q=MPEtV|DT zn?w#HYdNx#es^dFBtbO{xZP>)`z2`57*MT)rH1|SFTkz$m+ueu=Vxz=3_6X3FpA&P z0(8?Ua`IVa^0dmbyGNH!Q3NMoXT`d5O?%$Pb~12^e^yjQ_MJf#Gojm>l;(GM$!AAp=w5b8OmX8BA_7IMIVFF+3nYf)JagR5Ioo@fn;o42`^x=iB;& zSF)w!0v^%{d#dul1BT>C&baRcE-_vG+R^RE(CD=x6;(sv@X*X!x}!(*c-XJ@dN8Y& z6P$^4H{H2%)-3sOchl1K0Vj9czLIe5*1KncfLbFbLu_UUR$6jjtv@I8o6}^qqR?!U zZ+8;iU>Cf53gUiaVq=u_dw3`?xeSLf-vk{ z`YU0;<*MCW0q>d)BJLeXctLP%Hqb7I&OSf>;a$oON||T_GHW zPLrUe23&0DwA5c#B8Hhi35SNoV=O5y4Jj12*kqn}3CGecgPxlW+cRGy1D876lXg!e zvUeh@KK3~ZboTgDAGl{1z|WVtCM}p(>3LMW2-xJP5zI))CEHeBzoW|JjtgV3j?$0=3LU4Iw+@Wlni8%MPw878!@^t)wr>EGIF zGM;-EY%UO$!jW|jpOXBVd*TKc+8$m-A9ie?UaiO2prWe>U5MKDix3r0!nl+hT?MpQ|bB^Cxo)~|h^Lq58xSt) zw*S5&sn&RkE`54dOS|m}iSH9H#XD>l+L&~Nz8hZw6fNnfMtbomm(;<6&t&p=usVN_ z0G`^vSab;tgofH$iC&{0qR{Rpq~LpezTv?S)DXpE8 z>Ewh9H^qy!(rdanhpFa*|3}%I$3wlh@xv#jMM;OI>`F-uvL}R6Qizmg>@tL`*>_PA zSt{9=q75^aA^T1Z*^_;zEZO&cd9LqJr~BOZ@A*B?AE#HHQ@zg2_w&6z*LA(OF1MDL zah?R+qg>D7fMl58O6s0nn{>vl5i1|Ahe#AsNV;`{W@TYdnfs^fy%t4<6ZNr|^L9Be zW-?xAb?U!TUR+$9S_hNz9Dpb&=G`Q97KAqOS^GCVv@mr~PUg}$_fbrvV?oZ`@eZ~l zQt+8u=zd{017G5$>5NeKC&~k~xOhMIu+b|9!dDTp$ZLe_M{$?yY9;kG6)S|019K(G z*%*=b--B!J!r3(B`1t(UbNnj8mq@b>(pIDH;33}w=_lyvf&KceySuxiI*`t^yFiS z2SQ;Puy{JAb|!7>1i`$3yqr=*kOx4=i`?vBjR;fS1?&LD9RrCEAZhACwSGN5(8$l; z>aubAm`QgoBl3kn;e_7ckA9fvf2K%qv@X7nB-|L%s71J7cBb>g?0Wd`>gw73G;tUa~>gRK=p})q`46 zx(MH~8*1Zn-Cyn3dz+o9N`Zx^a5(kLYmyr`R4(5t&|X{_;Hd#$O;fd}82P)9 zW=QdT|8$d@j2Ut%kWK~=;dFb-7y#1U*FV39*eRo8e#s`_&D(RmY2_@J~=B3 zD~D|61LM;>wD->nyNB)FFq7QBur_+f!gBr@_x^6`^BiNxRd@*!BL`l~lkF*34z!yz z^9~fG4inu}?IJ0#!Brf5f}Zf*PJk8S1Z|GIQbu&GU~jWo4#`M+bAp7#YAxH|CHkgnOJ0dL zV&S=c9VR+J^ObAsfbE4SJ34H1boAxRmn$)SnIQ~?FCN%?d0(Z+dX_<^G0{(pJL5j`*`T@^Fo&@Nqv+E$f=5Hv+=fZ!>asDk z_q6SgJD)oS3{E)ZS9rsGh$9alU#@G)6w51|kF~ceB@Z%XtGtpyhck^Ui!!m~yn4#o zzpU2D)CcxeVV#zW8DWl;Bj>29+C~Uc8;x9N-wKYoJ?%MN`oT=qE%xR3%%jJ`YaKPa z2FF>OnR@2SF=Duh-2^Q;oo8wX`Td&I??+O+=TvA1R_%L3F2~v6KO+s`CE0ecA=Qhx z-)~JY1iJ(k6i^(D%eWJ$rTY6qg6g|Z>zyBJ%QD5>0E;{dO?AR@rv5hm~d>)6!y@_)%-+{hsM8YmcxGh=VG_$}GV z1OiuLzFLo6{?TNN4~ZyuuIheK1VLvXzsd?N?&xurOs$wU9z#2#cCEki-E;0}Zuf>S zO$SE@P$tK}rPD2*nPcC-$A|%pKqd9#_CZB?fTuzJO7`tOLD+f>kgHt}~qkkgf zz#rg2)7z5otcdL6pAi^pK8#}&$W`>hkI=spCh$`>V6PAVQU=*&fVp7j4wYPjXP3o4 z0KT0>Ycnci@{A?>E>J1F!@X~WqXPUaptfAyewRv%M0NtC3sp9TvI6syr>@e0F6oi` zD5XvMQaRl%ANG$Lr3U+u0GYIKAL6~XM6K0Y2MwqEo+M#Q%T^QN8rfMwa_#-e9~gOI z*RL9dyZJ2jC=z(!h3WcA&9v}9V&+T`7-d;vGp>R0-rwPr_R2zeE`Ckx2h!*=>-*yp zRh5odV&BD7?zdD#@u)Ot7+*i&p6nB7rCf<|RPeD7mFRD$>kv!}(M3T2UNTx)9_j0b z@PSF-<1OTz=;K*SzLyFh7c-!5P=pR`y@^nXNVW9j??dyB4=Jcdcy)F4#8%{60D{jp zRhhJ?lxRIl+WK!35pNEGk8==fh68NjkelJtN8MBOka0IoRg^!t5t?~H+xg?fOVw{b zn`|6eOxtYx^X*Mvh6a_rVGkI4&|n{D{C!3Bhu){gn`}h%vtdguTmP~vArF%grm9;< z4h;7(pJ2{^S2(U|t-SVO?S(ZEeh+t7UeokEBP)V@hTq25Fhi>U=Z7M_AzKrpByxI; zuo-u2=ozu(c{;MPveMG!_4W0`!^0q{K)c}%!GiXWXKn*70l^}u_;d(Xe7F}i&}vnI zaphuV;_eP@ttI7G?YZf?S6p*2#uaX_ zY>5pi(rM!(zHA2uF*#Mr;>xCY{GPB1i=~}O^%w(lTPdru$<^CLUx^Hg7m=xXWN%1X z)Z26$7dZ^@d_uW!-%nB;{<%NW0<`cIy70$J{esIU2ut!Ar=Df(x*gL0pY|5~O}|!n zTLU0z>>wSCf2s`pmz!WZz3m1v#2qZ0t?A5JWEsrCCJL*|e zhBR+5NGbm{e0NCYu#mNL`%lH`JJLuNY3WXSiUw^1vH`h3YNUM zsR75JKAKl>1LKq*m^D6<^COymQQmyH>F2m??HJ~K+GJ>%zkN({f#TVqhb9V=2iPX6 zGhU7)d|}rPRy}yr-ofma#p}2&(BAL#Q=>K=)EnI6ru0^>Ee|yRE;0q}$v^=W?ZC{z zNa%c0WRs=E9HYB%<~u)j9W;B+AXyHwQ+kAxjK1$9qo(&FxL!bt?KG+J{Y5i>97Vy! zkL2TRm7<2o0LOh<}@JY zBM6yZftaX^MY&CnKaJ&ddYV|Sv}-t6v%~(nZ~X>Fp*3#5psP#BU)rHUm1jstj{f>v z1mNJdFog^-?j6MIWDY9c?Vvx#!xlBzvS^-hpB2L!09UkBD`t=&wFUU+>k>k0JI5y&&#hE@OX57E)XvpSC(r=2u2Nu zgJ07kwd1#u5A-v8Nu4V)ENq=7*xW$Rb{zR_ib(OfcXxXM_75e1L=Q9qDulQ|HSXH0 z7EHpk6sdJEX|rYC8$;noQ|c$6@;tOFhwKSW&f^0%YY@Nwm)yQM5on2#d0k4?b=F0;fzbmlq5mOYJRO*=?Qk8sf z_WE)9Qr7VOjJNMI2#T5}ZiPFU(B?cYcur6pe4lN#2ICBKl~YsLxg6^7$HwC*X@YiAcpr<(Fh6Q-MJtPAXf6 zgft$((IBm~zzgU1#wFqlQHIlleuC2$EJeVsE(SXZ^dKd}%-a;Khr zd`6(ylN=HPCnPc&l_Y6`Dj%KVEt&gV zp%s@F67RwmmQXDJAFLqdzl*=e-Gpkrb!Ed?481=zaNEBojp+YW9x0+sU{CCEO{r<% zUU|*xUa4bfQ8r2eV@Wc1o`v#PJK}1AJ_3b$NeNabc6CgL4W*u_lW9(;TF?KOH9-0Mdbe56tL3$r1yqQ24Y zyP<@p3I0zGLFwY#R7L~fLTyJfY;QavD{A`%*&6l!d0|M?G-(S2uo0q>f@$g2B{ABT z*{k-bUSOFa&Fs=cf)3SY3ID}4$78@8p`gR{zv-zDCBt+&)Jvs$Asw5{e!xuXF-aZG z9}DlWhoEpOlkH8Njrq5^y+!h3bYRk;dw;$96`nwmeA=|eEwZq--U)Mpj)NumFgQ+= z(=eT=@~K291o2U)!fI#}ccLTyDAQ3M)K4B8Bu^d>KJ}x0XC5@<5E=nL20!7jZM@2s~OlJSf zTjx&6yN6t)2^2DVymqR}*0p3zFwCf5X|pG46uGvrhZcakZ|sUEuS&joHj9zRQ;Id8 zpdCVon@=^rO}@JHXslnvP4z7P%9}zLBe6uq!oPGBGcOB=AA!@`PrK~@yzNJgv1gl{ z5;OKBui{TxrG8MN#JxbB_^m|wJ;j5I7NvvcOGJnLUxlC4X^~E_J{~Fn>hnX73l`^9 z{}zK+Hdc(b>pRv(=`Gc6XR+=*m)%@?0}*QJGoXLm_Ha9Q2+{H&^M~VcEb5?;3WK@{ zL*zXM`Ipr6bzi_f}-4tijijaPQ{cF5V6Hu|ao=X6Zw zRpXLw>x3qHpG*9#8xfOV`vn+|e=0RTG2rssaQ+s_ zNbFif)G{B>a$s$`Ph#Zc>)M^rqHfP!K@cl`-ZEoOQ_EzOZnF0f_ss<3M~B-VK0Q@% zKstaBaW5OwK(0F7Ct`L_=lF48hIr-#JRiRD4xy{@B*FH^aW+hMH%)LJt&glyCAFN& zf0A8b<=XZz?);wv_pc}TYiR`&1F&7!MfYsp-$>X-g7cGCveYQB%8GffB*(qq|H9EI z-Ym(N=MwKAqLm!Kf$E%7K{-u=Wy`Xb>Y0ptrM!Un)~oEt4YV1A-uGRBOz{I5@WmnR z3@JXxW`J15n62(KjalVlARsLzzh!!zeJl;upurS)}Y7>deW=ZJ^AOQiC^=b@sUMK^3w#;>}fmV)4- z-n}f{Nw34=p6hbO2qN;cKblf@xR06d^zi9q+ zc?LH5{snm-3@^N&n~(Muhb}EUiNkR5cKmuOY+b(FvtrrI!$R(0_hA+?wD%dDIN@VYcYOdG|@SX zgmHlsyUW&Ku&RhAcjW$Z{>9$h^5x;g^5iN_NA^fOfm$Io%{*~(jIp7rEq!SI%f!T; zVg856|GW|2PPEz3bd`UtEVD;906%r8mRZR?j=;$5;DT*>yJ3eFKIA-y+3=0_5J!hx zHnzdz2{Wy%RqCHgfdxKVQqRoN`=&JCcMh0a6wBz5tw$S!G#PUjUdxl~<9BUsg7nfr zf{czf*2gRP!hS25M2iIOH1D#^ePoMC@`FPR))IjuzJ=QTNAD*?hwG)qkFJve0Am#lOQ5Es; zJYtWE?iv;jt`I8+biP0JaPc z)LJp~3ReFGgy1%$ltO>}>C;!DFLslcaQ*Q3USo>#LYis$o zoNt^rZrp7nocLhcV;(}hS}?~ba@ZW7*=bQz*cQW8PHe4O$-l8b33pUPsI2jZxk8|7 zQ8CMT>mcWmmsx${qazICID??|J+K#^oL{ZBx+ju9KbxdB8_l3QvwLw##PCRMC8lR1 z6JmM>nTs}+YwwxkTf0=3DXrlRcUv5&4z}l=C5)1iCZ-Q@*p&x2GjZIa#VNQ?cU|@t zX>Sj>EL`8cCkXTUAlLXf`^w7hJ63nWvyAr9xKGDm7` z#tped&_F?BrT|sMkLvL?V)93~q#{S$&t79V<_e!h6%5Uue<&Rim;++{RUdf2c ze*#?$lmc`EP8S)i;#ugIl*g!$4bDYkC3^G=Ke zkWNBdYOHzN*At>4E_)p_b|uF#CyM3fY}Bl*w45`=Y}~7PyuNyT&#Cv%Mv%=L=mPc9 z!uz4Bza(`L|H|}>_=!MZ_1SDZ4yv#3%j#BZc@Vigcw0zeLQUoTAq?(8B?@gc%f8M_*QXz# z`x57Ns2nFkKu>8N*`hPYL~3&WLy5{t9;7dQ@PEzjwG8wWmr<=b2JCxLB1Y>w&)=oX zJCj}_2_<*OT3E>(!EXvSsN&^lT2O!>YK8|?2nALl0lDG@)^PlP(Q&+z^lNfvNfNAF zWl33eFdJWibrCgs*{i|J)nDE>n5R6^&m!V1n^gBpa&yu4d*nCCrP-eQRf?c}N{?Yt zrC&>)ub&9*>1b);bIEaDOM}{wmExlR`DjS;Qr-}KjJ_dvQTa*$m4Brptj^Yvo6!o3 zk~8s(I)#q(`GN(hm**GX=Lx&4Uv8VE{WfI?-fesH3Iy6f3NVt^4pnw)pUOV}CcJa_ zMyqWZCW(@kV~K8t5!dr{G@=zUW4Ju_9L#Pp7%SH+$3SeZ`|7-{_d%w1w${9?f!>m4 z8g>!zvwG|b=3%i4`Gmx^E``{IGDhSIwZaNQ7%ffct8l-0K~Yg6g=DrH_SnZ^qKvER z>n8&%UXPzku?eV({uDqF`TCfiD)qqmyP1TDPw!to;fk2`F=la& zpbE-Vzoay$sD`f*;6G>ojxqRSlkW~FGq4pfW+_-Qu6WnK_wO-xJ=0uiNFQ+j?~IsyCu z3L>plb%}1b_*HNdnUmUV%z1?kIU{I8gJ#UnI!J4GZCgI%uWaXs7$GzQq=dv3mmcIl zQUe11*g9APIZYkhHrqf|tYd0JFyR_n{888|?q6!F?Jzv?<=zYVJG4JLh*j@7QNTQQ z`FjDyGO&|Kz1&O;#YahE<+b>TMP{S6o^Ge%VZ;?n-p<^_=s=x+lb41Z8GlYy$=-fd z>iH4g$$Kj=?%rly`<`aDe&U2HLkeNrc)ioTQKOj_RdwV=ht=9 zST=>zcijrURWN&k8{6@B3TBsmi0mZ}JTQiXMqd&!a|02w{%-qG>CH zILS4427~o0oOB85op|QWw6R^Q#}B4bxT)%DBiP_ejY+Zdzz`6X+d>j?Q%gzSQ}w5l z$sEsx%z7xP8Nc&ulI;(?c*tM!k3P+Xr{M!wZH(ZG6^Lk0NCB&Hrkc28Hr5+^nKg7o z#5VE62SY<^>zGd3J}Tas6G5HuaAqW&K)tabf9vlHjHR7R^%u#C)#ADuWMwu z@v8m3Z{=ms-P;tOnswWi(>4e__q{icGqB`f`0|+8J~mOjiwl%-i8VEpU&7ISYm<7N z*Jhu4>x?v8@>$WRmo?0wEu@IGHu{LI)^L8k*cvZn^EI!^%qeq%0Y~Mw@w{ns#D3zT z1Wwq=W^U%IBGq8BSZj;eftJjKuLr%OoajFVh$RfX(I(W}Q@?(VQb{84r8CBK_KKZc zdk+a_y>GtgzjA`63MHrKci?K37SI&7had7x-XkY&kQC{su=rNSs)(}u-9>=>kw4$$ zW_(YIG(sBl=Y$5Et?2;>Zosa|AXIjvT8HKrD&v4KMZFgY`Pw5Nc~vm-F)zvYV4}|- ze|i_MFYKfg@=SwFYTt$ON)s~!d#&*0neRlz%XfT{IqxjZ$|2x6R5Iz38*HS=kC}OA zAKq6TT{O1@X3{dMH)07hn{&FfxZa*)N;i>8#&#{$n9-5~@ua=C=7oVz^FpLh9 znxjamnM*n%BgGPYKEa;o&(`8ed2=8BlFqK;tHQiy2`}I9oqw9y{sUGIrG?)MY|UIJ z1#C@4E~L%htSXq?2&6i={%UirugJY(`H?*sq+cWRq92QN(1an)xvA1wxuqrKgP>r=XCYzZR z^&y33)B`2|LI&0pHsuwzrD=?i;b)#PVC^)0ydXVeXf6a^@o%AzU{eDEGzwD(&Z_}@ z1Kosal|$B&KP-P=*T<**hLo+bj$jf8Mh(A)h7H}ZI_KRt*#Z9| zJc65JRESa7E-oF1OG&XXG8)F$^_`F|14rxUUbf?`H;yHLR1$h4Q7x!pW0le{7hG}h z+MMiPD{pyCkg`eERwte3aB%&vSXK#_llGz`pE&iZn~t`y`>XN$`HG_nkzd1Gc$;RN zcelLeCZi4d_EJU|P2kn1{Md8~xyV)?OYh-!^!Hw!bQ~=b&l#}jxiF{Ww$l9}OVHxH z`^E)LDKVvQ&GvnKAve5Nc1!oRpWB_0XR_+5|K|PR0CQB5kCw2g2G`(De{{T$GP%m3 zzXr)w%D#4F45V8ed!qgcbFTHgF&GZQMv$wjk9dzTTAdlSyTY{Bn!vPNbE{vRMnAJ` zMGV4yxEdb^&O|*HR;~J3-E@#9)h6 z)vQHKb60zn58kV0Uez|Zto?#rsDW#EX(M2$w^~I}DB^-H_bgK#CtZgiZv-hjT>wVT;p^Q?7Y@ zgIzSw$=P|&o;}9{J&!ESlk66sJwme^_Z|KFzVHj}lH9KnHfmDCGKUqd=fBjGe>h=$ z?%k5Zj||U?Zc4E%x{!moVv0SoBW`dcD0Y7>EqW#XliKs>zIPs}szU0XOz%$wTPf*b zhw0CMY@)tIL_hLKe`Cm=AML$5R4C{?;ajb@Sd^8?@_>UqjNEzovY^@J(6d1mwtCc(+*jf!^xjh&t;O_HAEWWtnTrPFZYvwCw8b>}69`Uj`>osCPm z-84_U&adScTw(Euptt78o|U`AHQY8Sxya7|LWcs#&MZv0BEOTXS@J!*0Al9tSm%6D z+_eMccp$H?A9#029%m5kxt_E^{@_o=`h&Yw-*P9r7K|pLBAe^9Xx$kD$9+G2UY8-} zMmiQ{@$1~@-4cLd?bS4|WjIZQdWDM&=d>8$MZ;qfrYyu)lS1CC5o=w(Kn~}aUDe9I z*@Y(s*C5uj-EWBPrwCyh_87u z92iS0%-^y24JpowJ&0#I--3iS_Ok|rc}isPZ&rPV1K1HVuOP>Q9oWz!YWF+V`6}E3 z1J#xjA78FY@IU{a^gactSK9(3m`=90!redi-hM&D zwC%w+55XUJoq`d854QqjOa+>csT<+gz#nt38M||2z+6&y6JfRW+G71@ugOX8{imY5 zh>6pd+#$*}y$^ulDNZIbdJ&s7NYa$FGKi^nwQ@N>T;o*paVa>xaaNTurVA zW@*8T9i$nQ=I4AI)$>n%eA4MvLzA`R9?@3={X9E0&ymT^S9!^$Z2*g((0i6rQMn_q z%Q%+c={vUDV(j3xJcEE2kS(e?>g4<=-l=icE^h&KkY*R$? zv&2U{3m^PDs?3!rnCE%nr>;G|d@5p!g|Dg$jRiwf8uKnILj7~mXe)Cfz=E?zJ@N-G$ z%Cj!Y_uNPgc_S{{Y>c}>2HS!tI=TXfwhqU)76+^dRo(qm_EmJ2H=HLr>rC2->=(kw z-z{J3+51-G(wdm&m`hv40jx`4+0`cQ#M->dcWE4O3g*^D?q%#&O!xcz(4tt{R&K*2 zak)fh7uZ{P(gi-BxWXnP4CHOTtn6o<@y}J(?p}P}W~pi`5flPq8ckKLah~VjE_USx z<_O)l@N)FyLtNIMmk-k&aKept!SYlMzN@rBJ~0F67U8%5p{gwdVq8$FH^63Lsh!Z!|q`P@e59hmkSzaVuUA+Y^Ive6`x_J6>2kK3t& zkf$X1-Ja*sDB|jrVW}Pe3oRh2ZhpD{p?eckpAkwQp_%z(S<%%0+P*M~w`C0(_2hQ3 z+p+u{6&K4r*@A&veD`=>;7pp^qR)v~EdJ5szgZhIgRk}Pme%d4?5#LwZYq$3MKhl? zP2O!Yr*T3hm@V&?yskKB*`>h4GrJE5Z3-?^=fueAC9N8^S!}=V zRed})4~mGs{=C44g4n7LW}v6gwAdgiR9j@cuHTI#%K8>@X-JEETK}fy&-0($$*5dj zV)4w$UM52O=1OW&JNTukb4L%s{HIQ?lDARS|bm8ekBUYCH#C}a^c$I%J zrScW_-Flik`&HGXnbs^N7MGb7K(Bu(2DV!i62}X=-G4Y;Bs~WkvYjKr5%(VdABb*S zo`HbS^2}&k(;z789boc2vZtqV+4K^WaJWOc7M1QYE-l(ee}nJbxpR~Ll;`)a=70O^ zs7bJ7VV{ihbub9Yr&HiT>;U}Mh}X>H8Y03TU46?p;ZL_pb;nVz`@pHNyekG=R}1w{ za_}1aLJ%Q$A>oC=2c3v3f7fZ2=aCuo5i4iu*V1aZ`;#wPJk@!W+;0%1&6i}#_;K5U(@p*E~YYCHFx=h-eBQz2;us(F0z?%}W&N{u#$*lkkF|Hv& z9=0C?hreN@KJf8ghyMfLFY5p0^n|Yd4N@ao!L15L!qP|-6cYEEL{5UJ+xT@$E8+;H z@#44;D;Rb3p?w*Upq=FV7@m;GzoR95pSh%&Xp}mF_mb07mho11#7Tu=h*xUC727+$YMr94gc?Z#r0{YWQU?BSU zmWKog!q8K=Rs~|^wjkmq7(pI4J>R)`(@ZqPd$gTuu9z)x*V#zs4x1}AUS!b&7xeDM zqp_%nVO>C%S9257qx0o5|PPZ2Z3atNWOA`jwMcwZ?7mCZ?Wnj7t!J4Jm2dM?W_*>CsuBDw~*cl0P3W`WCHP6LhPcEkB{0# z@du2)i+IkWb83x4kl|b-m#C9|`3tJ!c_GEhjh8c|g<6W#bT-=`Q&#CDwWYdC=$C#v z%$MYkx_i#a3hPJktuStN4H5v)M&dP{IBMXxfM+9ZxNoH_#cvLmKHm4S$mxG~=zshg z;YX6R&|7yFKyAmW&o>(~5+7!n7@8h^XrIn=QDh<(Cldipue;m_7%s>)o3@oVj;2b?!pq5yxi)x7lV(9J)aDj8OSGd(drc zx;*IcAD5aZ?>RN4KW{w-IN6L$jBOHD%LT5kTxFqo<=mst!>(3JtGL|x>09KS+;`n> zVa6Fhxl|t?B`rr8jG$(qWdcgOYtzE!k-FQ+9_EPXTHX4y_1;FiQI7o9y66lOSHO?mbtuW4b={gAQ%XT&|7#-bNY zhAX3RmfFNz?*v|zo6nPU?jI-3Ysr{L*gQvIHQWei{e_-x`(%>**#B6`{y{x4{=H9U z#Tp>jnx3CBgcj)me>%AA*drkyiYwDhab${&=roPTqMW4p;K}$P#JxhI#5vI;;+1HU z_^mFVnoo*Ek{ zoK*vMah>&~SypJvTDg?P+K_?2LZ-jwhLQW^7yc$QJguf%$Jvx=QFhKfX9YO??jcnJ zoN5e(;ZAnu$Wk)peJjFR0EG*+9otYzFAF^C%Eo=~8Dv_`^b1zy#F0+4#7o^k8 z!Kbo(bM=l4Xx_!#r0JkV=G;S8r?yw35Lfyto#}{({yDJi7)h+>RRKR#atG$`AF5$& zh3A<-GPnXmxx>h^vtaS7`kVaC>D<7U=HwI{_vzUFqqauM24#(8szJdH`_x!{^Uug; zj^ABS+5hfdKxMp{bVuU{>Oh(Hd&WrrHi0d`QOy-LTdrZ7JK9ClmdXk&Gd`M@b+Jt5 zEGNuw4jQtY#q^vjTq*t;yz2O>fW5apedh0$nv%eK zeBD)RKTmNifzbaxSV4K# zi6gw^0-I#YOO+YGax`F@m6w>34HHAzORp{iR1efinAd)L-oBG&It-vL9VopPedc=}-}SUh%uM{!w85SqbPPBJ?5-9&8aakIJF^hY&2~n@2XF z!;*%8l%axJ@8~!trN)I-*7C@>_UOH6P#&>3L~`pvn1n++)=6E2l4R3I=OVQ8LdJxJ zn9FblVWmdnGBx^s!TTq*(eI}cIite-Om?5d!gA?~>DQ<_=?=Tw>tAceV13-g7_7+f zBr`#QjTykS1|Ts#s*U{`0Av=AZtR=xda22?SwGuyE%4$?%hD>FxSkxU4gBEn*RNI5 z(qdtG3%WXsJeqwf;(dyIi>FVYd~rmOVhDm}lqY)n9};B-1>DrjUZuwrV;pS*Y~PHE znP@EXRp_?5R7KRyojg^Jxie9^NBO>mGHB4u0>N|+OiH(n6A*k22wuyId!*hXyA>1s z*+72+s%Y))_s_F=6X&zOXO35GCiE@4(-sabC9sr41VQe=hO}|9FFAI^IwH+}&ue{7wN+ zSj4ObEH-D%7kq0Z{G6(2zRa~)xGEuje$IVk)_ZefJvS5*h?xJiw|_#9{k&*?W#W6= z?u?eFI1Saz%Wp>dC!-uicj-uT_DeWkvw3)gWz&m1^{v~XC*xc{e`-5OC4CWTN!t|J$N|9(7f5*v#M&Jx2dY9i`>vEY6w z38D1SVo?XW6^~yK@~!f8w=ecw+&Pl9YmgQ<#&Rr8%AcTqcb1)IBvuP?WVm!D^!Ff7 zqD9^D@(Y$h0MgE~_STZi-dD~x*^V66G@CFruMJ`-`yA@{)ob=6j?aiEnff#Rr=}^Oa`l~OUzzoO%enpSu|VXlVhJU0ZL^8Du-uhw`Ebe= zZEw+^muHt)9Ntg6S1F_Lx=6*jc;ATOP%V*quKfa)_9yp_zuBvmB&nkk$+Sj$3du#b zW%ubliOfAN*|x$7exSX4^=VjWcfU3M zCEgfO1+YFX%uNTUYZE2$JQ~*_<`Y|9p zmlj?6aSi_ZHSWLr!&P#(HmIK9&IA%55^;6vz#(_$CopR9#I9J{)@(GFje7?&N!+KN ziTCjExU>V(yI{FCWb{``O0QLM(aDE7t?$w8Y;-z@@99x)klm?bT{+pj?CBPyX<|1p z*#AJ?-cDt%N?Di$${p@~sQBvf#+OenHdFk0Os<|)Gv;nVotiT&0o^Q$DM8UF2V=a# zZCKkk9+@*F7m>~w4bBb?6IZHK{F1lqZ`GDcez{{_X1J4XCxAb@YXf4Ur|g&~UadBd z<#6yl?b_^acl#?jO7{poE><>_Aiy}mK=1Xm^RHBPI%iY!r$ygNitYvOPZ}C6R>hU5 zM%ar{7&Z3jo4*>1ASevqL#fLySwzHC@~b$0W9mVaL;|;>qSme+e%_WXR5eNQL)d@A zdr4QGCb)*yaf|e(h!Ab{esH9Y6KIVq^^wtSTVWr3{OwE#oj!gce=fu4O;PmunGWl` zA^-6R=Y)Zn>A?uSmVc#8GhIqL zPn$otZ-2s({1J`6haaMH&Pg~@MSngsHIwRjE&Ytgm~@0QYFzcZxJW@HYqqJpQ2V{* zgu*ws>@KeI zqH&|2^3Kf{H%hq9jwB}Cb75r^ScDwiTUh)^`a>FiBX~@g67pIqtF1;#{hFKv&rbv6 z@IH`OO`0zZIko9au=zDbIH#Euj+m<;jtYOE8+3YrRgm;!%0|G2CzVuklJA@^@TU-h z8nAv~#zi>B8%=b_=C)|zWN=+XP)5qz+08F396WeXl9`-qYP775hKcF;K9r`OK}yjl zl#2Y*z6hbGr0>sB9&$DzSI25}l(|BvI&Ud=%^6`2%qxdRLR zw5x7LEK=}d5j%S=M&77<2JcO5!Whs*haF}|=kPW}kmMCe&d^&a0;1McPrG}c<{RYD>SheM?MK)z-!KKm&f>2`iRcu9$n*i75judlY9fPk`XE5( z^?)%iaU@}sU$v_&;0b%7*qPm!q{0q>H%PlHVD;+b(MYL8(E|gHigLVUn3?7K8`c7f z1wb`@SI|@2^kE=x9Set7@veV675Mb%N-)yXdtT9l%k_pv=ACooqaI&)U)}y7?{!jX z#Vay@rr~Vv;WqQOoSA#FI+usjp4gcYaj8!Vbr;?l+)fQNeJ-e}yj4TrZT!7+B)w6* zEIUws@_oI^7hL_N$*US%J5a7lD<*rEJY%^}s-mWM>uXfY%CVpa;;WOTIO%h3e|X;M zU>h8`OO5PFT+%G+z82c%DU69Xw75FiTu&xA#lNR)w`UnJSf8Mg2I=4Zr2wyruBVgM zPuio?Mekn`v)JRBu_V*i^?K00f|cr7lyVqZHG6-Pt5q}0OytUxTHNha>XJF2LnFqy z*pmW^ZJ?U_q|bw}g46zk6&wbv;6QjkY&X`XYuv{=*E-UjSr|L47ZY;b^({8bBG@O? zR~x%Bs@!w!M~S@eaA5id-8@4m!r>B7q3mbxrp~@`q0woS)ntM7an>#(18#+UjlCO{ z-6sy)z%Eoaa{-cm+;xG&`XLFU;;wQ%S;ar&4^fG zhdAx`%fC5cq|u>4fgJiB)F~y=2s^&a&HC7~a)qe#OlZZX-& z20$yX<%hk(}O78Z1WU7)u*gzy< zbOq*q)=5VBpHq4I z-LL6smi|jZyXFOK)7R(am_8bOJx1)zRdrI!`R5w70jA@xekkH(JVNF&0^8#YApN;7 zCh!qA90OA@!U6)iqomvK7z_}4wh|F22z{c-@-yZv+WvNfO5{mpJ0J~s=ZzrP!rj)Fu}ya zI9D6}-M~gE1Z@6~)YW#bq-`=H21kaX8t`|4i&sGA95m7vLUEZ1RI}OK8sh7$h zs8K-#bRt3Kmi}bg<%tvYBm^h#Wj%Z?|6-3DfFBI4hT$W0*5gk)Fohomd<7@P@YlV?oy9ZmF?trOrr-?H{5B(zW| zYpsM(a>>F}4k2)|?GZ6tx{RNjq&Oh%=(^06wqvg#q{$|JD0kjCHD=Idb9>1e4@ja< z6nhGb=#jhl00)H?1-(7g$juBW0P!zaq3i>(MK{_lv>O~ck|h!PtDcL|zg39(_ajzlI^=3#<6y$C$ zOL^P`_u3K-ve+mQw*4dM?;#fjfG`*JY+&iWb^sGt$Dv-Hkk&8BBXi#ju2ywpS(NfX z94it@LOA|5xG|6g(4pkGU%t-e<@5Ovv+&=c8nE%*4yrm9_s4P@(!p-Qsm{Gk59sA~ z2-9_)|AIRzFEJhFCEjy!$R7NegLAhReMwoxT!B0`lEM^{#{GgLne=SaJ9;MPxkKwd zR$FD<)(L}Zj55P_rc|g_>KFEpP!UKjT_nhcgqs2E_!4nH7I=g^%}igR%!zRNkn|=} zBl_@8445ZvtDoK>j0lERulh7Vj@5|3IDO$%~G!zaDqX$CO=aVAu zAV28>@fxCA(CydL=TamYG#kJP{8P>4E1(iu_e#QkWv-&Q_?r7>>(6hYUzNQOGn^)U z*m(M(T9o5XGH!i7Ag#vv2Q%uD1Lu{bx+X2}0NT+dQ}M$P^Z{EdL57D{_(~Ce?=8U09E({ujf4@4_OpE4|yBFQkxBNp9WHTyq`YF z^=KO(t4vkAUO6YAvLxk^?-y8gi|qG@_-D-vWBRAwaXRD%`==)*<2rm= zd){pT0gTW83>|@x)c-Z){Nrc@dn+i+#L4Rrlu-4s?}Ek6$=O!Pr=w`*_O7umyim?9 zb*=H#Mr4w$cSw=kmet(8bS2z_NO93v3bBL)1BL6iW=7D63&)#>Csw7;7G*wWjUs1TTE4)piSv-A=7?4xJHLd>#Pzl#Y; zWNh@Tjg^s)b`Vur&KFooOr|gH(tPy(@>&uqc}ixVH9oH3u1~i+ASWad6*@f}PoQ|u z2a-mM3F>NfL}dEka10wPS4kSpZS?xrGVPaTC;S?iNt6@5_mQItA(C+v{fj1cyVdJ4 z>9Qn$^a5xS3qNP-JhGf^?B zH9r7<^ka7I5p|#ldf83Oapgd4%6qy7M;OJ)zVjSP?Kji-*OdY2wK z`u?Pu-N+Dn#wIFiYR=e%EaFJLHHZoG6XbY&kVbq4)yn&N7k}`-l}O5`M_yN0{C2q{ zT~hyIs*r4o&_2k;OnMdhKOxoRH6fRHpCy$~#};(Q;<+PiyvZXUvstF3cpGHsIQ9-* z_YWTGeo1(B^u8+$O)ryu_bn(Mhor9h;uJl1ZLF8!gZSBtHedali2e*;F}5}~zb-mq z@avhU3_PekLr=BXGu!O$YE{)!=r~-oHdmlvVbX260r6@P?(y%FCQA)CBM;6~2KZHe zw(9jfCBJi8I9H)zB$?};=04-I*SPu_UX5`~UCm$>9}qpBG1s4s$nR8}^Hj@rw=fkJ}M{9cDb+S{f3aeV+TA$Fh~4 zTTQr-uBMZo0_r%Wod3~H0{hL z#K!KJq-b2c+}a$RPP6ScPoWRDU1m_h4e1q?$&pr9)3?sU{iTbGWuJL#C9zEo*+;7q z2furZ^sx$DtpBR7@%nKbd&82Y5_doke-^_>-+g9cWSRnoSnK}ci!Og_ z`tm&DEIlp?0@#X_w}Qh%`#QP#qMnlVG0O79KSk<(cx0EUQOlcEYrS0|%)RThb+R{A zy{NISiiyQdC#r-4 z;$uJVZW!@xBs7;vpFnppYv`&8-N`fB=LdAk_3(7Y_d~oUO#9U(`5}(bR`m&p`qZ1F zH)d;k&5A<|s&j718zx)Q>YtqK>)4GKEvyC3#Vbs>iB5fLjZD~}&Pc3oXaSf2tq9dzH2s9?wr zA(x2L393R)K*d{OVO+R9%Y4zNOL`?&A@D31mQXc)ytF#;CLiD9+J9}Bmg8XP`6svd z7;D(RFrU7+w#;Zct$Jwshi%u9f#G8pX|4$uH@K;~COi||J2TgpLKZeDaZj1LJKFxp zQ*w?Si6vVx;0HDIr+28JKbKVB&1u!Vkn{KABTgE3ucK6=_bn=?WT;Xw`)M_fwM$8U z?+*LYISkuM5|d>Rm@Ec0hUmaMvz+3^=&+KdoGL&%+{;+i?x$2?>@N;R-?-h$Z7|mJ z2ne(P4`W{*4`mzvTSU=f9hAzFtr<(H6bg|&Tg;fOHTGR(s}LnjL>Q8Ni_BO?c3G;i zQ=utq_I=-%(7B&!dEfV(&+nY$kH6apU9O1@XTa{i-@y?5$`=fCQmE-T%EHGqW1OU@Fu8^Wx|ui_!D z1)PBYQ`_(>x=7{=eO%sJfI%Pn?C)lk4QL$I*iE*6M=0x4hYYH(U7!c#y6Ta|q#9~2 zP}w%nZt#XCm3RK~7iI_7R7P95udWY~0rVO4ukMB7O817FNgfujz5SHrde1c!2K5oK zdeJ*@;?&f^jE&@MnnUf!2n$J5uip;QM!;SY{gXECMQ|GRp>HmuRcM#f#e?aaZ%%q? zJudQ%x`6tca3~Vy9vDzZ4=*@%w=y9>rd|!3ZJg^JK*_gYH?|;jh6ztAU+-gX7(;(O z{=|c3DFvD2F_^ik@Twp70;KRi_?iF}0|XKKGonHD@aK6Hv0lcKp-dx2Nh#u&IP&+9 z#XlL5|M-B8KJ;F8C9#?Kk~g4b^Tc4=f*}y8z|x z9R;SUvcq%WhZte=-9fv7@%T|Y*>1WR`v@YD=;dTzbF*_ifwt5=&R}C9UDf6lTI7P) z4$?CF8#=&3QfxD9oV*mL%7NIqpZ*?H1JuvR6o&_Ub&svn(VpXw^*CD(I>bK(E{hLw z)vVTyj61>-i)BToXqo(sG&7#QS6z@pIph$AZ|A}wUl4~*-nCthEs|WxOs>hk%X|*S>A# z)=7qnXf_L`CGlNA!9DRC34n``%dKDXgw9DF1*F&-{JZ`W;!Ep9>ziMwICJM>rF!Y6 zr=}#FzW09la(AG{FE>~8QthW}W*IXf(-vxGw!d&lhd%(n15&{Z2}^#ek_tvbVWFXo zN2$CWbp|aP1qmOEta4~9`=zGwt0e!Q@j?l|_PWw|i9O%(sS!@Z< zMrFKqf_{#;$$FgbrL<_*z5Nk(6x3EUb#V*H2?HVJZr zC)*8J53w77%6VRi35!N;L!Fog*hiSG!_$%)#gt+FJ!1{;Zfb9gCRGvY?`J<%LA$PP zX;)_9MY8e)`wKQKlNV&l#Ip0$G>`4L{&-yF5N{#b=JW^(z${;ZdNEjfiQk7h-yC-< z;jTCNxEvQQm^WRQEp10Izc`@8&ioSO@ymK$i5wixaf#sI$$2m%=-SS1L!b&)mp2=o z)n$OcnGtI|i~4S2AEjkd4`gT$s-fj?wd^znJ%twuvh}voT$cP+=I;~VR*_w0!#bjO zBsY!ywnF{JK$QCr-wkXkD5GQB?Y&@{XzDf2C9&;-?0KSVq9G{8B~idKUTK#0HvY2f z+{PdPRUWG+`-`U1^bbO++<#^S(xu@dr3lUI*B4~P5>EE*aQ|$tVav2A^gd51Hhk#2 zm%^{+69Ail(h!ttXXWr5;<1~u{%QE)?-g#Xfp&Y|@-tQoIj_(4{px!C;lqdF;o*)> z^sMYx3)ZL$6Sqr!=DN9a4* z+jkbtrX^}DZ%K$<^8H==RQAl<)%}BDx8Ojf3(*2gF-+6Hs`maZF_sL9TFEuZ{~Pctl@8s zIVb@f>bDp9(C4J}qQFamI*Dfhu{;ek2rSrq*J>wgRAD)S6y3Au=ZfmJt9q89$8QCc z?3?Q(n)O$C*FQTkP3g1l*J|J=eOo64xlvmZe&JHOikeX0H=z`>mUhI}PZ}s;uflttW;E=PX#ZF>DG1M%LHhc85X|tJU7-7|TWo?hK!6?olzV%% z@4JBTmG9Xn2){an6%Rh|2z~l#rquUtfBT!BaZon(bUpflXtAZ3rPW8z!>QuuM;usE zv!Q(Oiet)sd%t?S-OwjnRKKcuY5F0QytVn2u-S43WjB5k2*X2^G@E~la)Bhdnj4b< zFCzAQ7kq2z`RNh$qn!DYxzhXlrI_Hjo5$vCv+ln-;zUd8%gfDm?0BUCIw)7T&w|SK z=iKFcn_MFG_XKd_8=4&zzV5kq@GJ7Ej$e}PMnqf3enkegB;nM!r)J69s@|vhEN_w} zGl=3QA3dqdA_spB++I-j6Htw-$IW`aZ%syjV^5MgG?&(%ypz14NckS+dumFKPI zK83iSukVk#In4JmI}*NdAU;6EC{#Ye&BN8$ZPokwGvd9EAI%B(r{8{*T@d5V0_RIV zbzi*9iGer{!1IHHITVt#)!vN8iNWd6aV{>dlPBjUCndXm?ygrBtLKkOYES`t_O zSna0T2d!hpeH9LG-jo52rikmHZc((aEcdhA?7Y0dzP#73>Gq?MOCo-9qsI`{R%_4X zs}-q4hWxZ8H~NeWKNhUUEwK`tU3$aWk$6z*zx@(=9u1U@ zsY!K$~BkRUHG}lX!gr=-IUj^_e)Mq;E)Lw-(I?j4cAR6=~wgTrzu?-Y28{Y z0Tr&|r&I1#0+jsqJGHFh!Frz?6WL@BE3TLBWFt_o>30SZAnnX+?o$usyyrX#+NB>q zJ~K6C>*VBQV`F1$+YS1(ztV`C0za~|1AcJeqOl<3`^Q2I3!5LFJZ#-t^Gx5=lp&5z zg3VsM(>Z_h)~WZeNLLt3@_IXQSApP?62DL<)%)6UK~49{l>BO7`sZg9`JkLVM&25!!+d4X|BCvCS|1I!S~ax?-A z1Rqw*M$K#dI+Q3GDx_-*55+xL0u5MxZ*NNSaKKT-rkfuHQv6X)7PK3i?@FMwa&IFp zIvN{ShsJ31pa@2lImDC#L4%$a%jTvMaPD;PDBc#sLA3wfQj}|F8S5gWJQZ4U% zo%s<9A{lFfglN9=L!3BjB3nwjk2AogTqh*=eQF&#C(g=3e60fm{mw`aG=|eMNh+Ii zbl*_jt&OT?HzS}v7Rmojw>;WR{->8 z1ctWy)WbD;8aADK1CfCmduoawhEm1RN-)|BbfQ7Wl>EV4%Y%Cmoyo9EHS{m!%`T8P zKD=yaEVr)_9lc$!^N4i`0R@cYmb;+OWr-u0UK~r?O5&x@sA_xjpVOyW!x-j0?`AE= zJc`?;q1U^$WWPLo>PP>>BcC-y<@9J_P&?Ouiu3`sVTqGE0E)U}5htG1O_hM2Vc&k1 z*ML&t(m4?A2zWDwTK#=npt6A6b+&I5TU!hG?|34}mY4S4TF2j{bly_eILBE%6Z#-R z_lfM{5(mB`Q^O1g0+HUCDbODAJ2N<$oP|Gc#-CKSOJLLoHDT(*z_Dh#lOGJJia@C= zCpshV5;t<+A>Scmzt@mv`p@aT)P{l@&W zc=S$Vu&lAv%2LLMJQJy)*5FF0Un4{OLQv=5>eRT%j zxR24UlY$$M;svYUD;t2V`;YK`d%%&-i6eu&u;A?L>uY5--j=3pXlS@T+q1vgz}0mb zELe{X`*j<>R~(yH4=#KNJiIrKT7ur}(64oRsa&mp9qw9>H~B?hkekn+MR|LBi@jZ- zGFoqVwP(MFllP9*Pmhis*Eo~ixv~EK?WyWcuH7xWivFs!#j?CS=cT=R7| zowm0*_q$)Eia!DxEoNdt|B!g^u_K(IJ|n{QDZ-8XbbD98sm83^)eD_e52+yaPa5Nl z^S%?si=Wo1jeJJ{XjTFGgIPM>Ez8YzC2)eVCEYmQ11rZa#>MS>bKE^d%V0f7=3B*g zrm#iMgnjyw!r#!uGB&3#q2#OIs@iXF`^bH(`skMTCY}Kt7s0{W4HuJ2?B=fm`Y3WH z8LZuzdY@)?W2ek|sDRRoZzRN9i@@>9S+uEh}L;`4fZfyzl_5;CLwa~)Uj~C}r+l|v}TqJgy?;C{ks0+KT z52wcsV>x>t*J1l%p13z~9N4>*ZX>pqoQOT^*NqK8%d)k#wfFq*7KiJ@j!Q26w3@FT z9=Y^U+V{cT^|1K|9(nWY9y0ykcE5K;2g$wuWR>)wDsSFiy_kQDJ8lUrshfI$id$Ue zdBl@+75~^hD*h@h$qkl!RD>VW*N5n;aU1y?PL<8-jf(++0N1q8Q{$#UON2MNpdOqG)px_0)>NgAJ&8)AA6 zH-^he*iF7}ATdXyD+iy9IT#!At8d=k;c@cRO4J6mmh^xYmrQ#R{{`dm4i z8Grwv3zr}XD|{S@oN(#q!S7(L-t>4^7|!XE_50dI6?u}TJetqfs8zM{&+2{AR~b1_ zhh{<*DWvRa7BPDWTPbk@hc48*L{)om!+ud9qD$zb~O7{l_(KZ zHqY0oRkiW=;j=z5%5j5MU|Mag%w`#$xvpglSyOv2?nRDb$6X!Q_4{06vt=fueX@mQ zaYm-kZ64xgp3t1s&ru>Y@K`^@ZF`7L24ZQ5O}qzl#hRy7!&U@O)`EPAQZ+{Wo=FD9 zGo&eu$zaAdFNQ`j=`#m@D3$m7R1dWoK|r8p#FsDcs>JeE=L;zbS_XPm;tQ|7(eAe% zI4{D)A=Ewt8{X1v24VFitAvH#kds9foQF=HVEp(tmkA7)a1b)?-UM5@G`Y@!eh9uF zV{stgg&P%w-$$f3D7SiVGn`3nWoaoOC^+cy)@1(kXKi0!S)&tKnVFdt6%`2W1bHS# zype|XEO(Y~=|N}dfmby-ew_PC=p5O6j-T1B)cj|ucm=_%ot+n+E1&d}V9Y(X@9^T2 zj_Q+Q4+_^pljIJ*-tN$6f2nyWV_sXdTIu4IGbS78+3Ef_Nz6~fPjRBt&G-@p?=%<0 ziC(2&$xx*V(AyFfelAU=`sE<6-SMWS4*VDGx6B9RNi@wtk7Z7EuKu~xAFna z0}l5j&(vX=5t!lO79k?9gj7?@_^Bs*QAg6KnDlK(SdOC%rEx7biq0@uKJ7U71n^2@ z6=1Po5SHsXOyDUGMtKXZ?gAApB* zqvRK{&YYQ7jt? z?m5X&n@yTxpJ%N`e%VE(SSrpsu@+aGFy5MH^@zWu`<50HP)Zrhf>WR()69>Dxf+Qr zz>EyRl`;H36bO#bS9$y_!HeL+q$b8&{6Wa%&$5GJK3AOY++h+me?~7#@6+`yZ!(G< zAGAv6xZfY1fE?7DUYvLGN7GS06irp-32#mEmM^2Yu^I2x2-~Nno(`!*TKLF1wY$(- zc6?^lKLVROrY+Ybq!SD~3OnGTD~Pi1cB^%*?Af`{sH-w=l)1dr5KW>VH{!a1yTZH| zAqjqMQDWFF>{Xywq_x;P=iWc3LkNQYn7@Y+Ar5CO?q);`XUXxQz&=oY-vybMc3SaZ z95sHl$4S?Sac`uE?q$3T8)jnsOV#_Oedhcs3Hjv4W-zcC8^yc8}^5J#@+JBq^>VjRVTVa)j9!tG&ybeF2H zP<$}CedTTk4sjtPgQTYR0>*w00hb4zGDl`-%%L$qN(J^67*h)^!h~nd0EU3#M{(Q= zIE%98VFBjhHV@cWH*n~bfyZq+&kg+;jd{4v9`K6R9wEV{w4J3H_521TQsVd@zu{*g zPR&e}#mBC{Q??4AQx~VMr(L&vmN>)AP>`Q~epxr3k7P@BqVZ4W`5v*>i5^UAGW7V= z(()y}TB6_6UdUTnVQl(t$A`Ne>kh#%3-!G%Rx6Wa6sJgDuKY3Gu4#DSdc<-86Cnk8!LJ0}s;#2c=c+bzRVeT5c;V*QQ% z#*4LZP999!7OI4e*l#H#C+MHz+htfA7!H{)qjTg5te#FZYewW%MIsTKbOZMJ>dS_&u@gqZ%N;%T?@u+9FRs8ER+F%wTMbu6#^}u!ggW? zw(fU5L%){1w1<(etophc!AFrxYAZ#K*=E%umJx4SZtOdwb1ejyvl93|7R?lwVBx+B zKj)d3s-eXa_Yz~N4B`pJ*k6Xl;30X6M1Kc~hPDF5*D#aRT=hN|2w;^%3|Jw^u^-zP zA%~*Dl$?2nyH3i^^f2$d_zv~>afHf1XHLRr15LW%{Wpb9Tlioj{m?Krwr(?lYV3FK z!nqGuaCtw|XUI;Y@ttuXRa;-vK}2@(QOo#T(-orrrl%=6PF6&AEg@q4q?I%}=rcbn zje3-{-=%1hi?b0{RZvGy;#}UeT+=mzv%XUC4Rtzs@ZdDe2+O@^Q7a05)=_98kDoyZ z?Cj5QIn2@tQJXM%8TKF&eImM0ruE)y9V5jMAE9s#TC~ks)MI3d4n{GAA%7?cx0)q) zW+_L`cJ+%;E-xzc=;8bulloM_WNVa0j@mgXcsK_zG%mIlFDKKe%2Wv>zRqR7EN9^1 zt!25jXr|^gAeRx3w@!r1iE}WD=)O7BDvDoP@>_@WhU8P_+x9Z;zW6)Q8~h) zy{I;5l^=&pI)D#F2Q&>m^u6B+(`r8E6+pEKEPCuGIU4pN8iQbl%*<+18LUK%y1aa2 z1|J{lUBkDYb~m3-cWZS!EVNppuE-olZEfgHI=y~8ld zVc$#0{D`75!VQhZt6;nWFX`Z+=V03DA*TXH>GxL#0qC~LT=1d<^z)-& zktUT|kdW_K5Y&$iBKet!;``8wfxi~ZlRfweMzCd%{5Fw%G=gF8?U=A!M9xU-@BBfy zaBX=N054Lm>0_DkV@>e~^JIN?i~1R*H#^Jr+>h#{D^&hiy~bf~S)q8H@0P8`sqKcW znxYXc-r9>`q+nFgk(-Y4`V*0RH~o7lzol=&&@C@;O`YF0|i~MJmArAOVCa^1Kpnbzr%t!2Ynw9@MoBy z?-x(M0XJhcgvJK!DHS0eK4^ea5dfTM55dd8hRbGzE(5{fq8*GY(1<98?}h&RDI$g? zH!t$f1TmA~H`=+dw}QdyvAy!&XNZM<2$!PRR{_+MJ&3D>$B$EUNBB@a3M6Ir(S^W!i`c2NP^zkL1@VSJpP{{g{FtKo?e>x zp79>rU9Fb~+74!hu@dQ>Y~F@WNG^%Ln0rbVW+00Q6pDAqea5##$b0YE%;Tpb9lwoVrmsU^8FZIrf6I{TS zhM+`t{@=|3JMrI@NqJvq6+_T%ns;3o5pTd^#FlSViF!9}zff~GhUgvoa5#te%;=1Ig2p9S=0eyxq`$Iy30fE4oeLcd^VXwm*Coq_EDGFPY;jPTf$$Usv z(Qof0DiV$^>mwMw3`qY>*ge-KwYkeW%=1Ect*G%dAM4pgZ(}<)iv@=j!@m2}H#$28 zqq}6x_m|`Bx=?ms&OFzLR#Him-;*hzTP`%-=23%C?o+rZ2PDAn)Zw;VPLA z=C=zHrIc=x_t-rv4|5@5uj!W=#a*Tk!?%#8QUkB)yJaoq*xyPnIN`(ECmu?SaJr+~ z!@A1NIv-uefv`ZdK01pM`<%m9r?zPCT$a=kM0P#zUBrRl1A$DG;o>s-L^2`UsG?Bt zL@?Q@H`g~DWQqi!7J=B_Zu`aT*P^{j_)1u12Bz@W;DS~8pD!T`Ab%%Wg$Y5G#NkIj z6E@_Y^HkmU6!8r~L|i2!v!G8y@ea*d8b*Y}LBq#C!}s05R#$68Z=dE17SoKeG3|+$ zmuF(>|EAb~II6LvP2J0sth+GDec$?^urY#NcY^o%2LZ8#Y5 zaApLW+eWWlMzKp!^*%VNmwbhYam|gK#3x@9hljV^Qbg&WLYb=!?9j)$@8tI=?x$@B zF`_a}#Ha&(ie3+^j~;g@@rJWrvP%+QU*59 z*xrlEs*dr(WNfQM_@r|L?xWSBcn}MO9ay^{D)lPG?q{XmEb+D~>EMK&YTNMtB5gl?6}DG&&X z4Z_)8glnHdkxbkoCBNO#8<;J45cd>Bw*n}v)u|twX}r|bM~SJCXvIfp9((~OAV!54 zXXr|%5d|lR5k9F4k?5SiUJ*DZy}uj#_6mVb!2OHwKV9vRx2gCgY zIuu38QGQW1g0yMcL9pW?TExpQMysMF0{|wf%Cn<)rC(7+7cYCs)aKv%7~IUgEUVnHz5C=n-&Ix#*1_2pbiZXdG9d zSnG~2EoSsQ-1(96>wv(JkJ-;iQ)c&$aDicOZJBQR#3z0dENQ?xtmpv zFN~_rOCELjJO0410-QPhZ*vy6XgkhQ}zqh#!__A-FqZZWEq%ZOEn27w|HtFBn2% z3Q_M^E{G7aQOwpEjUq94zM3iTxptXxoijC4ceeIk6|HMJbB1x8cw?C;(+FlXtkl*> z@YnJ-eaDE`okXstP3_m1+OIh4VmT}5rD1JVTU0&CZmFxf z)Qh!~?dxQh2)b7yuJm+@*Pm30&JVZ59IndQ%t>t7My*?Y@ULoelE3M&B-{S6c8IFk z?jET@Kp_J7098Cdi9z_P%~q#wCWN)G$G>UfJD}g~*Vk4`>J08a4@?*9_unqsDv|yn zDr4leT@ij*{7qlTQngCUEcM*F(Ncr(3FcMdZaYizp|Ro5#)V>9pY6ALKKTxqBdM72 zbghgO0_@_f)Y)xqZjSAdhtKyQY%lR?bHY>(B^sVNc1y8Ec3J8|JPBKeZY(M5&0A2Q zdd-t7kD>Yc>L@>oG*LozM}?ax0RE8I(?MHkD$29QAz&Bf?uZ zR0%cV=*2WD^)5aX2%zWcFBoMoA;9cECGxf*t-ZdKa^RDdXmW7?fHISsUZN}S6V+n% zmiLp*By&VMYJH&Cqj5RPBS{P*pqw;b~sO~i;K8;J!-6Jzx7NQ|tDd8QBB4OgvRY0Xc^Xboz!j4Oov}j<8AkquVh^SqbEIEED497xQ zsK|Y=LqvEU;pZyRb-nWC&dBE2%-#!WVV#-$YU^*iB^3xBL$>;c7W#%{XMOUfR(~Nk zKyd#G{QemQKurFQICxKAC0LuW)?Totj$)p@k#Z>p0vW-%=BH4XbLpS_AsH24uuLcPM}Vh_88+EDknkmP!g9G)Yt23mzA~54xPGYdSwG1@ROCP{1k7_DPOQ+)oy=%z_#2x`d0^ScWoDhvy8~C3fQ3B%z5(hQn6FrCMir=L_ z=0*AKH>iv^M{P5K`|Ci_X(}bUhMo~i30^6e%an13Jx@Z@&_eV}!if`CB63HLWP4dF zOl^u=SgW3Rp)36+i38zvE7iXLdY|9xb_cUqk9~~zq5bL8T-tyj3&Phdt*OPW!!05H zKNZ0BqTn6Qht2%6P3EMjCC#0r{plxS0LCv>=q^+^%ZOk3krCPhvjU^{%fe9J$$9>x z#A9n$5sE58UHhIT+#so4Zjf&j1Wefi;7qS7yyc%I65lK1O6C${V_&QKI#exoYHvMz z((-NcRp8{S`5tD3S_Bo-b3)kX;WLr^#zQfW8V9s83YytoAXIUeAWQ@Wb;twIg%tt- zXCTI(GC!1=@<#{gzGK1bx8vS)1&q6ZBW-L6G9EnD>7{E+`0xH#%rY1@=!?U~eLWU> zHCnkIv}zIR>h&QXhA(17_~RvJZ=gfSOiTWKu_F%v}r=S!?P?i zXXKjJ;P8i7*_o=9H{TPVu=gWwfz67?d|F)zpcU)bQM7IInLXiInOT#$rODh!_Xdm|`o`ZNAjq2Mpv&=%9O!*+PgG)_TjrvIuX7#PP z_4Ls7`;Gy%hG68%^H1#F>l{CCJohLHo#C)tjlXD*q`0C#%t>rRRKhRB5E1;F*XGCG z2taS%Gc5l&ADioGTRsyUTTq|*PK_%GzSDXhO}FiBpS^G9qVA~H+PLiCY(Q>>_FExG zXe|I5jr427QuYdkQlZ!@I?QPLKrngm?x>OGU_dV~#!J!aqp=YDbZ$;dqnId_U8T#> zT?u#hear}%x+%AkX*-bA4x5S0?a$nJV7n{R=$1Ltx^Dk{rm+M|e7^^T51#X9sqx@KJWBE zXMB%IN@=!wWx7VG&z-=La-;+!!W{V<+^6S2d+bGj(gyad9V`fMrJ(8h!#c5E1z-af_ z(IjO{6R{Cm+6XT~L(BABBp|y5=kAuxMFj?u2XDvjGqyRG*TaIK%{dP!E-HfP^fI9W zkPI;smr)4`;;{}1t#7N25p8uCzF+sq{1?Eys3wl7jRwrU=4nMjXGFNR3lx{{(=GV> zd=z6wff2tv8|2;qi+b}YbEmg3>S zeaDn&{09%AF2NaF89{MX<}4-zrn1s@OjJA*$-s>8R^bMOJowp(6C_4`zoMeEkn}14 zOZtp^`Gwudy>FZM0~qt)5jSNs>QJx*9S>yPnT>%*$!&aBJhrS=lp*^)aq;eQS^r9u zZ;QkM#T$*9e-RNByi>5l|KXQ$MZh>$duDFrq^_0FUqIOUrseOf`tP})SvpLbOuYqd zk5v}@cat@5xo^PXa6%8mfg$$_Lo#8s!iIJ-bQ{WouKzc&tImjLydc@z<}!Fuy)ttw z+x;W1|D5TM&*mSbMPl;`TIHE^FKXZU)Sy;19_=63!Bi9x`%`fT{m8Edh-lL5S0`yjRAA0PG)%K1k%0I`1yeFXZo zex7(->}`oFt@gIqt`WYokUH?k9_2u~0a$+&s0<7&WwhKu(4OJR*GOGGEibRi{dZPV z%gV~W7VeKewssx#G2ChNn8IT@v!7PTY-g4s7`|mzzZd7vN{$^8FVQtGQB1x1ZiMa; zb3Al|ze)y}Yk$LMY@*PNLA)#lOagM25hek?`-O`rQsh4n_RrJ(2J#Jv8B$Yg9jhyr zO#(cvP;l+K+m7j-J7vA%>W7SvXkn{PW3?iIb7+KLCCD?KA2O8L8Z4?R$wfKD376?_ z-U{UHmo;8VE5jy{6Qx_kYm2_B#q1-BLmP~;_bHkNWjt%cp=XD<6d)PFfIRqzE}`fb z|I#xc`h*f{`~&Q;Z{KSYv`6|6rv+xefZ}cpVC(vblaXw+))txQ%hv#!<%beR6h8;$ zc~2+BNGBRP`p(&|KI_VMcv`gn3He>xc>4pwMvQp$UjFzhx0Z;% zur5|2dp~Er72lNhv?(#*_7-%U*YtZX?Mo@Lc$Vt}_i!xcD} zTFGQ1k*kansmij%LU^%uygm*^oDb;l$fK&gvT%6yP084*#P6Bu-C3N~%P(|U#FmT}h z>jEEcb#X}8q`PbBM&q{rrSRQX%V`jz{*OYh> z^)!k}SWmG_V!IjgT$Xd1d=wJa(Cqh1b+JeRORWq`Px7-UkC9pSnS*{ z{tT?VR@+ba?MjEpjZ^!_Y&|yV=M(XLl6f;n#mY;jiYl7UX?;>iYLy>j5Bu3#>%bR0 zrYP!k<|z7@a6A9YPt_%-<8-zJd>yxL9By&ad06|bPW{cL!#@-{g+!tCRK$!B0U29@ zsy_hqK%bW82O3gsmYnpTtqE;X(0KLl<^UT7e&et8_(yg8m%K(XA#7M?;`=cc8^j=6 zxAo9Do1U{Ga-B8w@?g3AoI34!NTxPk*_4eLoBQHZoHNWChEaSF$9`F2OF`Mf+5XL^ z{pJt7C$WjvAE}$8wOB;+6n^g(gSc*&{$rxD;x~(|iY~Z z7VuemxDyeDkLG;vGlD4gi9UIZTf;1nrwsHg;);h`3%b+chWs{o|5B`Qf2Pp-J5z%% z8lRs$;Z)7hL>6!Kg)t*)T~8Rg8IS8^BUcsruJSW%qo)mJ$8|0Nid!$D%72mUxM|#5 zE23+Z(VGKXV1Ovc`rfOji8p-GGah_RLo)#i(%^|$v+b+w2d2CSgYZ6+?%t{>vDZ!O zg_l8wK|6A^B4Wk(UZXE!{{S$D{!p?6MdAi}^bT83(~@9w7q~ zHlws}ggq!@IW~Fqn#3C)5ADOJBjp8A4|+fsQa)BUzn^b1;&(UT&}CfhD@ zw7OjN@%y7ik_3!QpnzQsMF*&kqS3dKdTZ_1Msrg_xW2@fmThk&kZIU6U%j%lv>Y27 zi+{Pa;fO)#`JG<4_oh~AZ!_NF443t)%b_9clGUAtyr(^98`KS#d4eMR!S*cRMU9l` zFyyym2qV%)b7p|r%~>EF$y%BfwJAxRi8d-}=Bvrpa{oXDBpi1*U)>b!B3*InUQ`tF z^g_&xBNh^N${1Jx1%KrA)YXjuckp^3>D(`37!fP$Wx!8;h%#*B_?3pa296K|r{E7EhW|~6 z`=y!#uk*)_Q#RTw>@gxn^_qcr44qH4TzX1?`rS6+rx?kbEC>T$Hf8rf{%Fn7Pig#% zh5?U4>!_w^Ya3B}=XAe;YllvSMm^Uc~1m0Vr;gDk5?(arD77cy^jSsZK`)tA%R z^}s8u$3f&lZ@kGXyR4D-Ig(-PpPr@s$yU`fY*x-oxkiM63z%)!CN#r%~bh z?rsX{B|nuz0EY1tYIhIfOOCh;evc7Q;_r3NmMfwR2Q5oSQOvja9dto}MOR#YvzQOH zaPgKNH)z&ZEc8UjFTX}}kLchyh#uaF_s|;pzS>7Z@962MPrmQN8pzAzZ~1NS1p6yc z;gVE`23- zWw_dKIpB&ZtXIHa&hcMLGKJD%R18Dkwxc7xL>UMeOH4lCiLRXj`s@S06^?sS)h(LO znji;C)ZIhGB+?JXljp}@%&gl)2RA=Fm$&~i&Gy`G=5yT8<>h;FlBFTMv4>Uh>c7(h zuD-tTWA$Bx!mve8E^4=Mt5e@a^F;oMJ=Tby(_xh@+fJf8xP4ynG5W1F>`#>oKGLci z&^{{N7G@#bFO|>QxDi{LJ|NaQ$S8pogbX}5+i{%8TWb3TQl9Q!PKFWv>x;CIrWrU6 z(2I=?#OhLEI1!@lqqu=+1|3JmzR-r#Sc7#HR{!PO8mv^NMB892V5|Sr1sS$KW zoeFNtYlpr{W=(u1>To^Zf+wZ)c}gCq&%0%gs=zx}ey>yVl>gejAA+)p*=4@3hLmy2 zyD~o#1XF0QeYn!X+nSebAQcQjpqx1La|?L_kQSl=Q#ZP1u49dxd@kX|`XNg$>vJ*? z6zl_w^F}1K1dU3L!Bhkad4YIu?+lU{#UZVThlIVZ9YDfrgFHKHtP;WKVgsnh@)^bh zHni*2TJ+5@`fURAs`Pvvzi?$il%x{I;y7$WJ>Kg?bl;qqYU1$SkE?+;C$AFX&7q{9 z|5;x*V1PW*A#nMJ_Ec1CJB6~8foYM{e#9t;4AY_!L&(d!iaYF(|mK+ASki~~B$IcH^6V8K7v}g3@uDNy{xj+&pV2^256 zT}n#YzI4+x%|9YBg|WS?_rNQd#ild(gBdWRHzB9 zD(HjNn+{`m36fbc*A&&7&v`I)yw}Az_oeuH40t zuM3#Q6rX1L%O9TyaP7s%GH^72;_CT5F&Pg`BdYQUp#Y`;sdC9db*nfvFSqMP#GkO@ z7hI+c%YRnG?T1L4N!k8yE@yq~#tn{|PEC!dbD!&XECJO~Z=Qx!J zg!?JPB1H+{0gpYtV(51jqJmK%gbe1}xkltiov(qP$JCZ0oxzWu@p&r`2H}P_Pc<%M ztIFNgd7%~YPu?(yeQ`zV-G%CRAjO3OFtLIt8w&pkg;MkZXz!V_kE7*-M`gittbREx z`pxZR7Zy@q{B{B=?pa%Ze5a%|#uEmi&gm~(!LS1HFPyj>dg0 zzPzinRrT$lNyALTG3|3(UqXCVBmJ}Iyoy|(pyx-fd)zjV+|I zpZk#HPOJEHfiMaf^l_7klSmlsrKK|81 z-t9@pSpJ)hTMvfx?@)5MGzd}q`E06{D+C>5nGqIAB<9?8p;Or}87@-uJ8VAZo7MTk z1A?5gxo|pS)0#^DnPhlH5dhA%N!c?I>Bfa4J?06J+zXgXER=hwQ5x1<)RtC=2dQ|4 z3DR6xe3XMhqcbfnX4StxDX~H;VlM5+cRu`Q>i*PO026pfYCL|tLMpFy$P7E*MmOb< zb8TONRDXyxrMe>|I+=-eDdFqO;U@An%j#^OMTOlNUp7-Bj8uD+$xKT=B268>lx3)d zS+y&f1|Up};8Qz$lEWcgJZi!OWo4DcY_ zfRx`~OVpnaH5~%xe-sd&Ktf=~TlFryeQ+&spN#jTA2uPxts2)pw?u~QFKR~<;p@71 z7JTT%gz1^%Q!~e}+1YYEV%@-JuB`vyR;0`tgi)Z6luLSWW&~qlcYl*((+R!h&riM- zre>$`yWP^974ftsth@^@sO)gl(v0rC80{t9BPbUwht)cSLo*3y%s0FfEXeYM*ADZj zr<~;d$hrVe8gROFlbR9P%XJuQx zmIayCP2P7+fk^jpwp<5_PfS$H^%v{Wl5V3G!TB<`7U6q}BsG8qdDHV2GIeUZ(lI>>L9m+}7ud~KkM&&vXKa}8fql7R zjU2bYR7F(bh59qt)y=@Px;_64*A2mKUld)ZJ`RM8Ak&P@%;|>Y;$n$A!DMX@4-K!f z{-!1>Lg5!{evmj~WM7z?x(6Zr#UYFMoC)81rG4y5qO_5yCs=Y{>`%-%u9A3%$*Z@HBA@pS%oG`@X5JH|#BK z!lJ$B@LWa3Yv(U|_+2OrsevC8R!~z{UvFp-BGCWWzXYloV^) za9w zZ+rQm7uIoFJVT#eHxb;~KIM<+AZ~>}f!_d8ChRE32ALYqh>wVvcHBsQ{aQokWz^VJ z^lpFuCFnXs z8*+BMIBDLv>oVytvE5jm=Pq+MZtxH$Ih_~PE)$@Eu}G}PD(N@Y_nF(@I&zdjsDChe zGF;9I!#^9PIzvVPR$?GK^ke{ZC=-Ul@H^;ico7cdmVZq9%z>cIrV7U04Z~asBTEJ0 zR?NDVZ^rz1E{6#q(3gRX`KkMlf&K6~i6;k>5hraDBGG;~S@4UCiy#H&@hKFW0@Vp6 znqyD)y;O#--J$g9l%=%-0%*sW=jhe7>{vd5=QbUhwbu!v?bVk~E)4BNOrt1kf^Xry zyUnnB#ww$?&JAmF@9{J6Gob}=-w?7l{Si8X@Auj60m;0_NG0r$p*rB>gWT&}b;}K0hLVs#^?#Fob8$nJU){(Y5uoo1<3Od{H z+d5A%<0B4M5Am=mXmn=}I``h*4*cp8sW{9m^3n6{1j2+umr_o2O?|yaP|1fSxEps3M@y(D z9!H+gyX;Zg=iwQ(S6m`|1_pKkBmSh%nA<&FF+lrI`UHSYc$pIzO@pJ*sjrLoSV0(h z@)h&e|^LPM5ZC+j{UFAK=Q%6MQHQI)fAAd^veH3-+o2XVOZ z@0#QY67#4PFT&PVXOs}9MJ%F?JJ0CeJxzH>zo|Z{t9u&(Q3KEAbG@xTz z#-j1{YA5^*1nd%xr2ZFUZygnd+C>j5DJ2NfN+{AHARt{T2-40lN+X~&NOwspf=UPo z2nYx>#L(R#EhQmHcXz}0%y8BFzQ6bPeV_ka7i(P?GtYCLbIv|{?-SW?5(dVR;OgH< zf`F+jls|_SH1AayjjAsinUQsd((jLc)KGE6!wSRkvS0~urmCR*eDRUo3ezc>KgYZw zB;W~>E0@IpGJ>?y=Y^UTI_d!JSnesm{{(w3A4cXN&fvDjeJ;k&_yZ$nHTN~iGjqLZ zza1RIj-cZ=r!$L>QxakC4hC#H9TH&1?{9My2}y1p?qNLF4a@_>T`)LH=;fXxZ`_g_ zK3-Zv_6L)T7*zZb(SfFnUf-AjYV`PXn@3*=%-{<|B`nYGgJe^yL)bSPTEcAKt&q?JE)1$+%cs)tv%cjQ_k4m!~2YNWWeDsQh>BYGiWZSPkM^l@#>P1A;fM&W(pce!2v76DSVK}3E!&@HlvGB!@M?@$gLXg25 zNTo@G|#^@Q!@v$#hIQ=bB;yz&q`c5qD>*3gZhjT z<=pReTD`3Ug{(L+`6h8>m7 zTnmBMq2y4ENY@&r0Nl-GwmXWB|McIW?_-AQ2f@Q(3$hr%j)o{*r#WVzmNomvkN!qP z{s-!}_*mxFECD6>h)8!J#*251huQIiDY1TNXgdu}pSPONj*%DxHL3h_lyYK|49?No zm18%$PAJI~PJJ1#VwFxX7zm<El8$vRv|7mc+J7DteCUhb%0@bYl zh6X@CiW+#&$RqkHUtDvDsy>Nxz~_3Fq546%j`LGdG%qWTv#~9y-KG{!rp!nkCXi=b#rT zML;(qEe)^G_7fGSQG}F=mVc9|^@k!}s2a&d#dnrkFX{=N!9i6+vfFhN#T!5$GWP$V zMu0O60s{o@exiH%ns1hJ zL#uM_bGwZ0(>dUW5y>rS_~FFXz%^M;;fp>m`h8f~{GEs9&A|sa{}>&922{N?>0*fMNxlAJ{)k9JUFviy@q-3x;N` z{x>RxdWJ)Yfo`#1sMu+DdqAjANvX@-Yc5X6V=wj1alntFB1lkVk;09z+v7pm?``ee z*ROXEW@X7E%Q$=N<8nzJpR?A>N^7u%c1u=fd}iO6 z>^`>fq9~4b9_sl~RYwaNk6C&DAU|kI+3hBv(T8(=KVar+6=+83_5V$k1XFNLa1t`- zs@UIjTrd>>KLQDjUD}g#8$!m0tZy?x{Zk`T`&X>JwE08?9odRcqt>cl?p0}QZEfxB z5J_L*P;e)qXt9!%=L^4H{2&4Y=LdCOck(s^Gv!r9jcu={I~0bUDqFeBFi(BFQ$I6; zr~_Vqowv;945^fteC@{5O8EMU(9n<~!?T>1asbV{`s@}rQeA8yUthqqI{EOiaUw;P zW2GFmS|k`Y4anj+{$c|i)4+AUo6zPFBNr*icFvCVE~`gop5lAQ?&IRNYvBKmyZxc9 zuqDMlqxJH`vi_`#RhXhrx7?ES3k%uaW9S zFHD767s(-|n(sZ8yF$Nlk%8r|!l@Zw+#)XhAy)z>9EbZ2DbGKU1e)UVues5Gfwlj9 zqZEe5L;DX5`@8!qPX;!X#ym2fE|kvsjQ!fJze$<51necidSu@l(5D3v^s7{PKKoZ; zP3@gSFOUiRKB6@E$o3rUM@iJ1`vkSvPnD%Sw`N}CF+rz4yDkJgL;E4zLiqYX0fH#O zW+fgw^87@>6r110W{%kVAld*2?sc0 zgr`){^(u}2t+7eayy23-?}*e^K>%p+p^Zq%h`R}-V)ss^&Q}20;GFRc0LX?6fDUN2 zd%DPH=vo~7K%Z;0r#VA~f|Sv`1TZW0SHXJWfJ%4&FVvR)!{A7reA-}$@lNF1)rq+3 zy-hAj=b0jUx`Xajr&&r0#>(<^7I{PeO+PyRk_ioc;g1%KWE{ z3z|Nl=6QwsL;4gh1Aoi}ZSI8hJNFaE_6_96{%TiMp1JdLsJyg8r~ZS5P|`QnB!JPg z1F$7N;2Q6XMr>HpY6Ms_k{pK5^m(x)^KvWb=#9e(a%D!jfpy9)eR(j8XjS%7HVu?9 zL^!fq$d4;p4Lsd)T<&B%+wS-k*Rc>6nJ{6;oGPC@lf55LHh!IR(TT^VcOm*W{b z^t)MnHs8-rnNw2_aNHb5-ETjrsG6SH2qK$ zN{hN3fh%ycP}l?$^jj8K|BYs~ph&;q^A^>yU|hxd6OjKRbNySK4upf|_hVs^k(vbs z1&+Ifl5X2QuUYuh)1G^zr0C{YJ3BY{0^5!``Qt0N9=Gq$3Gl@)h+P=7IpUN0va%YT zdmf8t4uQy#FHA-7co!Jhu#>NN1cZ%5k{Md9I8nDA8%z#VO-wHM-vZAkpQwaBbi4%S zp67$m{;yIeor#Mz{f)qD>O zIr(hKBfVyKK(+ZM`^I~*@%NzY`bTl$OUm(;WT2;~x;q$RYio;v;;;X=pc+kThuEXd zgdY77x`f_PK=|@TiQ$%x00hKKrf>hpg(+}M_NNlrG92~!{M>e z#{vakF1Ke?VylEkO59Z|aw=PGX?cIt#Ap5d2d-9H&tTwq6Vz6MT zU1VrbN{~G_o{IiiYLVFwnXD)LTulmzj`7r>kE@BPS-g`XixDi{@wxjU;_+srlo{X} zzexmQ7F2zT(hh;S3khgw!6U|xD%cqf9k4!`xgJ*E;QJmNd6Ak#H}V?l`-A=cLjH@U zi1iqVFwu=wcz8UB8wvoze=`-)RQ0UN#aP)c1sGgps7tflrsJE)<-J*J>aL?sso_v1 z`X^1d_o9IUV^)l$x+`edhCUM zB(5P1n*gmF*f5}!OX$z|w`#qGk31x>$&8J~1?p;61wFm7{r$TY?i7@ipu6qHj1g_} znV*unD)^_P{1-g|UEcx&o7B00BBM{STFE&F-jHEK_fgYR~#E4=R z$&;Qm3vqQ%zG;3PY^VqZqRo(h%K!WVG#eI$ZPNYy&yF@{ z_@lXXfCjd`ql1f$P3>xGbv3=aXi_i)vQ#sHU@nNxi=!FFbvAUkQM_RTotkKGs;4H> zFVz$QvL|%)d#>XB{Z~|j*oZ{c^{77~Rp&QS#YhNp*w?CNLaCTJ3+-DlzbI&{q!SM_ zRa1QAdB^Bs^m5}A8?!wA$UC~%CX;Oi+C>V4tYKXwir{&b4z!G!2bI8Cd{63&KUF09 zcB1G1=q~YoBFy7!rWeNQ7uKKeD2aS7eILr6&IZQa$`c>@_Dq)S4Gj9ti5 z$L(~q7s@;_UA(t)@-jvlF&hxL7Z2Awl7!%PW?-70i1*ux`!AvI2CAd^FJ{8EKe#IT zKpBx`j~{!MmS6y%NCF5Xawd|zqHuSIUX-2?$rsLC<^hQA+ToCvr!;zLGkK6?2Iwac zN+oyQR)!^2UpR1JKl+EraQ;F5*teXI`xy;)k}^+k-F-iBL`lh$(9GxqXdeFl{(ycK z8XAg;iTUBf2hgwq`D?Ho%CG9j@bdc+S!>@Uvv&{S|ODYCV>IFpUm01M0I1G z5O?Qp32$l39#H==3i$5;2EE$3U?4&jSey7__=CaoqmQ=@OrBLoJO(;Of~PD06TiW| z@(fs|E#s<>nA4G8x^ZhECMfPR~5;M2#UEm+#H>skCOVm(qRoKO2EH# zNq+&!`P?Ql!vX=x-^dWgMYYG1k}=h;q<#J*nOR-$?uv{UdvB+z0OrAzqDa^CsJ zfC6;gU}lQ`Df6X5W->71V8t@{aI>Nzgv%eIvHnBJGMI)$fWb_vPYPkiFkvPk!$R1& zc&n?cOZKLnjg5`Hz5OS$W7kPNo$tiHm7qExtPTTT-)j>1a$5WQ&Gz^BhNl^0sSbZ*T9oMX(~mHQ<4k2~;AJ+Tznw>ggbe z)r|QX`L7+5UHa3_lQGGUTd>~kR(}({JsT8723(*03-C{<`J}?PhRm(x=O5Y}mfFm3 zLUFAfLBG?3foi?cHXrC(dxOd1HQU;0aA7S?yr=oLi!bfS)Mw`?f(HB zdHn&7-cejs6dT8{zNGwm`HJt=eL4}w93bNPzPZU~KT&h5sp&yeQvyu)^vrZb;bX48 zrNP8mnw{pmTJO(q-n>B|I0bciSR zU9L(P^DHy@oM!#9ErZnI<>m@PJn`|#6#U8As!!Oe#Mo`8qE4sT*72W>Ix|G06ewLm zBQ9)4UDX&xjl`BlL|l)%l>HKHqtX+UFHkmlE>lH(V2Jk#)R2jf|UW5Y{B#EFf z7Ba-^f8HtrLvl8dJCZa~@sqDI}B@(&`VL=$?Ct(wS-R0h%pP&+pi8)BAt>x+O6LUV; zq&9+pT+m|+F)%Q2aS_IWZOCFw0pg?T?S@RR7X@xmCHVE_Pik_Mee_jo?qXAJ3ra$g zb_aUmCqF7hyNVpObrwFV8JI)i*7~62*1s1_jstV3Tn2_d+JuX;KCMNe)8c?oFngy9 zC;I6Ak?DZ!ABGH?>d^TQ4l(~z%hi|2+${y1sLwU$3})kfJ7V@ z7nj7czC3*&h2$Da6l@SzHb*x>HkAi;9XW zDk^ewZwm^4pyMueuX{mU@MQm{^Wu%2|VrX>G5tiajR>H=<`+)tu>cD>&~K+#+#r)g5mLZS;>z0m{QKB}JMLqmUWfLxiPdAfdY%U@^d`?e?uy>IS4I9Y?F}cw z+l3OUAObo@L0%}!n*FGYdPjN$aEibNgIZMqDGbEhU#r6vS__g2O$yau`#^+FoOUri zV*|eWTNoi;grhdoMJT0HI3Z6r6}-N9@LIDx`j~FkR3pI;Cm6{Qpb5?b|j0Gs9Q!_0F@<1BBG~=a`HIvGMW#&rN}CcoIZMM+X9d zJbCg22LP z5nJ*@TDvM|(of{-gt&$?TW^)Q;P;j4wZ=v(mMY8>kJBAy?klFGtk?TDn7Nx`XC=1@ zSusGdkB%7=2G%q{MH=UnujcclyoK+0v_P>Icws`&cu1oeDTgEy5I6vbxOa`E&C;^f zQH1dO%=@CmVrHnRRw|JeIl2-@mn&#Kyu>S|xBDzm8wm?G$SshjyuV&l!$fBKyn-KG ziobnTR6^oyn`48St1C@x{5mCH3Q&6b<^8M(GcNipp3vWOds;5+yStY}j?Nf9PRx*C z(zi?Q#PQ*p5y9lUBQ_QcVoz-_baRS9gyrXx$%TOxe~+P1z|4z|ilQ+0_57?F^14*U zmiI1B3GUs84i1IMtmA^?V&gJYI;qbvZ1_pGD(;efqEaU$Fa)#;n4OljLGL(=v5v9j zhtXgDiIqAIM4s8qEE9nQ!4TakDcxRu&))IU*6c$QtJ$48Fi>|@xftKT?v;2IFz%3c z0*3e5K=+CtguKz4jZ66qUGIZjbgmG%p=y54w*1!{OA<<>D)xR_ZJkFk=G{VqO>e$}bBU)5iJRX6?^a-bQNMI} zOV>>zu|vU){L5E^aTQ+sy=JUCv(m zj-SBlym)&s6FA|5_X%vkN=lMW$^@xbC-E~T+f_|NA(NFlyfvxc*GyboTvk?gTt+NA z#7@dPtfsyFJ=(CrC-mq6CA2R&t#-(Ku_wfJE#rnS{ElIvs8URMS|>Lde1$Haddd)i z&zhRtoBaVV3;2+&MgZB&u9f3LL#soe#Y4olnonh=#pBOJNOip`NL(}fRYPMyjmZ4N z`$OCGO+Ff&4)QX@7Qa|@Ractw?HQ9wg(FzXl6mpQQv#omdb3NZI2bvczIH>431kl` z@%@Njyb1W0C$fI*R~_w#sL4&My8#7w}iy{1{$+cdje^|m)R z)8)*^A!~_!z}^*Ze7U~f@D?kD5h{tEbkm=7>z&T%dCrNBbti$U!8|;9dd`U6jsK>o z=+Zqow;{hoXg<+4t#6pIk%@_7Auu#*`U<67-vAMC{MzDHc=*&u+U3|^+ z_AUv0HTyImp<59$x-**M_l#fb7u$4jf5M&As-K!;{P=y1D)q@j7gV6kLByCY@TKk)pM%jjE}gV`89p)U++jjdivEC^Tf83U5~ z^NWk>rfwl}TdwFD78VqU2~|@)@LR1N|86t*`WAGjjmLdr$;o4afLL<@UX7UMZwS1f zi{t!ypbeJ*iL=k)YaG3lt>HsmXZ$XwZ0AU7re5`;o@8oP!eN=$khL>ot)B8Uk9P5A zGjBe5+-nq@j72l8j#!I zeL|vOyO40ft6R1PQeyy;5W#{*Rw`T37tvoRCT6R$fmKzmcBe`bjPVjD4D%D7-y zxTh_i<`>YMqdUWb>`B+zbBzgoLqbbug7QPHl{NvEC5H=v7`9>zFl(Ot1n_gi{*2|p z>9QSTz6p`1Zle5jnhU&8eJXwI-N_E_Ev9o*RP@N|-T9G}6cA<|+@?8BkYZt6ZX|^t z&&(zLv|C)tF8{TY)5-O{N_>%k7-D!hu$FA<6rbrC#P#A>4`OLCNpeEWR+wzR@TjOv)PWg?z7H4L7 zUqxfLB}c7|)v1^&hThvKv;36)&~b}V;@5=+NRJcD@9$qhFp<7#X+KwfRePPad|;70 z-Jc?$@J;RQCLW%U-3p9L z*X;a3yr&E!OCN2y@I}e{hG=e8&gg6 z_7rUvsGY3r-lvlNP}%2a*vsSTV|Vu#g12rNplVSxbi;~N7v*skEY&Wqy82K0a9B5q zHuqyuA~?6U|1X4x3NP+j20#%gewDsRk`{I%d_jYBCJDKNGDFs!i1lLqn+t?=|!FG@@J-*`0}dL z%N))2uCv#7ls+cf<{1GKsx>YHh#gd zijlPnymTwI?QHg)%AU4WLizXGj@b#*r9I+xjs&V-)!pQ*K%p+Vw>M^ zeEMm$4pZx6;u6ZVb&Ylj z`(JO$h8fx0w~xe^tXZjw-M*=UD`d>n;h0iH1fy%Hiz3h5IM!&eGM}>^6|!{Vdwx=-hh}jR=N< z2h#+gUiJ=-5^r!MZ&!L?8t_EHX6h5qj_b+oODB_fMs`Sa{O^&fG#G!+r>5BMzodm_ z?p<8d=5oY6ptJVL_ww1#CZ+Iw|G;Th9nq&j0{SJ=j!qqXaaDu@`j7DJiMM=tt23}X zgkNYC6MrucNY9EJb=maVY5&;F-B>}u8F(Htgn>1XwyDM7T>ZU@IE{TJPq^#+*80a! zWr|(To-kDB2;aJtpLMbF+MMi14QIExCGO~@En*0DuVxmIbJZiESrBdJgoocAd#@pt z(%KL6glV5IdBPAx=x8BtV84;MMAKephL7XVg}uUr5GzyvgF6y*-U{od;2^Kgsg}8 zOuZAY6pm-AtWEF26AA(Xs1Szb3GwSx1EhZL1vp^R+`IXKKSj~(Vcg#7*y&z9w%38x zl9%WHR2MnKMAkvp{1o|S_MJa@LB)@E-KOPK97I1!)cq7isu;DX3-Jb9?HGx+vA!i^ z!P|2ofig@iT3c&IQM!53?jpt2X9W)3mB#6AC{mi?G==b(J`2KBZ@6agnslFn&zo!J${ z#b1PlwuPINiDz{@>;+28H>8~^L#dNruDoFUAys2)K42&>G}`Rt8mE^^NdOebN zxnsGSqyS>3D@o#If21(($k=@U-QjebLCf|wr{i|Cb;Xswy3O2=f5U21spAnBoZRx4 zWp$ zC8VGD@^2IxdvFws?pR*dk0Fq@U5H9w(Uig)hH}|Pdc>cXpq@dI+zli$GM$F2?-|M* zl`O3iI$Jk=1q7A3xO8Z*^*z82KzZk#n_&Q$_^*5sG@0mkE)iWA3RLIb73Ze~wYs#X zll9b0B7fNcGBAx0#-DeVJMy`lc$;>GqQku2K=p>V1T?#CRT{(L&P4w9iP>KvvWBRsIrlW_}xJQV(m=`zd{# zmCnd=`jYl+THV3P;pp4=V7s4B!y@vHiG0o~gJWq~`KUxw>x#*KXf z$@F_D-CnW!kve2K@R8=0xfJemL1Z*G5?_Hn;?D8B^I=$6di_|rimsSecZ1+p_Q%_AHHJ#? zw%k=mYKr_-m=MA1%oM-Am|7B=pY(Akqn{Gg%r`05+|jWVVIjuR78gB7OZ`MYBld^l zZ+I4P9Kg^YZ6bfpc|ff+$z;6(M}RXCPjE*a>L-7tx9g;E*Yol57Yb627Y4p*>FHnv zWph)LM^*RkZuabKQ9#pdSy{mQo{*7|y4cvKz}f`RIB(#^_h7*-Fjw+BIy>`-9v}bM z31(ncdgv2d$Am5%(Ah;(Vob)fiie|3>#2z2&LW@U4Dd$!2SEPc)QrEISHQ1L=7!&a z5ezv52M|b1iHg8!PISJi&8KjbE|2)X;rI@Q0sw*+7WOYOa~}LT(gW2Sv!4l3f8vr_ zQXaYVq-!wv(A z&Q7mqFqP<*m~Degv@R4$4yTbXi4FUXx77$lZ4t+R?QZgrT?rq6QyiN#OgGeNrlG-*T;ev(n#> zXKIP!e#S{~JzVg^CB95V#07rlIstyi`$R!|=i^3Q$%!2@-RmEH_g&=$4F$X$tPBll zKrMcZ9T)p4CZ2h~$pPn7w^Br0mheQYc;cYu{0OlA3V}jA43$#GmtX#!~X6SFk1N6-9?iO$*l|> zb}bG|?V|JY^9cxEcP4Nhtk(}06cqjVp@zE1oc>Cer4f^Qs+i)HQS_V2c#O#5awnec zLom!qSAS?zY141Bl8n>fc0TP1W4T6A2glT?ggwr?0*{@olKlBH>M6}|WgtF_Tr;@) zEKN-;assrpi8R;O{FQ@L@zKCE`jnUY*4N#wxMNB(E8N|!R%sLqHE%bVrKPK1zx%U$ z^y_YV23g@=wI1;HFRiWhm|N^WRq6M!Ta_Fmt?qq}@Mw*JI_W>f^d#m?_xHmSYq-P3Ovg zGyU$Ijhx+&D2@9f$k^-GM(nav#nzlne<3G7MNhZe5zRd3M15fNCJ-RLUoU@M({8I0WU*;xJ6(s|lqm0Q)4?Ss?> zBE)hX?&5Z65QFq4>#@v;;)c9|I7FT92VTZ?uNGU~o9N2yT!gf)p$@wn8#&`&o!hSN z_W>IbWD<ABV4>~&6Erjx~hB1|nl6D!8 z`or!oZ_CF^)naLtj;z+CFqmtxH7(U zu##?$V|K_~T%kc&Syr6C>#n{NmSb{Kjw?}is{QW~ORImiD8`6TrSk*WP+CvnD5pA9>^Z;)8(zt`K0)0=6LjQ*ijChj+w) z=8Lnvoiifpt%=Fyxw)gXv>y)y{lW4Z04b3m|I@C6%kvST65fCwemN>WNGl%nU9by! z3_;UF=u`#|Tw=BlvZ0W!nOVP%KJ6H)>&CQ?WcGT&GeGwriwMZ3$WnCa2Q!Mte5&a^ z)+hAOysaAu+UkGRBGvSu51RJpSqHy%4fLCWdZGr)@Z)0^s9<3d9w~-Q&!qg{%cVPz zopnc0d8z@DmBpVFx(y6rcZd(mp7$3F*LG4n*h`;G&^9*nS4ID_-{Oxx>+iK5E#I;* zZ|;wLOHE#6Qh12-po!|={f_h%xF3pxEeuTRzjKhFv^h^m;bpxEKrL@4jUseH+`+C! z8%|D6P3T)=leg2ehwfF_vsf}RY~WqI7X-U5stK8bNWM_l>oRlYmlclewRpZZE9gX| zGN~J-P;#n1h%LYnM7}H8tf3;3b48%$+6~Bg0&XTN^3Z)CH}|%(YO{kr^Xyn>6k2tb zKSZpYPv~?0B~qbg5*^l_y-~2x(5(iAj4Zdc(J^(my*|;k{fe9Ezg*(53|#Q)^H`uX zpYXizyludL`JN6>o?d{(Ma=3^&&w@QoG9k}QG@4|OlQ0Ze5dkrVQUTn*#EQl`>3V{2R43-kJX@O*%kFsAfeRF)~^qd)~o|S59 zz|4Nxybyg>I=}*w1~q}<%ntX8t7_#+BA4YvHzp@-`YRTwNcQ26;wr$XX=-k&tiP$h zRY4EWvpM7*$UYgc;aBc;+kM9;p5 z9OC{hxytBCY{2W1`%9H2b%jhiu7ccNQppcKUwXgELZd=T0h90}q}A`Y!7p-a(*7Gq z51u`HeQ*F?q{&HSOpHUv!vc^_V`->2Lb)?sli7wmOu7k{uijM-iUdCC&sD`; z0|_YdaQjk<%YiaE&+#GH`iYR(`L^=vk@0S(oRyvBaK`pE+j6T9yfurY29Vaq>(li3 zx@lks)94~#2GkiMit?!dk)7?Qpu*42gLEAy2B_-z)}5xs(ztK>U_;Eeni|?foxh%A zp#CHo+1}lSQ^gN-+eVi9hN~)^pKxj+S6KDCs`)Pp>+3~n1lI4Ebte)#t0Ult#wI2PQgkmbb8_N_1=gu=Ii@$DTx*Xj z+UcEjD>vx-X{(BkM-#v5Res>~^Vl)xa5^U7?yB0QXv@SKRpFtCyxkDe3^Z(z ztjr{t*!)s%?w*xPWZeSI2M?bSV{eAY?7^As^p`!3hLd~uYZIVHjXr;1go4bn^5^;a zc;;WT;e%PiL4E?oSEu8nRwro{x%G(lGS@E z4WHix4-X(E*q=z;rzihdtT|kv2}m5JXw6kHayW(Xa^_HZYDanGsi(kel+;s4K`$}( zA*HKZ|C{)pwD=Io8hnR1l!L`Ja_4uKaR`H9Y+6G4!9B%Qw=7Fkz`ympNdB$|`QQRq zkHa2&6e|DAT;V9$0a_@BjRXDI*2>Q9xQ?}$O2p~CgVrpAH52&k9Oi9Z z#4}b~+3z1x4|N-wAA8z}tC#DxcDY zBkQa1!Ou@8D(>HZ4B;xJd?ewhXY+|yE{|~J-FgYN0=^wvjF}s;Pj}jieYr;RD|0G6 zCI`(I$3_SRPlvT+vF{I99f(*wEuAn=xgKwU%Wf|Ts{A?0Y15jH8~XU0YjPe(}5(Cm+dy0luA*0R=N=JHAcNjLcB@F11_vb|yCz}@hHJ0HjH zIA`aOq{e_0=Hnwk!m;0`QclN*^rhE1LhZ0+Q@DTx#6N{cK(uf`@zSoP;6y-x zY$-hv>>6Q3ACS-Zf*VIUo0DpXEUkKPNM=A(pO0{1@puNZ2bZOFS9w5O_{qyL*zAf0 zJqPx7^!AU`dYK+PvzqrXgDt9=YI|-l75MRJ@-b+AzWr0QV(Vdw(6Q%a%fY{zQeBTb zZi@P#ce0!GdPpEYKOcHOipQRbswSbzU{VV@I7DD^t5dIX_>Q{d9o`xXcm3Sz zE>SVQ(ShQM+tn2%@)icBpNn&@9O9W!s}UYB;!lf4%!KW_@*i*oz`cIOdTgA^_`|=ho$obGi%)r5IXR?4RlBL4s~Tm+si{m1XC$ zeo%kOKyhv|g#b~fu0}Mp&uxtDo;F}qImPlhDaGqBLw|~k@TWDx-aSOjf2|>-LFQc} zFJU(9Kf!04hy+N{5`}O$4C#W{zQD2HUC0DY1f=0~7u2p_P$!4MeA3dQ@bSN0>dnjB zI5-gSGFO*p?FMK}5Gh>fWo##TL?RDDO4MUj#uj4y?AOM-itEpA9aSD^HEmV#hdVy& zm9r`ei^Moooc$4>K-x@H?SOAq^9V8ivNVdvRI|6Rrsz&l4q1hXv*C=A2yY1$Yf-4e z+@I8V5tA;nqUU=*zl})cVSqLL;lingpP#IGovcBI%NASzkK#Lpjs;GRd)vF@Pm$Og!o6pR%su8J!;-1k2W~^~MJ60%y;bt!k`#fBk;4ZK&4r z0O9`YUFpyQXjuGHg-u@PN5F`b_Vzlqce=KTwOcy4+Dbkp+Ln^JUT2PxCp-zhQ7v^y zVMo#ag^htfNGsreRI?={wj)ntVpx!mToj1&d8fB24IM6d#HFHE|z7Xpt8CO&H z#;xJ?pFlHW%W-1l32Uf}hz#xO%r>87_j`kSz8H{;tIOu|)Rm9>b;iU+?h5AL3Wu#c z)L1O{*bz_B7k2Qteto!oX+N^&*yFIR$VOGs^w(K61(&jhlM&|G|%pb7-?CjcZ-NFi> zAP>#9dwPd4=@prvFc2K+wwbcisVUwmSJj@OecaSaXO-hxoxWdV^Mkl7-;wZ*Jt$=x zcafx;%ma=idHzKh9c)ot?F|%t-oda9OB41VoPt7Lp^$r$w$o9(k|%JFZTX+Q)pB<- z!>_ZdTF^pXl5vofn<#NYK6L&&bTh53)|MA?tj*$L6An&0g{%)(6{!!1vMenDpF2{M z#yr9#6rla!Pc(%Q;O0&iIx6WuzfmWiuc_2L#1ke#w|bsdh5mdu~(V7?@bS~4mS+G z@X`x&dST=G*~`V#z{QjIWTf=nL~UN4dQq5axM6ii)XQNCJD;^AvP~4%@Y`pAMO2=! zcM3h$lPs*bHk+gcUQyFT;+MP5={C_>=@>!%zwAzH1}8S)nsRcOLF6AL*lnCY4t*y< zk#R6F+YgUoc6P2Ttr-81ttP$nW3+=16Kq6@VMJo2l!Sn-$bgIU3RcMaQ?SfRVm=Hh z!`}ew@1;hB!;&V#YlyTEEXZ>lb%c!L`{bm8@eJ03_0UkRyvK}5R{`k{9H{c_om9#w zJ-0D1 zv^F3O&)DRrHK?#|Vq;Awy}m*&{;V37$s7;1k8~EXWDG`@Yg@#VX$x%9sG(ty+JOs` zNp(G~J&U`=lHAnSY0D$DyxR8kO5MgX0CawM7$7i_l-nnk(34Kdw-`74bXcUk%niJ~ z?h^E!R@sP+h)@137M{8RMQe%sBb31>-HAL^I}#?k+S=C=KT?T0W0Be}ji;#OqavDt zMUPkv33xF_OJ`eNo|{wjlDS@z!?l!pVs?l2=H_yBBpf+TCDp~nIe{=Q>%^k(qJ8}W z_Nk7D{FKt<%}JK$nQ)GzTOGSs@SjdQMV&qT(Qq=@l=3HV8_aEOQ_})$UM*W#Td#2{ z5}}wf^fOSesqas6y@c&|wF0{mT3H8z`$xvd#hZJpZP$Cfl5hxEI>~cT3L3OH>wJ>_-N6fn@-842Yc!0B43xd5LAvL|`yRX)Y1%5SY-yhupJ$GcSyW-w|% zc~qX!lMa2$R}NDbC{9+^BghRlHv={arZk%&Q?vTteQ7Qwz<3!;>agr|7TRjRjK6$! z-k-?H%cP6JDR=&O@IHzaBAg1I$l6-R=*e}`e?n7*V>Gh=X$H9q}XQ4=vq4ur;MwUN>}W< zD$I;dh3%$jPH~Qxv$Hdh#+aGmtCJiecF_0ypn~u?j5gr3el|&RKi_{cFJ=y;dLi1H zw9E8yu^*`pesu+&o}^X9Rd?i>o%i}bze+JO=Kr~(uV}7)>S|#m(*#q_d-f8bLLf)B zJTxIAYX^L;=BAIKZ~1^~6D|MyYbX3)dOdcq2Hjt~F zVEQ`en=u;EjfTuyJOBA*{V!#?l-(tEJAHk9C1+Mnfw1%lP1RQ=_@2i4s(rj+SBc6+YOvm0s0lJf{?m-li<>$9PJpwqdu_9 z+V&QxR-<6XLdH=M&!fO%?yUW3j9_FY;_G;pu@WzI*hc6~Y-#qYKP?p%ZjJGYFU@U` z+`N)#((|gE?NWJZ8u{5gsO?gYjT;^Tzxux|A_E;eN6rgV)0Jgv&)xY{N);1jrLMyV za+SIOKlz5fHoB6x?q^tAS}rZF(otQzwo$jRFLM_aj#?5tJ3Bl5)|CUNiKhk4JAvZa zXVLkR7Dpp`Y^Do99mff0Mt2u^q4-xx`3Qc;7+dzif%)Bwj^LfKxlLn-=Z~u3|7!fu zyGzib98_ENy`e$f^OUv3@P6Zdo$p6Obym~!GBD+dk`kHO)LmZKH`WYqd*@3oaoQSh zAPS(+dk^E?=wqvRuS8Gx#^2r$n+}^GgZbas$HfL&*016(uu)z>7>dauc*vK~!QjXP zmyE2ej+PeTox`Pct1^xyVhO#C^0UH8Nq4~`JH6xepOO+=8*<-WA7Ax1?titKPWhTN z1tWNIak0p1^sok#IM;z4Hd^)V8f;@`Z1#p>Q4z1dpWyp%8HAfD$Q}HC7Aq9p5#_cSA-PD z$QLkU89Rs{qxQWVrg{VYxiou7IOa+48zTopgB7`Wa{2~TjJt^o3XAnZH(O+87#c63 zjMLG?um8T;PXG!2IKRk|^q8NeS%{A`^j)TmO!rlArgDh7)K^P9{yM6dzA)+ZD#N06jaQx$W(D zhKu-U(FiFCM~EC121yA0gol9;TLS7@K_3{xHaFdM*Dw@AlkJ@xP(uoo`^$NSj;2DP zeX7p&Oen(nd2rf-kB~Pta3L?hQd~Tr6C%&b_UiY55j2dtH_g0-?`5*1m1AKnZ@Ls$ z%KTB$G8xfXV@k}e?;v1LmuQ+SSb*~|MGhzfOW?Sz^Je41X|%h&Wdpdh6FAG*Fe zEXu8WTWM)&l~N>R5R`5NMHD1uhM{2)DGBLDIutMn1xW>j8DapX8$>!3=}zhH`1TBX zj_>=uKmM2tJYF82XYCbxt$W?~e(%Y?p{_WgWMR@?s~Ib>v|S<`t3q zA3Eo;bKvOW`mlA%D;2jt@sTvZ4lAlnDa)+UF&#xOV-+gD!E`PbxcMau=h-Mg}yu4PmmEzpY$xyRd=;BFl zkMecC#zjx;=#UG;4fWr9|F7fVzCl4V4)#PQ_!)r+c-BjBZvUMd{`Xvj!SgZLLDKPG zprOH$$IRIJ{kVFGvYzPFREanej)YKgL=g~+6|{5 zC#n$mbv|TZU_qobHy2cI&?GW^X11fXgD*9?G3Nu4K@OS5E__qX`Abom#8(s4mH&WY z91f)dz9;O)+sdMPMC2SSm&^rvS>03+CM~Z8-BGYoTmgPx@LPXSh$K)zk>1pt$wc0H32i+^o2iHN z^dKRP5z4I8Zj<3nVPSjY-#NLum3Z-%Z#(#qT}`m7Gb_xJ4Xrer;lUm8Vh`^ zP`FWxMY#d#-dkgJ^70?X>e$&$Ko{vk8&JF9{cCJ!{L6>q{U&+Ar?YF!r<-<@=VXpH zzw?!^$^iz2O>w-k+n3d%40yQ3LTMwB?*XUzz);nWM-^dj$4@abGP0uspOY2;OSHw@ zXewFQO)Rh}H-HtUm76084kj*zD+LSl%2FURYiJS9?SCMxf1aMG-V&;oT!15S`a!9V zFeM~@Nvl^{xH*ws2N}aBgjScxo9-8>eMdrkgHukk^U|i$i&sc){2|7q(=2$rLr5KvuRFeV3! zbmTJWK!-3HR2I7p&p_?o)WjaceKqRY?=8enzKJ8Rj7RZwRoQExY-nPo6M^xME^q$> z%mF%}i^F9ujo@7^JeBj5y2^?6QyQ-S70Q`tbP4sNr)TS3hi5wZ{yC2EA{_sCH~o5e zLoT-dZID6h7{G3p&My^l&d#PFJM1u39XKYw@P1!EpvI2V}R}N0j@rJN9m^)$O@bEoLQ+)161n#?l_V9b}#AEVgFnxzQjE=Y7 z1%wb6FXUwM z*)p9K;9LdtsWHL$_1i{n@6#Z5911laU&IwGwKVn}%K~*j7SHez58xS`lUeaFCqa(& z^xFo1ztmLP7loyE#zv>N5PSRAES$8zZh+Wf1%*;~7IieOZS8cRf9!V7*IU}#|2J?% zD^+3w+;J-%wk%IF=Bve=ShK4#+;hsRW8fDsr^Ffa-2U`AfTQp9$F~FZ#CQ#027PbP}5_M0CHT zIqaQ|Rmr>)0$n@}9qpkYZWF6~9_b}ylJPqwbNrJe^KtP36_|t?B@P;V`#UBW5fTHR z>uY$fWH%|9_$nr*{rK2Bx0mEN{WfH-Iv7b|`*Uha!}#*MO~T`^ zFGQpMgHOUf>z+$}zpt;LT8v#THd*_csdIAkmg*>VLF;0tMNbB zSusP4WM0N#usn(Hkmb!@h6YMgdNs@!UxvtWwt6J9!a4^H4>x2^HmY`?SGgXpHEL7Q zqP*9h8y zdyxI!dfkaU;^Jwz6?9GU2X77=YxICt$Ui@p(tnMEk1vszAt^{(P0gsyp@uT}w(^%# z@IWb!VGNKZ3vok{OK$zB@h1c=q~zh(;(iN($RI0vOtw+WU3x9xZ1fa2h?`6tCAE43x9dB?1Flh*T?j#{Er^SlB_$q#o4Z8-w402uU?d@%B zY@R+npQ+5?v988I{ual|IwAB78A;@e`sQX54+5|20uYuSQel;BQu})DqQiQRK$nVl zN5NH|Cp#1~l0OA2>{I%Cr9Peqfg0;&H^4|{rhm+S_%L;p#Q!p^V`R==e^$)e9=Kup z4Kz-81n3nNh#5 z^%WX_`pSfogEv_|o8vx2#MHT3>rC-U^eHg8(98e4m;Z%Z zOD5=(xLF$3^Yk`dq8*$lm;3=5?#u}=E{gc49%hEX_bN9$3VVPMl2I-70Ca(vx-bkE z|0f244X7{fi}iu=>qZIu@}2&bW!ft;k$)O5Aat7)E_y2t_bGfhe?(TON0 zj6!k@-@h^uQ!Znd5`dIWJ`O&>;nYI`(Zo3}we8_wO*{TLrhLX)-HCcVfVxZh6J5Sv zrPlp(@y^#O6wK9&TBM}rUk!e{av7G}SwA?H!@D)D+cc5Qw@|Nm#Yca+E5jIv6Wo` zF!Oj@q*r#=a%%Tp~(FPA`Ig(4JPwc;9-8WHk ze{i4C;m-8P!bcAoau9@T((~Xi>n#(QE3?K}cTHQ%B}eDxt|-w`g=zgMVgPK;uQ%kP z5&5euzl%mI^>f^?Mlx;JTI~HS59=kw2Aw_SRE4YpgFx8)G=~EXAwV#My%1smHC1;@ ze;P_$I77U?v+uD_fRIl`nwyh=^3NFvpV-LA!F^Rw1~q}P7UnH2Q8Gi^0zfT-<#EdO z_3MsXYRLSACw44TaNh`I^fq^~=I_sdnKl!KFY3 z-+TbE*Al^J+5-K2ke*}Wi2N3uIACmSm0j#YxfZ*XF2|d{T4j8ymZ}cdTUj2NeJjAl zoznHSX#Q49{MN-d?c;Zm`AHK%{GW7C41Lyvme6xyBzu7tpC_QNKZ9)5?@rfOq&WL& znGHZs)>T3&Arn5!vG<=v=bOA)_!&!t?yjI*T)m#7OHK`DhIHgN=|38LdNRmmt;ELr z_Dl5QI?h%ty^TG9JKUs%^zAb`)z8{ETKAbrjP-g7nrt$6DNj`ziYWuT`38*XD&AY-d#kb2)GH*$xcf% z1vDcg4WcLe_MceD%H5qV5Z<1i-t(*+t9*1(L~iE`2kZyF7h7ef>E}-tt_x-HE(tD6 z9w${j(R4h%N#Wsb+dC`wAAXI6b#j+_PD-M26z=f-0198s6YN}e(uwdrT$EZ-%Y`V7 z@lUs|!eX88iE`5}PLQ8td_3QH_#!jxPc!+o0V-%kVuqQk5ep{$$bY~DvgTUcsIpkqGu85<#%?#C z*ZbW6=C%pSew$Ja=plf>JQfJXTMWfUX8^>S7=6Xyp5M_>F!|*o!0Wfi^eTe55Zq^O z!Z0^da8rF41oz$FpRs%S#AMQuh5~Z65Hq!qtDlcnZT?5ijq|(JNHMCfKmLNELkH2a z>9xwFmvLB#I^mKJ!iO{Mf4PZuQfyaOiAip*=SJ|njE`1P^vcPTZU65)x05@MFo8`F zfq(r$J+!Wl&ELQ7=*Zv6X{)D4s~M6N_Pk|%{W*VuJw6&y-#oj)B<@JG@IwOt$K$aT zR2unjp+htU{<=V}?e)M_;T2 z(3Whs^FwT2iK*oooVIa64w69NAGt9yzzqyWz!ho~K)YSG=6fg+C{`1X1JdoLg@ufm zSnB+T5z9-rP+u!518IR%*=@&_7N(9IsH7|h791UweUB=!nqTBc4=i_Guv1KcU$~1u zv;0bUMIC4%@~6;JAyLDx7o4@@mD`TEPg1JcY#*`x0{ zAJ_=mO+EveHF`kH`IH$Hsl*6VHUN7NiCrCvi)-5Typ^XCFrl#c^^Ou2KkUBsJ-_ta zXQO*wH!D(}YGgC9!TS40MKZSA3Htg{-37C$cJgUPRqrWd`hfTecg>M6(i6wBUj3bL zdNxlL7IrL&kBiCq83)?o%UkDE89cxG!QCp9%9u5656qNwT{q9Pd~J%rcBQ<$ya02c zgg}uOQyqeTOo_wVi^!nxpu3E?MN*EuH8}2dY|f&Cs;;;hoyrbcK@X=8@Cu2OZ7xRStk!@3Hd zh8=2+hnOb&pH?-o<+BQEt?wIrn%KwM~HT6UKvBQ9HPrlz7wd(Vbk9g`F zFmbMG3Bb760GRUtgU;*MR99iUCAVCe2vxy=ItI+F>!F>rEG!@vr`BxA@pS>#27ixC z%SadE4O=NdmkUJh8y!1WcYBayQIBmbFca%0yNf`ae{_iVr*(^ z*SmKNk?$K6&#Zak#ReiE#|C5kA;Vun07(a^vmQQtSW!{2G9{SQmU|Ckw7phBf*&2- z!)_LAU$hs-{MJ_D=HTw(gDw9zfiks!S<3k2d#wAPwFqo+uBRLlEG_NPWt8!qH%-5_ z{C#v8ha}D6Qzn`b?6h{z?LuxQw8|6GS1UDmFfhm&QIUS!)@C%BTOp*W=C~A-TbCYv zsZw~LtI0X}v=m7OL)b9_LP78jORz_ueqCFM66t!{Ep_F9bw2y@kE%IX=f(HK0s2wCem5wQP+8C>28oUmLO0NTm+~wH zF#yD1yQi(mw>V*aX?(oAK_a;2*013BqiHPISvqb4H-vL$c9QGyRlyVw2})$7eOBeu zIi9{MM)8B}I)EDD{m%5%#%L^Y@)w3hrAqDK>+_`d_aOFHKyGrhyEE^XFH;}Z{^2Vo zO$jLg1zBAW<2W;aAZ_grI;)q(CkjML^?Kc;ou|@EPdgtR-88(C&raa z8a`lM`R^e`U6F5I-^G7t6zfO9#|ACPt5Q+S(M}NxyK!p`3u@j$w(%N zyH-sIO(Rp&G%57@NqUK?1CF+Fm1%zb0XvP(Y4?|%?sqP|%uYmt>G@%m0I*(NO^O*2 zxT+c52ez(>3xVFfOF#u-6b9UAe%IUDzIQGV_y;m<9o6wTt_&S4x0D_%o7!D_!^ZpK z#eV*Yd-7~q}j87Y8%ZNjVhg(Za>8jH(W7E^_F1Gw&V#Ll0K=TUID8HdoZ+VT6 z!p3l>!>5vYC!#P%UpEsOA7I7_hK-Vv9U5$Wu1fTD?b7?7k~N=NM@k9DCCLcLP>`8i zvD0GGm;Ye_tOvw|{#z*!)gdPS-%5dpt4aORu!)0nRx>=Ib*7AXLPi`gJpm@X>rQ%; z@r(JF26xUGoRdVa1C&-H1VHX+|Al9rT9DNB)4JkqYcHjTRtg?wE~>VM>QC>thW3c7 z{Cd-w_1sgeQDC~ja24lhsitObtBCEo<4P(aODnLMc5xZjeQH3f2itxI z^P9iB@GoQ#PC`;lHilo@6FiBG%g;k78Ozg537AvC{m;ck@Uc&syVG(tVK3>z=FXR4rNP4ygRQ4%4~vD!=fg@8shCe)AH79`0b@5kMMYSCL9EQa z`SDoI{c?rIi16^p4&Q2%l+YJL#G~rx?e)PWAqrmcFW(#fe@@?ERMi zGK%S_E+N-b<12tu_NuZ*5y;17`dStCWC=aG(0+5~o|-J#+Ys7bbA_8crZ$BR(RrpM zQ)@3fdWub3RZqHUu6)Vxi8R+_1m$|4aMlzDjMcd<4g=2nk0R;gWJ)t6Tu8B?yKg|T zN$sS%8sKB)O-=vy+4X~+cF*J7&h*@aq!#72RS5|xsil^dr2bWzEoJHhslQzHmSm1j zIQ$g@lHSns>B;d?VGPPt2{lLY`Xn~w#0zIUmu_!MOWrP9(;Ral#W2JxQR-ny%PB^fEV8rtcB_vWIDZTB+ z9Iei!zKtTs6)t3k8C_N0g%V=*wcA zi7KmKS!GS+0C}Ph(HNUHRRayJqrr88N zwU!z#(20MlkCHQQk;+?QR+AZ^docCTNcD9%4du&V-n*KcFOCqIaR5=%-kJCjp|xy} zc2cqH`2&2HCA$U1W2?WNWbo~it)-R4!s7fg)5VK^^y^&F8l#jDFh&29mH>Pqk71JPBl(HME=1@_UT*Dir6Ow z?#lb@Jjj$=pu%?KrdVM?Si|;igmR(zH-B3YYw5QVGR{&gH*RB0{lUe15o2nK(T#CT zUQPx`+ZzFqnOocW{Z)4{ND^#76P7KXTBv$ z=6`G|5r8#k?IdESTUds-p#j41#Azy|clMIMFmL(0n&kjAmQ*r|-QuPbT7krcUiP($M=W4#c}PbyoF}EE%3E>Mf<7F^-|dzCkhL17cnU z_|%ikK5@=O_-BsXv4MV#nH2#n`_@k6ix;;X%oQK%dNcyy*J`7a zn(OZ{oOaL`UwJZHwRc<~xPjzEE1D(A@E@#Ob*eG`f3V9_!*RoZ)hotKD=@nS{CwT; zsTy!SgQB9_JMvqk?idC2@h<)`9&xtrsb|K)`?ownIrRT|qJBr?(B94eVMYPSGjFMp z8lm%$Ii=oSRoNIz_=|@{@GTUGyq6_;|38}s`dHA@W*dF%2_(U-tE=lI4~pRx%_GmQU9JXq zO~L<7RA04jaj(9aDqT%ua+FGbhDr?BQHuLz?9XRKC_xN}5r0FMjZaArU-3M`^sg1Z zWP)UFzw;9*FBZ#3D!mVd-T`J*nq67ns$nT9u&PkQ(knZTh0Ow-9`4CxP|8W3+Z@I5 zla%5oGbOQvCv60InRSin92|EmcQ#Udsxnt*Yzt6v|Ju(IfFVaKy?)2lNbzWbk7Kc+ zX}1O(^8BhEUKID}nUdf9)*)Bj;b25}hzOjFJkA%uY;36o3B zmrEw~mOTq?;w+z#6I4T@k!By$(x@5x7Pe9;Bm!!36o4VQ(Bd&Ynj$$r zHIVUYe6cWnUG-QTYfCr4_l4?F)Iq2FsL9{0lco1L#-saj>xMw?n}q<)BYWfi(TI#p z_pr(*DmQ;5Rth;!ghG}60(gO2&8IM<09UTuWTJk`@^0g3>-YpF1_n4aGp4OTymC#9 zM73|-%|f-T@5EMQ$7e=$-)FYeyO-q^{*7KArv1^P&9dX-m7S`-IK%pU%kJkBQfib) zqcm#d?O^hw3Hqw!&yUWV;D0W$d=ae{bvjrCyQeAtrar8x&Bw(6qPcTd;waDSu^9tJ{xWKi}lK4zZ_%yabd+8&AEV*NyBw88enstZoVpMQ&M6~^4Rmh+E0=jGRp+y ze?b9T-@KfbE%R>x9h>q(GaIx93O@=pLlCWqcEw*%=mdu9K*227dA(7OE z@vbVcA4VqUFELBaTf(9qBOJXnCeg3`F6Lc@J&<%F&YdcLOMizGV3-U712zBJ4LQAh zP*R9NIO?0VjX7a6v-sZdloZ$btzOk(N3EYMo|O|WCsqwUyHB=fD|g3>d^~3+BaGi+-bX|>G%f^haz?9tFw7!2Q9Py;i&>>{&g3@RiKcgi-$MZ)@BEb82b5n2# zR$NjT&k_mnFq}O<1j9S@ZEYD6B@XW-_m(j-Axw9=fBsu_f(j*28Zr(bJlMj;RgIrN zW_xzPkn;O40&gJ)sI@UBM^+d$^#%aky?e*Dw+HT= z&lk5dp5+}79^Lwcx2t_|Yj0j9slC6F>0L;k=;v%*zKV5XasoVGEi<5eGd)d9Pp<{w zE+QiSwvplCHDqKSoHBCr^QCdo@`y{ix@K^Vdwwm4J-Oem!ZtRh!ftbbw`_e>VGS%W zI!MR2^kzuy0~pjB2>Imt9CpuC5WAVox(mOd4LxMHXOYTU0^trxpaU?dh-4zz53eD~mEPfhGLZ6e zM_69#(u$TKV!EU=ojE;uKIZmnw{sYpzZ~2PL83d&c@jV#t=!)kkM8iovOWCp0u~@Y zPWAVkB8Tg5!iT@jbBZ88wZIi_^AR4NtTzJlou38-K01?8F0iOqOwPBWN(&$G6Um?2 z2V`Q|__v29Hg7{fxlq82VgF+I2gl!fI-tcA5_5;;Yc+>5E1O40szFPO3lg53qwy3$ zfDI(P_h*D&sh}Mo{kdKDqnqj%33@PzKea-J?lFGVgVl}ifCaw08waXsU|;};6X`dC zNgq(fdhvqvQ?-)jy$2K8X6<@?=*c=J3F#5^Wa{Ht*6GX)X=5=enA(XQ+nDpS>LhS@ zZ?dv?fB1AI8;zDfoRfw2;Jp`B(tkdtxMmy-A#m{iT`VvFs~NJ{%~^3Ctt?STX$-sh zRiYywraA_r8sKjB1U-wxc=xPFvf-3S<-`Z!aKQVpT0h@S?lVO9JsT*u76B7G|Le8n z66mm6F0Fs@JBq8nSF`?wevzm$B+;AG zMNiD~wjvHnwb)@yOv7$&!}Pq4W{*g>Hy=zcsYF$JF$B_AmY0+2*8p{zT?^lKW6;Hy zLxzQg0qNm6@tK(!xgOt_2A8YD%o3zLEN0ukOSpLFJD3GM{`GRbRltHr0YPlZY;%@q z8GneTSjf(QOt_{EBjIx?qeGKSL{J?E<=EEBLrWI{NFbFIzs}73+)h*3eB{~Zq(^It z%`&0U$K4TJXv9s}zS?pfk`nUgRD(k+DI*5xB}5cBhZ^5614larpn| znaEczCVk1=Q9xCYm$DYnl;+K=5ny1*0P$0kB!3p&Lq*$;@)y?1pM+5bR zFE(~1HZkG#^KNs}_jkMyieb1 zdXl`eKBj#(d%W#lp;foLvK`7*Y)QF^`&1a>dUd=vEh8i2^Jl1f6u{>I0NR{59Ua{a zpsSpomQ|(t$cKW8b#>?Zc-ibM7nHde4phXG+6`62qY}lP-dRT^z~xcuYUA+PReGB{ zV29=3SMEoEHzaD#65w^HDJUf4la1v$)K$A7F7!Fb(6m_N$cc#*6YJwg2md6O?W&^B z>b`qZ;mQj5iOJVuKH&JX!7fN#4~Yhv=?A9AX-x76)4lGQPz*380&dzlp0nDX2?p*# z>($k_`owttH3YlsDM8hL!K4JFq{2s&bt980xrk3|Bq9srSENS^7u6*8xzMSTXV0N8 zUb(Dw4@KaGXQ8amo~@e_a@v8d1sedE;Q#mSV{s4VkLlV4fsZQ>Q=hEsEox$@;U`Ed z!JJxe`T+_s>0HctZOft0mKFb~d&6yHX<$sO;4UI?nE6-4P-W%Qk@WPLy`Gwy``=7B zU^}BHHSGy3Co>|mkHikLi`D^urZ8Ww$CD?-gsS5eD#goQ#Q`JaThBU)y-msuYz++! z6%+^$6?=MmYF6kRnIPQU&4SUE+@V?tGKRZj{;}Ij8-pZ#kI>fV*kN6o@I6sufgzee z?=S`%>LH#NIdXb|ya~z2<$$D6Us-zSUHrnv9vyt%QjX zoH~(d@5Z@*cG-Zi&x7Ce?|YA93x&d+Jq+wd4{^3VqYi{$&SmYp!|jGQ-^>5b^TH#J zIRy@%3LGf2GrdSP#&Q$8M_2r5Ac(E5_Jj<;2u)kkgS~@T|sH>whMo&`3jFZ)SheIbsie+XCR{{9- zoo;{!8v5P+!QM7O1yEsWggPD(23Z*sJ8&_smYW3EFLgXeNDvbfQ_FTng?z~<0X|;w zSVUU$FR0qzs~XS#hO2>%yB{QA0xgs9eSvb39ITN3P89e2rE#)w>}Z)N?%|;Bg35pm z7gyoyw*Z*2hF$&rwR>0TYhb)Z5_a@ot~dR`wn2SP)Sy64#MJZ|RFfgQe8}9?^ejxr zvhelmGgj72fTj*VaUs`b)J^Ju)1IG2G0|+}afbnab-kC~$*lX89rd}rM|H5BB^|20 zS@*faNAo8a`IcJM%J@8lt=AW?p6IQ3mnik8SPyPiTxT%pNnAD_RdBgK$oDd_V{d7o zs@rz+{bXp*^Q&RpZM(Kx>&m=48-=qDkDmi%)8?I@4^YxOJ&`)P9&zzy)faW{8qt!) zTm~{c9*-W;z_P&tczZ&u%aY<~naELR3c_`;_BoUiqF-8f+xk|K9atgZCiH&t_3PJi zgB37B@bQt+yht>lIy^kA?>qkjTwy;~oOnZZbgt{Fa-o}h@OsGo_JhmL6ye$s4_z#l zwTmBnJkBH2G}X$)q*ajrjS$b+m%X1PhdzMfz#ZIAWds0>_yyk*9z*j|BXmGXLb0BB zv`_qx#4x~5-@kv33Ua6iQ1HOz{_o9_Ge6FFaNgBIi`#tbR^4slOaf@}O$mt*A89&iswZwjI5d*W1(I6Di(NHF!xzvSvv{nsg`t zRrJzHhF^OxyVza61;9%_4r>=~Y;ZT7SSEQ&94U(b+I~a{bK2uS3oojub(eO30VM>x z%cJx5EMF5l@L$`XJuV#zHlLjxyMA^f<)d}9@DpfpJH@#yKmRK^SEQ0=hjQW{0a}mV zjgsXcinzD|nzZ{C%mLFUN8ti_q&~(MH1-b&Qx?cufChm{qr4(2e|uTkXX+J>>)^cM z19~vEJCNKwmtsa#b$=CtzjdxkWc9tAlkeD0Q$|xuD0@SaxJ0n@Q6W17>7UIR1;qW( zSCc4g&W?~2U$e6sBdD?Vi;sR(Oe}QcMr?v|GW2#`)P_*X&P1KfKQhpNlq7e*l4DkG zfo3d%&)Nf1$Aa8%IDlQCYoJE9#5P4aG&D2-h+9yv(9pt`hv=WA_aa<*`}eAhVfJ6E zGH1M4_c5?+Sb)mgom&_N@oj_WXCQU1)hX&ut}iGdD-44WwJ7$ctCV4zC8jSzuXLZI zo-#tb2(43fw_)?)zoY&x1j>I`8w=xB6h)Z++h#j$e=~~91i-u8JS~77zq2oSy{U&! z8_#ma^2{1_1m|(@s8g9tHz5*O_MBr27amnRa*ieCb}}Ga($#%m0NIup>S^gAx>Ya7 z%2|^uu)d8~wy6#|ZxTB$bP$L~M}rk5%r&@!-bUtoU!UH?hX-5MzYLFUF7iCnFVWa> z8I?Inkm~P4`bvuD@SjJv0(N8yMlPEAW{tV+RhM_(Qg&dQVBRc(c;1qO5pm+kaGi`i zB1vX6I+kAH)tTmCj$Q+8;MD=w?xL0N1ke(qdrU(#aTQqHVuMVsk_hOoN z64+2n{yUWb9l7jac+JAa>#J$jvM5qM>kZ5WmOLD8`sfiWIx9O{nHA8XV!on05i(9d zV&LAAcuqdZU?9+qJVn4#C5gw+7u|~`qg2dLnZeKhbUnq0x(ev+G`+lh;WSRlV(UQA zs2SZrUxt4!W)V@TTO_USIub zpQppnds4&7%8Irp8uB?jTzPo-ktMF{^;A$svqsXQ#j705_H*boT_i4dJ>wi+Kp;B@2i*RLWiEOCdZS(h6fBevptuY7h z3iZU(dt{&Fn(N+f9svAc<`0;%*_jA)^};e&|Dlk@Z16(e-NnDcX+awN`4j9%-jZ;= zd#PU8Ky^(K)u*^i6UtAmq=uWIFBL1WA-y~IB|(I&dO*9!dBxph7ouU z5O|N-azp+IK+Zf5TDe~lG>*Cu_|*yW7v5NcrOb6%BBNpv;X-TIKKh&@U?Of?rUe(5T_YsE{=Ri#BNO--{SNEkyZ&t)FnH9v}qs#PvZQAj5;dUsKZp=0HRC0~{ zG%k~CU+$}4l~<=>H=w%K^*U%G{Q1@8_%mlI$DS3R#i{qg#l>MILU^lg0i@Hzo*sr) z`PK(s25IW5EJ6>5J2sMKY@K)By56B#Tunp0dX>2`>!GMfFn1tIGu-8y8hjN=ym#~ywK<>bI2)e za&(rt6@oTmqMc7(FOP830rjQ)801n3P$`)YtCH8a8vnTATtVo_RIKm8y1XrUP&-a- zK=45J(7Usqn|q#x0~J=qqUJg#URG`C+PZ-DE`@ZwMx~#Nk=w3kcvF6l4rQW>3ZSKu zrSxD3D5r{spKDhTmhszo)fU;jr-qOhs%ACdaU`3*Yw^jU;0YjG$Vx2K2kS zzL!|yc@jaxVKmBvI;tu zeHZ*cOyMBA&aG^c4s)wXD&@yL-XKbBxLcGCHK>S9Uq)Q}T>-&F8+R9(nj!lR?XAF}ix616LsdTS(s3cSp9c2Y!Uu!$DF zejD{@gH0ld^ULN>L2XWM;+UMlAqX|ZGm1}#RT|~qV=gv z1ZXPWyzY8S^)QJlnx{3)o%NP=L1ad(uMk(GjsElCd5?!sf`g}B#HEZ%P3@1LGN@WU zyD`t*gWIc6Q8ziV@O?yVVb)}or^!CHdT$`dO?yp(dd6MkkD^x;F zygfaE+_TjBh_u}z_s8Cc?VJFP*c{hCq{=Hmse}JEX(?bLN5W#<6YK;ke7BV>a@bU; zx(NGhHp+M;xD~_!J0EL7HL1$n(YCngo1bsIFYSAW;=y`mnkheL@ZmEfkFt|e^S;X{ zy~by!$AnH^;wAX*%L8yOK+a0z#>8T17dCNqgS_~3Huef{A8s7nx-akS6_!QA{Bw8P zLZGi#&R(Q!%KCFAg5HJ_jmsM~m&#ipXs-{yW$(-~dpXA4bM)-Z!>NzLKk2HMxw*?s z*B~KMq;dvv@N=iOKmT4;t)6>zbl!A>P4v{_!ZO^mb*;bwdBRTrmfv1iJ+~jfGUCv} zUV|Em_v5qG_mUDkot|C~JJo7(<7x3nnBN*@gZ$^z|NildUQu8w6FIP}HLU3Rb75v# zWz^f>(L@m4U4HjJpCLlC(r6cLqaFvBSF%GmOs2cE-ll}Es8t_5yKNUEW^AA@q@Wdn z{x%JT(QA}Ujb460B)e!#xgKVPNV5kA`TYCHC*F9o)Rz$Ke?1GX@(u7rZ1F7r9C+hm zMQY^L-mL{dOYLuJ%C=bnOEtG!z3Y8U$p}E*Yb9cSH{d07A<=4u)1}#`Pv)!{3Jzug z=0|mQSXOG={1n(q1{MPiWK298x9~X~?jFpZQR4{5j&&-!uU^qkdYlpxjfsF`p1{vA&++NK zD)^sce*9oB9S66_{1^(wA!$z!3kw?Gg=xNMZ*5h6FjW7jsJuE1^P-{h5?Ox&`_do-HXi1*dR$!Hpsegi8-C*s`06jEZ8eqw{H-+HL#(n-m8k%D^7cY z#q8hF`_AuR&OQ4fG#a)W7YESFg__jJdKyMU(PF!hNEViXO0P3-q_+RZuZl@YOLmw@ zz)6K3h=(~@bb1ul9C)kLrSR;$D!&UoaPpq)Iet3ymv^L`2zBEN{OyB|G z8vCx!wR18e@P)|Y$1r|SzR^{$2oY3PwL(Pix^J|}zJYhyTmSohut6T{m>(3uOT!)# zy7}z^PKkCp6>_q+%!@o;tDzC~Y_L^!LPA2BW-G~ut~Wo2e3`VkARit5nf900-a@dV z!F!8-8EBPP{(i<}!)W7Q?}T>0BV~$u^4}LjNx>xJ05j**=txU!lTLb5#U)AeN{XdF z_YvP=t$pPSgRHIN9K6mP4zw10tm+>P6t(@SqJ;+)&tdG<-Y)qpnYW@p24UN zxCnPA?L~_&SM};X%kGHR(dcpcJ}9tM0V_0}s#R@l7IEi2u`FK}Os_aR6%GP3GjYuTaF^6gMO_)%6<((pt!z$ga0# zFYN_36upAWPeJ-gs`B3Ry%`)$JZK(vIN7q5Tv=QIU(3HPc(|O*RqjYAD<6KBsk;KN z*%;?^#C^!%jna*}g&K<-j;%VJo=%5l>umMpdT(JS)RW;NMwJ|}=bho&_6wsu%(l9* z_ff3}p!+?~7OQs|JnC6x+Ev3{{TjJx|eA41|)jJgg~+c3{I*{JSbKZ$?f!d;Te?{D@$ z2bqwUhDTqSh@H^MBYcFAua067D$xLOw$>1 zyF8u|+{x$Om0}%BX^w+@c2Xv5v(8>(O6V_=e-@X(*uH1|*c08Qbog+xUaR@KsYOz8wk~fYX4UZmeX2?j{PZ1q1 z1&Qrn1ZThX_L!zFJv+=>Z@R2H%dB%yVs6Rbp;>+92kEdP&7Re_6E>B%6DR2dp@;W5 ze4aL)sPFEG^ex3Eo+eN}kU&A{0vrP*?%yRjNJWdD$?3+rJQ!1AR>Hyc;RSRJlZK{# zw7%egh4OtH^26!4WV>mS-$D;Zr-JVPe^k(IA<>$k0K4A16hzQ#$u`}jS%6rvLyh># z4<6O?+dDz^nI16g*c5OLV1o7by2tkc%w~%RTBtChn$gwNp|mX62xBULH?lO`5q`rK zuI!8RPLWYnLpoa)X1`{R-&ZYs>+ck>bF$T;9sa6#eog7=^*&rG^y_xNYcYO%u5qnX zGCnHWMdUx9zW(kOOwuc2*d}(zQR#&4yyTKrn73N;9RI^mWz;RlR_U%1>6->0cFvH6 zJc6!zJR1kb+gDn?>*D25a6hQj!+n2$pBDxjxeJ{7PsEbhf74V}>DT5ko765OSj+M- zvQa=&O|)cDwLcY2|Nf6x9Wp`!V3$N}4mZE60=1uzV&dbsmWaAMry0Pml2RHFk~vR6l>q;P&IE4gIg5_o;j|)6|5P4%0Vt^T}CRaryPeEZZof z7V$m6ZZcg)brkf~_eYdI&FwH@^(Yplng`zGQa5*!%o-Q(3?6><#+VrauGl?s$*F82 zr2C2gQQ811#SDS``HMPnIs>`RdLUq4x_I%T@N`a@eOU$|dRlg7W^cX?1)aFog=_J|ZK)MWq7z>Dc-%%yHG@eUYCj&ziIy)8(87OLX$jnH6;XjuWMum#OZ#_U9)l{@dFvzbsHD| zGBkbq$)T-ktzdbrVC%2t<-qTbZ(~Af4Rb(NE5}r6nxQ?%LqW}#ZKrQoo3&)6&sHSr zp0!?#(V~WU)G!0)UiBIYs@fUSFZ@-sDue=r86SAot(CiW-n$}t-WU6!zh~b1%gxWM zsm<&S{oIboSzh+_JzGvh)YCoS{I{vmJDr_XC#tur;`#TQbM4wPmpsy)SpsChmU5lH z?&!t+A$p_0FNFnIXV2rWVtENCBPXxr`YKk#W#r|WwxX;zz2+(i>itL6Ff{wQt3MfM~yE`Cf7Ey976i9VD> zPAeIncBvvyLvbxWj2|sGNNs|UPl${=ZSm~ASH%mV6=wk#P(341&(QH*g~`ip4O*^(vX>O7+CgzbqJRy!(HQy>~p- z|NB2)qC%M&WrYxji0oAgWmY(j&1u-1Y_hV76f#o^;TQ+mo01)}vdP}-aLn)Hc~13u zy+5D#_xt;uKf2ZJrf!|bx~}U!u500=S+dg=A(lrR(5^mq-XkSXMCIC6IAvfM7KD!G z%QFOyXQxfCAGuyTknETN%|4uWQuy91lz=HyN<8gv3>%&ZN`C;lf|PZ((fIPR?KDfE z5$`{7W0d9_Y46pJtN}LcIo-GQF0K48sxFGpX})_EfXHZY{acxU>#I{r)K^vp)LlVA zL13_IUpF$M$9Z~);PsmXS+)1NPz^^! zlW7KIjwI7Nv*$$3)krBqtzqgf?~I-!p=~-7mdue$aSC^RY54KM6$caGVl7Uk0YE0e zj^hF_A+A_8gQ}Xsi%={DC7n+voFdDU_va0&ESh$!!{qZ|K;oWG2!hY$vF;CR*Kus_VT)^s5tZeJH_Dw%nWo!>_v0I z@A34xHdAULKZ~Z1uYW4;wR+Q7F~hGp9KSts#){ezWEDh&9v5S|a$i#;V_Vw8&f6kq z+O5bEVrEv;r}n!Bfd@mFBl%z1&QsS-R@P((Sol9p5Ubaxm0JH3y=cE!6F?G*z7 zT-Z_n0fWmEhGZ`4@6Tly_cnxwzxvGX4kcfsi}|kQ<)`)z*2_l{Li60ufje1>nY@Yw ztjA?%43+*A0CUS~LMJjQ*MDtA+Lvj)BRF~*Ss^6a9+LgN7QtM!+i2j{*UsBe=idtOzwWq%YfR2f&hw4 z8_)sa(+GVDkG05mPKWe(2cf%kjAa@_flI%fKou#^gDEHT&*SMk@ocSzSr7S0?1>Ok zewpOj^59hNVB1`~uzG1*%93HV)FZF>WW-trT4LklWJoun@Br*m2UL-k+8k|>x4f%~ zpZV1b`mF~#Q-LYwA`jnaJp^;#m+HP~od0zpt2vAg+xsyWt$Q0vLWw3i-g%ie?n@n= z7kukK`MPgTO3Ht_duwVZ!x}vr9oyr&D6ZCSPIBjlX$j*?Fk)5se zIXE~`9EOme$K|4rsz5(LEtbQNTccj+?wtd*N%9iR9-oWnm!xf0iFC1E>?PBLb8*TNyA5|Q(yX;EVf_ZrmGF(!_BZ8MPtxK{% zQ|xGTK)h3;hVglT7Wp#(^QN@`g&IjReg}UeVj4(!CIJB2^E~+v&>n5Jmt0{(zrzUb zkfVO*h!=}a{(kxT>%OKBz1?#?TDMyI%1;vdSd3%W6hRG$BY! zr_+09U>7LNF6Z&ovids%s-W^s_jiHmY!;5QocYaH>O6P4JQ@1>5PX;VPsB8Yh9>DK z>MKw3CQ!nHFUQ^%sr?k$H!FSp1e*@cKJ#^{hq>T2TBNZ@a?B`OTU&!x6Or4avJNZk z;?*vFUwjUN=zL}(99DK-cT^qtziy1EHPR~{RQocgGKdC!OLj$2}R>9Fnqw`>}Z%K_3#2tW+ zr-U=?)KIz^hqyXZuiMJ|vX?3<0&%u;TQ}q~_&X5zLaB`l?}pdV>!UQEcmYJ~GGo_= z2;A&AgR&u&*V8K2z=8WaDNDJ7zrlS3gce@;sctowZJsZaD_7g)oZvwX*4yb7iYRLc|Rpg>NRI*t2jDW^!uwFZ4*`0%YT-A#9mu&enfe~=wvL~)}{Lirt9CzRF8(o3Hl-1Q`U026q`1A~cG8^zQbhb-F*tJF$ zpn%rW)GQgcDa%xcO5}4?S-xLxukOSnbj&cErG2>`H5Fw_TQytvR#r>4cMDyXX+Z+# z+YtPeEBRZpkaxyZp{@U4ynm;$Gw9=vaPX||_r_N{56Z;wFwP}th@|6G|FZ<`x7(Vi z1tlLaQ{_fa;Cin*OMKO)+?1)F79KBOlS~f8VTAbXeW36(~47`pRnAloatg>(D#Xh z;J(rQo#m_GYrYH7ns=Ag+LEo^e*Su2t}Nq`dPxXYT;9hnG>!%7p(eIc(e0*B4a=Sd z1?j`@)|cSN(*bVIJ&Xo!cS7_HA33u|x1ew3zzqvu`63M5k0MKptR1rlbF^(^ zGj)9mFmrRw$tp2k)pc8C#t`lg%3ty1yTMxD!Ry}6W$hJ;9*R?Hi?IAmoIStLKfxA?FH&a)rBTGTQBsI_HxUOlCnJx71}h;CQIGd&}E$U)F>-o}C-v+nKhC6Zu zm|f|TBCvXM11Z>2_jZ>TG}t!oWHF??mHIQs(#Um930rS{%jZ#B8Hw0C9EkZdtnhHA z{b3I0WiHP?eZIY3354lzpK;gPNy7)ui4}(dt*m9HS#zE;jw5$W>Axnay@`f+7EZXX z-%G^Q3;;L3g`R_mIud{}9md_j6PrD3hjFaW8hG}hWEU*f!YUXgGL-o2_wLt{9@^9y zsqZSh{|Npe6q~xai%9P3C0fGYU%b7b@x zFoqIvz61d9Vxoj901n7$aP>FU3#B!8PiOzR&c((*{YpqahD7&T!zUf?WQ-7@@?dY@G>{;cYBuLY8Tz=xMWJ=~b1PROA*dEW zg&e2DGdbbJxdNbl8iaNz%lnv`7_ji=MPLJKqY92C_x)fj^9{XdqFu zvT1$gacu8dWs=uOmh?ww@|TS{>S}JbS3aBOaOHq_{`nl!3Qxa;;lYG)E%{K;a;~?J zwf%I)$O!t-yJKwZM?(W&C)Tc^IVkJCA+K(??h4u$P3MoQ(oK0Cw*Or zi+?_t^CSqNK}&;yMV#vuJQc8)R4{k#ccc41dPo0X+vBLS6I`G0%2cmP$y7#oet3HA)JjYZXbF}N)la?{xI2_b>jy9(6$2Br?^}$2E z%A*`G+Yv6VtU9Byiwr%w0IRou8Ozdl^V!<><0IIT-V59eq~gdx7oug3qDCKWV=!>5 znU(yL?iy-vcXa;1g(%HROj)X9Aiasb2aINN(Z1*?^DBs;tp%krGpTD$pXCxOd#)sY zi2Qb9gqzS9_a*qV2YN3XLWgK#*C#(5$A`1=By!8E1=n?z@7mo~MGD^@wd6=ixrHJK zdxC)W9Bgp4S&%AfR#xa1=zK(nFh>H#CG?^3dliLf8@i~jSN&5NK~t}lbQ`tX6b<*P z|MiAYxG>arA_>vd9yFF&(e@y^!zg$E-qN0tq|aFN_&9fhPu6xqGHH8xtH=Iza-Xq- znr(WF7P;iCFr0nFpFGY{WKG?WYx@kX-^PILh;@))Zq7t<#UV2kyqrsu4Q{+GwYiD< zcJQ>^yrX5sF}M$*LkY{1Ce+0i?y5S;9L}H^6q>2iKWz*=;dhHrH#7=HxMdm5eEkH@ z!EJC360*QKu)K%=f`869B`X|(W`_nJ$%Aq?6sUK92QihV+bD3Z^^S5du7hPN$VVgO zw}Z77I$PRz~y5MY%qV(qkPMD9K`O#?N;GTmmf1*~$ zshdzP2N7;2(?Jr+USyLu-b_{Z2yNU<>L@3B5GXV_zfAQQ1UTH!uueBtum}iWP+uLM z5LkZFbOi5cjY!=v;d@Mb+maX%+Yj$P{7Y3sxb73c%>GsR?1dHiL0lPe_&7)b$s1Vw%I@)mG#x!)?F0x~rc*NIcknaBF+Tfm@oz2o=_4GpF2Z$ld7O#yv{dIwK6A6lqxdVRcbcr2#uNz=uIUWIM5P z;sUq&+Yrzp&w(6s(!ojx#3a<0RswTARq(W~0{x6hXAvv(zG~sc1`oc3pGN2N{=^YP zF(Ejv-mCmSZsTo~d(i1mBsE&o2Ty@E<~R#53y7X1d;ap2ZeaQ#VenAVG9IK=zX}DAySSQB^>(fd4%D@bD7+ze16v5EQ97%#3_Q zlscAh5ls?Xu>{FEmpoIH{v4PP&|V+U94LZJ%nT-~4fGk>pjh}VRe&@ZxV`>QVM#_P z^h9q4_h6VL{mh-L@yCEq4IF3PTpbX&hR7kZn1vM($^oFDd3zIW?pSwcrEY_7`ptyA zT1?isfv4soMmk&o3lHeAl6aqg4F1tWAp`Zacj*-;oClvBpKj*@tAOV-aAXwVM?sni z5~sz$1|^n3Lij&N^FL(^pe`qsmt@56G(V#70Xzq=q@3B;goI9v`hZbWX~JkQMFo8F zHSbda-Jm8N;%ql5>ZIbG0;-^w``rSyot#JKc&FSVErdr&9vT$fjGfVoB@mRYZ;a=a zG(qb5-FCTkJZlv;^qAu$M6lq*PLTF2Oq(P~tjQ)9A*(ziy`NL3id-Swr)b@R=?@i48{&jr(I95~2Dt z?dNjG-*tP(v2P_MTbhFphu^D6#D9lf^g$CsX`w*2%TYZRQk{Y`uSC5iq9>fj3vFW_ zad~%=5fE2WHpzQ@Dqm3A zE!x%K7fuz=P25e#keX9M*$qziq8N>jlbI>mA<>oe=*&+!^-tEiqQ@iGstsJmmTjw@ zMYg+s{LnmxrpgNsT_;raVGQ#V6B*8zzu{AO*{%L%nS9>#97^`z zHDyv)domH;?!RR?j=7uz$z*L$T%^hRNYn zyG;419P;p2q$mZhif$qBRqX?sEgvio>I7ewn`zj-R#gb9tNiOq%H$`j0){GfTL{1TD; zI7ltXmdS?N!Q!${A~`u$);?*4frO=T>&)4XM<=R^DhRw2vGc22wrJM4@z)bPJoFi* zOalVO$zZscx%rm@a5dUSE^Y5RlgDanXE2w@%rh}>8zy7gGi(@GgNc*Lr^Y@}PhPi* zSEZnMq^6sr@FoGNY|mNk*YoUnx2DXcsg-MEH{f|h+shapRw$V@{;T}KaXEkWS9q1e zZ>1!@@Zn)a$0u|{hv)+O0z*QngH?SStiyw+*Ev-dOlf;JFXy(9POfU?{(ik68}v=r zf}Z@pQnI};jLyvZRC@uYf#(J4^#zf?XN@&P%fO@f`%46k(Wi>L!@J{a`Qwq1j#y_) zX0Mr`gm7SA{n2AU9EzyTiuhK-=q+{56{Gm+1jyCVyx}mvv z?WHesSz1Mv@_e%>t2g7QkOPM!S<-Q%K?kByi%m`1>3Yt(XVZjAC+`P$?JwlJIu)g{^zTBGX z2~S6AF;$wzh1c^`JVT+E^F(pClOxOcoz^QVm{?Y+eYfN^YjCE^oP!EIk)$5`R{)2TTR^n1HF@D`2)=Sky?^64D_qoe-ajFa zB<}wM&A5wW?QyCxATGPf3cq3WfaY#tHnc?G@f|p~6dMgn>gf4Gh7hes&Z>HfNAoL7 zh8r#`Ltrhcj)oM4L?4@YE1H_VY#^DM5fgkfK2ziLGE3)*uqfT}mjbmT6N~`T?fkt6 zZ>85Jx9-TJ^vRaz`Iax>vcl|C6w!NV5zZx67zPRbuqVh|{flgHF57D{a^tXihCk9v zyZ|s3OL-*^*{;Y5H}e2#NHc^aPeD>K5+gu^Rs3?WR!8L_MQPs7O6_fKDKkv#j+XS4 z9+mC&43y2-{*dZfZDJQr{Q?vUTLP7nBd$9qRuFNHG+r~VEIdy_9D8^gZ%(6v$jU^= z8;-9Sv&L2TdX_(__Lx+1L?-K?Hd^ED*D+7J%HGMMXXul(xnZ0G& zbB!$lu-MnDrBZ`+<}~;WN7oOaik2CWd^=PA?%@`GF!MsZd9LwDE6O|Qs$e6X4959G2SlfG{^|Dm?lJjblu z$NkOPDl5F0xYB*+#C^1~k;+wO2hbzxW5wM?CYjsb36C@)U1p!wBc^zHFKvj@x}UYs9qD0-;_yBMHcW&Ap0jsmvTB&N z4>3Rm>$9jw9B^jzXQ-#qXF=@t*GkFOvdVf$GL-2!OzKz^C%;EHu7k_3UMC^|iDG48 z0VXN?OjbJDGJku!s&e)$IXM~G);2kU0=7}dX0=#$sp9A93-wp8Y*0=gcX~D$gS^aq z@_m5#eq-FH({GS7-|sUXZq0H1lyOVKEGCfc{9Gql>?yvm1iEUgoKd56pVE6_FPeUs zeI1I>JCP7FWv~W7KS-5>V+wIgjpO|B^)C(*p{aNYW(MP~^(&yk8P<2L$g&%})uycR z=IwkFwrKCQZQ7Ej#KDqjJF{I3jS)R#BFW{Sbcrs*Q@lc9A{m2^fa)VL6;Y_1ly#D6;fNWpuA^5wxQ6iA}nyJ%A47@nso2G=>)59ko@8CT-^5L3pTA~&?|HZ*%m zw+z$7W?cSxyn(03^Giz7y#8acC$W8qq_1UO@hZs@@`mcjPO=2>7C!;$!a47mxVZDN z?+R3iD6%e{Q_uH%c|;HV;DxtNeHm8qSvlna|E^rD@)6SZkH*J7gWy?kD`jJ z&U^PbcTSd`zXa&-2RBgxq$u2}1h6TtKqeAPUj41?vr7C8A!q~!)^nooAJC%U zL!i3OOr3SruI*i$r#nUJJtpJiv$ba#CZxWF3E$fG06-rnr%i}_0z3R8QZWD#3Ck`? zu~tU$hCaE7W}}gm131N*l2$hh$vp+cDnBf!6ERaH{i*M})a|*h+b3cJ%qpZPpa-UacooZ;2lBrm0Z3a{EA}Vd z3JnE#vfTg#{1^Ydz=IrC*dLyE&_33mhyL5&leqY z1?}e;2Gh>j+}bv({K;Av9^Rglv&<~^s9T8u`of+?z0%8ki(AWWmNx6I3tua=zgB!U z@HY?|vFtu>?b=&DiO_0=C|L&Nl#2}DFCfDggJ^1)k9Uc{@?vN@zH{a7Er^t}*cI0=)*-x;Z&rL& zx`X=my3b2pYg^V$`X$0`c+jllRv1+KgK`5Z6hNua#R0cd>i_ZM2N>Jd)5BK`cGo?x zRQJxi09LrG%bh#UEiE2G$*!W?+H)^TAsum-+M80z#}!MjKRQ2GEiTfxu+Eq()pgu$ z&8^3OE06=yL)kPA`2%;qSQ!dffVlYYxQ*shH!oPRe&LCEj7ZR$@Ee?IQ$; zgT`a{B$8-`IziMn&GypSGiR=e7S}A)j2VM(zWVLk_wRW_W49W@5v57o++>O*9o7@U z2%_z?P4p&we%WV&*zKsxc(0yYX&^}=2P0Ym0)u&2w&77p)W2IXH?>@qF!I*i)^d3| zGM^5pc2x~Wr0h)&Ze#o_04bqf7@~zKpwQEVy|vdsI**k4wF|+m?5Jm`cc|xn>_2=N zH&=V0CoLq~isikHM^JD~_o|fKKUF z`Q-CBm=_)T<2P^jhX#6e7tIM;{SR<+bHm`McPl4|W@y2lgYJpe)=Sn)OLpy71qCHU zWMm{nz80^Agrt2jw;JVaM;u#tb1MQb_>5a zP{$(R?%bB6#+@7NbcT#P?iPis<(1~!y(|adQsI*<2xVa{X+BEO_?5h%YDPr;i)CJQ zX0mKW=FKaFJ(Ps~&?*sG#Womhd_*%J#kwKTsvu8P`s1x3ea)iNf3%A~m~Osg^2p93V@*NryJ0t-qPuPd(c?-d+XO%6R^N;<12%eO7b zva#K1E*S8h>5h|s`_tns1*~!T*vzE2yZDj|)}@cnXxVdDCe~@O&Jxwtw6H5& zGK8q?z9xIfweu={W8I1z)`u5|hpb4H7rvjBO~B(yQj9= zM{N}MwXxBp9_Zx8R~{?|17!UOjM=oML1$)V`A2SUZszy)^?@cMK8`jy09~x8V3cU; z=m>I*yd!o~vx(f&)v7)=oX|yu=bTuWF}qUmKB;QVB~^!olLZ7xr(RD-mA>_{4D0b6 zz40p3T>7yZHoLR7fI?Et#xv*m5`94(_iIQ5{qo}B8!%W`GynMdBcVC~#-CD9{ zL)VQa5^QW#LHXep#1NB7JP*=`bc+e075=9#Cf*mJ0V$*8nRS1S93bpwkx2GjC)IGn zG&Ju|WmvUiW8dhAkIb!B`I9S_ChHr>Cy|$b<>@XR5?CI5B*CJ9I`g1St$TmbiQc#R zRd}@s%>(44l|OjgI%Sslt|4sv;Z4-9lx%43Fc93~9(1ElI2B$c!I~2aq}2Fg61Xki zD;3ZdK<)sb1r?rKQ-GKGc+9yfTY=x+`p!4%I79(s)&@a*2j%N~qxa;gAPMl1lq~mf zk55Pd^0xG}w6Q{e8)FV;4a%WgrDq>VPz{a4Q%DjgAIR=Mi|HoKzFszUOTyq2iz9W> zNiIatQM;$?<^C>0*MPJAApGFSO3b zC*Kz|ASg-?I6#(}pd4u9sv`^ur%(B?F}0E(-YPpHfJHsvR|mNC2QXg%&GCcNYXZ%2sccO48WH|imkBShK!Y_M-9pCPPAD%^I%EzNPgjbbNKmwY0T|8a zh&XLs-z9Eu)-MwmU<#;j2EJc^dZl3Bw>M6R!)etGtB;so>kY4@OwaI|AWT6o&u{}{ z50Jw^$s(7=5>!FrfEut6F_q2DP@Woi9iPPF_)Wlkq9BCfKmNdn!{67UZc=BZa!QYU zQ`)xWXR;09lO72^T^3mVd{Y$=*kW-d?pqf3?ooP9Y`$PfN=nMMw*C(E`e1TjecUMn zb-weMo#59@UC!*NWU1E<>p=pBUj$Pa<(D#kyX{0yOnh*GpG7)TYc*s9>Q;>`36vJn^<7 z2eOX>%QJ)!b6kS=ZsE0Z(>=7LdpNCcOax`q%wO1~fh>ppFjWge$AM zS42iHj~YshQ0ci>P*OUP`kbFj06wmZV#%|!v-FINR_5m1pv26{$;r+R6c^Aedq&$F zghD+m=3T}+r3|5s`o2$rj9#{(-{_5ab&}dG`t!q$n&~*wlCOYK5&7fZ1rK4sXCj7hdY3e?>#p)IA<8xoIQk&ZaUhK(TSK!^VLc}< z$<`ty8Vyq82-UU_VVI*8a-&1uWN>zSk`TC~dBI|`9dWJyh%9n3lihQk(+|6uM44DFj^C)7jY8b3YuKs`1u_?XX`n zbWHROt-1aoue^>qx5Z>H+Hcm!mwAz9d?)H0W}DpSXy+Wg#=VM|r-1{nLPd{WTc7i9 zv}M?L37-~-^Si+Bd0ax^$3%qn>6^jrpFTuIeCPWH$sKn0XJn5|eK4k@*mf8C!cZ8cEcpLY^_(ah6!9JEIo2_$6vEn169vha1- zP%mxLnu5*K$PojR@Z$9;ehQ!LLnTs7wJ|w{Z@m$g7l=KmAC*1+LUvccnm&S&SfBMi zpM}o(^)p&u!$||<*WsS^uD|*Yi@QZX z6?5&@DMyPPF+{{}FpJ+mph{ZZx<2CDDy-8HY1VlmSKM0otB>a#rR&~{2s>5%({-v1 zkzEJJ$`+>PpQ;mo9(d7rv=uzt{)R#HdMVLoSw4@`%iTAR4#&4U;+L57jLJ$}smqYI zCVt`mj~a5l*oA_ojcV!4^~XX?nt}!*Sl-CU&HCOx&v0LF$o1fC)hn%hJ+CY!&$9@N zQoR&0XeIgg+Urzyn`jkZ=vjn^Z;d6BE6(zV*-_d3L!E-*6BuPWI$NYRJ*z|F>bv9GA8d&esZK;# z(#HtY$^@(RV~nYBtEt1zGQ;qpTE`2v!+E%3RsDN8bFnuPSMxHfpG!%YpFYH9vhHZt zwl-HCPPXWq zZ1{=KrULwUz`Pz>TjEmGHH#sVnN@~-d6P4*KN~l~T&l+2yq}aK3Qx*@Abu)+jn9T* zHHQ1xgH9jg8sQ$khRO1~^)~No3ER=0>k_9A-6vogNUE$X?Oe5RXEn?lRSb>0yQ7qJ z9JPTdXAV;W=yA6{KfZ=J@gRF8eAB=p@U>ue2@T#q2${oy1tila%M^;^!1?gI)c~%; z14uJ)uPIc~Qx1hc!ebM1Na3px*arIKI`3FzdkpFIFAOL24gFf{ec_h-+|9E5WclNY zzO)lRS5YpF=*(EiH~l~CADmKy@zfxI3Y2-cTxY?z;aXq!`9KWFVRGPwT0cCxr~i`;$TFufR~Uik*@p5;BNw&N8{`Q**owq7mTn;@hyAY z7KF81L(j_*#Seu3&biS+u=>mH8DM<8XV#5KPT1|RkF@J+pKbg3uEo}fH>sh1<{EA} zrMv6(x+O4giFgj{xfE}&`)u`k*6MA!t$aBe)H`Gt={kB)BCdk`awNvJhgFI_|9P~h z@En$@t>1g%6(#OUT>5`#HYViq91-^$0;i{Kn4~50oh@Z82aC6j%0+I{afPmyeD{w+ zAtOVYZ0?{qxon>}pRIZ`CljsVz?u8YiP=s1ZA_##+#P)>JJ-1W`uK(r-(LEBu{!+7 zs$%CII$xP`dv%6o&u9JbCB3BxIRH2TZMt=sn5<%XFb^#c1Azqa^D zk7w=b5{;kMG&;JgPhwqrkg>=btR6(WyG>s0Q!udNySlA@-GAkd0WP zL$8aAW)F(D(>T3FPn;L!{yCEbgNt>LHq4_Ou?{l>tA}yEP`?(ON)OlW2RLQ6bj+^8 zAitgtQrPi9uaG!R?Mbq6i^k8vF69pgU(xfsw&(g>YJaX4hH-U1!dTyF&^wdBY`duY zvHA3-W1sR=or-)|mn#~-)=7xZpNrM?y+#)fS4MuyVhnYsv-h`0LPt4KnZJzM=nkem z4{e8=!CW}lUcxvpuhZIQkSgfICW$Ym6d?*2kZs>F0FeeWq=5#PcE8wN0Y#7qS_A?) z;0p){fw~F&>7@qsY6*S&`0;dB=3|+CyZslU3WjcvPH(@rDK4({?JslQSsh1*hAsf6 z!%@a$Gv$@gGk-4l%EPjbU$p5{(Jy0+9N73i54 zyX$O;`~u|uK)1pTi4Ao+IZG^3h2Hd91799`LdQj$PUWLi!VCF9=c9uwz+)2nFL6;k zBCy*geX@*cNhExGwU3)q|+Swxi5|>Gv zx`r(NZcH$9Ng8co$zba>=wTjRJWpOh=hNshZsewX)Z^J{+e)|Jn?2;?1=|~KO*%?C zzJ16E(Do305&5VIr}(I#^1W~*OZZ{UrAoe+33Cg_f0diwU5;rNd1SMIgyrREFp|ZZ zVoW;Ir?~RTf0@?C%V4YTGUBTrIS5dMU^86z62J)JAQimg48#s?N5_$gi41L{o+yhu z#eIehS9lW>OYwB_HDzS5)7iqVoSF*Y)-4IDKi+;H~%P=+(BpE z=daN0SbA8ddL>(Typ^?~84a3to*(Mi$tl$Z%>eS+pY0iUp&4?~f=0=lj={Z)f(uWc zf$jh%^esu|7JtezP+wjoMG6Z^j+Zjmp1d>c{aI*Y_{u<9Bf~)2Ua>6zG|B-M_R$2l z`aq4Osj2CS88BTTvRE0Z+-d&hPzrjIQ@ucZE-HEwjV(qS4W4VumFR7QA+Ck1h>N^DTB6H_w%albgLN2B^(fnu*0 zVwx5yW{7Mi^wfLO1#WP*aUUtk*e^6x7VXk^-x0qx`ds2){O zY&i>kGuz8d_0pKyQQDDiq?D8RcK*H6sTj&krgHEcc`1k|Z0~=$D2i1tlcT`UyN9BC*uIPgHO> z$2zztN5w_g&|blt*qn%it6Na1S&3hZ_FPQNb<0mpzJ0rxqU*@i3RoV!T$JI1Dfe%7 zj5U|}E;*85FGq)Hy>(1=^QECn4bZ9_oEqME>h^5?SZP_P0(be>c}CzssTzJ9C5R+) zd=huBti^=a<#Az2Fa6i};?~Qm>N( zG%u;F`6nHtYUt#)scMb*#`p~VO@CdXhfk}9v5Z)@J72wsK&P2FhA79DXN-Nq0B@)d zL1gTU0|Le8ik?$#@*o2I(n34D;tA&UO?5m9yMph5_Rot3=u4$CZ?8k}j2Rm({StZSa@Fv<0fba!(0L@yl;866nj8-^X(Per_2EY(p;7jF?Er>bj6 zruuDJQe}`|#zv!Smd)|Ie+5B6Y23Dm_-wm1+q>EVhbgnEa`6d zWC1)?J0V`z(qcGJKJ^WM-;%efOA3yGd*8ko2}@9%7x4hTevySEQP1(FT4lYHSw&~y zRWf#KTfXbr!)>X}mMBd#Votu0Xcn@y{cq}F##h`v%*Qkr-SmFT{gX?N)s8z-s&V4x zBm&R5y+NcJnfV9}S4#IBWG+nX?6d)T9NHO!sSeOC1nk!Dj(Y!O(s0EdfG|$zIyxFw zl+D+ZH<~`axWf(g6}_JdE?K6NF?7kq*#rK^C6hBg4((G)p%XKI(a!=?J2SI~o}Qi# z4ph0tNLJMdn-ojR2A=MuIHN?JyM9X#-4%3n%yx4SA^y#o$E;Uzd2FAelSZ}`hko!UJ`Qq|K9pMTOPOetLN z^Wyu31VDEHYH0RkivHrEq@UO2sT&Hv*yW`TSYc@A{s3}1Y*VBUXCEK6j*=;nRUKSo zYwoC_hWWs#g{!K#gzgUy6eMW5@T^jip+ZqvH{xL*3!hp2G>_TdHQ9Hyj4m)SrA}zs zS7-9Ek%{WFzi-8kBh(oXQheckApmhScGd(gkjpt*`cgz5=6yMx45Fu@&ZES0$M`^t zMx`K%;AkGE|GX6x1f*l#?>|0d&Mj>IHaoYn!otk_jHh1F`BgySS@gsQr-u*c+s++K55E{?oVA2(gD)av) zTZAC%%fkKU^_w9Gv@oz5xw*;r^avB*M%@M7k`GRtID7WRJMlcF zFsgCcTfkoKrBNjuOn-9XC54VUg2zSKG=Xg}o?M43I`?i2==1j-KP71FX?{((nitvU^)jOz)Hd}jBiI$`c z<%>%Gxr45RlTiran)VlkDV;O-X~RV&t7mUnd_{Mghx{~l5>OBsnY{yep+%p*90UG} z)w*X5G|$Pte*2oW0lP<&x<)c=fs_Zcc<$-w+{uwAR`zYZe4owf&X~e`FAA$@ihc7n ze2b3ihRjd-Q&W|3>J^*QmalPD1JoCnnDj0E^09R*$be~3Uii$+(ch;Gcz9>(=kb0Ba<|~feb9Yn113~ z3r^FeIaed-_lscL>LTdg@)7r3;ZgzKKcT}@p#sZUD!STw-a%TBP$ZGDX7qbTM#jK^ zj%S)y-6_z&mG?swMV6CGeQdFJ)V0`eStyQ#Qz9MuHNx0a_Uo*rH~A=Fm}ErED>Aam%KIVlqKPP{Fwl8L=I7@F4FI2B5u=Rfj@KO)ugy-= zWUrXGIGua<#*dE{JWY{9efzA2%w_eut662>iEjom1k9%$T%0zBq^V zSJ$sCS6u}|9dmj6|0Xp;h0u5~lg#|ZP!9DWaswD(N?kjqb=h@3}Cx+4D{oYJ#<^IqgQB1uH@ommiVs%>`d$o23fWlkkwG>Ph+ z*^=<&JIS%DHNGb$qdwsmwNs`FNC0jefkt73if4UbTvXZu67=lP&p~I{H*oUB>3~O3 zubo?>el#>suIYpBT!n?#U@(}l@K)4Eu<(nEi(np4zomw82<=_MxR+n7JrZfLHwp_L zPHEYzfgYmu``FdhA8%D$ymO0>=_QL+4h`eF5L&klOWnszAh2moEeTv#P%KT}0#JiQ zuyz!KuPqO$D>=~3oE_3l1T*!_AymZ3=-rR9uo&sNwCR~b$MZ?1ym~X7?K2-82Lxw8 z%T6Yj5x4XW(9%cw&JMM&zrQ0`^3w!Xh-1@omk`%4g+L*|`1qmI=>($dY!4r`)sFAY z_$*)H=T|iA86K9Fk~+ghO+P}>AHp;!AfaNS@iX{5?VFoRX!-?c2 zLty`9VLz^r{7~}z%NVMq?Wx$<>lzZ9V>!6G1W8(yvN~26c-_5!)rH-ZSARk4T`amZ zvirfvbegLMi+~zh_s4<*7(5+BnRPJ-@BjIZ`Iw`AHAY+`W5YgMe2t{bQVS zcD<#HBOq;G_ETjn_j_>sGyz*30q)6B51N>86Z7P+9x~F`*S~kq9=kpTWCVwY-eNPc zZ{I#1SB-FCQ@Y%z%%;Ttw^68}#J|`2%F-wH%YdkTcXXCPf8d1=I{x8`3lrL#X`fletwdAcw* zNL1(Ejy1%<<~F^W)8vpIB%;{on@Xm~?PYR4Z&t32bu>9^bx@lwyOQUk*~o9&ON|w* zlUI-yH6Brn(nP#c#O!)M0ZH=N_C`;;mG13Mo$V!(YqE1=gBrc*YweAnf8KTzq?xBP zZ@?z9BId7wMg&X8+Z!v}UD|r`ZL`Za)MnJHPQDJ$24Sr+JhHEGrFQD8bZ$cX%%=@C ziF$!$yD}@__j0rXy-)5Yk|?|=Kdr!zS8#@^pHilMdhwzJZ_R-evFo~ur84r7*Nf;T zyS010+trhmWrK-s)mfFp<%22#dn1ne^Gv|bfZO3pNJ>JYq^z8knR$VM z0ed>An@p^km~lklnu^H~xiYU(bVKuQ@_8}j=P_V^_D9I`EBR-D7>R$)Q<3L1@kZQgo0F7~uNJ-C&d_fj=zpyed9*C&D5Di_VU*1NnR zo-E}M*W~Y^3VIbpN3QFK&v}k;&^=o`KEpmjGfR-{ePlD?*=}?D>arsxGLV)jvE$0A z(oYR<5vf#lfpmx)LP?-i#(2N|m-0zNAds^=G6C~^AehsZ~9WSx(}LcXK1+BBm1YzBPMG~a@I zY!dse{f>Rq-Yx~h)WIzZdPJ0pZG$^QR##$}G#0wCZ64P1h;<-1b~T+HlZ{jxXB| z5BQIS^yr;m#<#1}iC&&_%bkH=XQ-Y!dl8-*a3?widjns~i3tR2Yu+=H_h#`*6-ow6 zxh_K976GjHyZ#ym2F5?yf_6w6=B3=EgW6|Mj)CL-)}TKIZIU|Y!F#9LeL^u77}7cS z4hk5{ZQKuQ$_XshDU_TElE{JEs=tywm=~>e26cjp8FqHBG(o!&^3&{H3Trse)u$Tt} zsvXHZ4D5PrIyQJ#qP*`B+%}%2IUDmO5jbocO}+PBi0;qaTp{_@ zMs9)_q*X>0y@X}cUV_)+8?{W_qn{}W16roj1b)yhwL(0VWuk2&z$9>+P{HPMYNFeR zyq5NK=w!O zHb9Y;dOlU|QIQqMb(0VVE7_}0yt{tF=a%+=Nb2r6=<>Cnf<)YG&=_+CKL-8Z`j11e0T@^=EPOzBv{ZdiPwu@sLCSAD zh@DR0QcpCqvFQeVMSd&dsvtoS=w$xilwn|!0o1oajuZM-oj34*aXQa!G;guW6wrnap$D{hAJbe=c66 z2b)B}#%Oy6_32Z||KaStp_w)Jve*NKgi{9^>^SWNwbv?)Z`FPx!QuOc;qR_!# zHGkb^Ya{zy0h25pU{^qE1Sj{_v#zAmF($R-u*%nbydFzW;6Pnw&!K2aQrz(2nlxYJ zVCk2WYXKQqsN0T3$+b%NHs#sqrqBYrUBRkxk&A`US7#%aY| z2aCmb3It*VeP`N2RAHua)WKb0Iw=k@b6ua~2TL-`JkjG46vZFe_lN>Eh}oKJ%612(l8a6F*}6R0c7d*60gvw=5E1{XpInD_!*fO}_$t7M$axn7wi= zIFP0c95t7@j!8=igb!4^&Mdbz5>oS2eO)Xr<`JGTX+0SuD83xQMnj#?w}?ldr*ug% z^qu|ZW&E})2-~y2K6*{Z7&APh&sxgJoQW6+%`Z33+)aDWj#{njGhB>7PAmo$=Enbi z#yG!XPOufMfO8^(0378RQa2k#-5g+Z`_w;;!D#5MN8~V(r&gDpHvJcFFNqVs_uFKw z^(bNM?_Kn_-fu#M_G{n~_iOs0kDa;KSQTU#@y+i*ydoW(EpvCUtdn!8vSUVW9x$dka(u z)N3empx5!C+#Lh*!a~-}{0?Nl``!lm8ulfB(?qYix(RAJywd#z!fO(h4ffi(R{P3^ zs0zd^39RxG+nc$3r|T0n-J0Jk+{ymBwW48IRAlBu?A=g9 zJ3QOe;wQ&e`NiyVSue1J@uZ_3(S`~pDTw*fjxL#`gVa1rpz+)qB@Y9a1h>@bQ z-k73Sg45xPUrt{iCY8H<5Mo0TB>)jYx6Z9ieD1>A2y<>`3KzMd@yRUiO%o@kc1zey zna;r@4Rwirt$y;%h&! zwC|U_YPCEgy`5jkv0dccOplPNmBE?og{<=*-A(~t1-qe}bJPgUjovWVFwEY-&FBCl%?kMKF!I8V z1*aarsl{s4wooRNmv6Xlu1D3q4saXW$;dyyKbLiz6#fmlOkNZ8oGBu)9(DtE921Ka zM*yjm_ezgx-@W5WOzP9Rc~##9(a@ypVK>jTIe zF(a3++lBffdLk6uV$kWE4QhNkf#?M9g&Telc!`;5s2YRCU@*c6O$Udf^ID>ck8dTM zO#EDmP>8lO9S{`#EHWkTlh@$acSUsGuTK-fKBIqTBVeyHDgc>3pOt#b5MY5<0p&Q`;5KayYF5I1y4^QAkzD=dee<5rh z3g`0*QN5k29+cCmY-1`%T>HGkz32VzN&x$Ul5t)A68782U@#ugO?JSE>&s{=vd{Q? z-%6I#le5X7+9};Ey*3LS8OoSKHt@Q^?D)fWD0(RQK$YY`_T!H~4Ep6xrrQC8CPhrM zaJwOe7^q%@_f;y9h#6`g+-n2}0;rmzUt;jh-er7COUtXVkO6P(;HOV_@7|4$j&3H9 zyF&&8rzrQIQ?$mnLbAZ+o3)&k^n-$`>R+GD0$g4oAC#o;H>ghf9y>o?0ncCC*wa0Y z-5Z_c@zMQ+&%?wo9hpovr5u^pKiHr+FU)^xqAOS$k0pBSBr`f7E9gy!n>^GefK@3$`m{O@aaS~PpLV66*v~y8@ZAxwezqcF^~N@ z0~yv`whd1)8QjqL&iw>#TA!xtLVRF2EBIc&2d*t!HoE`EcKJ}{a zu!>~eY)=X(07vU)9BE15he5jqU3vQ527F)&GC@H^^Iq(b`U}osCCNXFNlTMOS3k#X zcgA;n0##c$+vERr_cbqLL+(?4YSs1I6eSjdL?g-VTD@X?KXrEnwRrHPm8n`eor8lo zbh5hcp+1P1Gyi(&W|DY;#wk1Z7eN#sjsEEF>U5H8HHECYjLPn%Ze~J67)>K**0YqP zp8^~TrlFfvV8GzAkdVx>o4fneV*EWVfQSNLn zeX{UWNc{|sSwaNmG>*Cy+3n0N{)b(0`PEYrw<8tBdn<$CWaL@}XOT7h)DtLfxiZ+t zT}82RPh_n8hWJMF5PiVa;h6?8pDhi8Go@y3_V(s849O8nKE{5KZ;086PZW1^rQk+; z$R4#!;p6TExB~?HwqSUWatw1D#r&nwj0V0BmhZr;3|&z~-{9yUwsfgkA6eEMK!ig< zKR5`Vm4e;+GU{-j-T|Fl^o>3lMbanTjZZZRUEnLlQYvtrg#5o-T`Kf>h?qVTT66*> zLmp4=I{JV+WM0y!V*lw%&U}NQfMJjm(E}4`m7_<~+qH)Ik6nn=*X3;FOb(3Td^2b1 ze3H7ORZ)kt>mnVGe|H~Alu)1!>6Q8TvEFT}@p7@I{7?~-5TwY(xV(%LKzY(cB|HTL zW9AkXK)XCCF;UtGtT1!*aj>#41Md4+7nGHe6br{K@8#AxU9Rv&YRm~I?ti>v_FjLv z=3Dx7<)_$t_Xx7K+~Jf}5xwlH@!fvGB7~dQaSMUkI6PjrIOm=5y}z6pnDgQ%a7QC znr*y-RR~>^BoHiZ12cABGIrg|X-4SpIpQI(nyO|g3Ya1&OKF}>dhTJJ$32I|p}-z1TH(HP>SzoZ^q4jHT^s!??lA7a}uRCd-*(yJW zuUx!*dAY8AXa9X<@xWMq;r6|YEj1Rv3}tJp71PuVK$)0nC>a{cs62dKTAE*8eifuF z!swI*h?XAA3m-1e0XCFvdt2srNAVjbDhU>n9D+K#=oH)(IQU?S_Xn+!zrqb1s>T9r z$mh*jQb&GnysTo;#p!m5hm(8C};6Ju{j zMk_fptd=wSD6uYkg*mocJ{>P~I`KI?jXaeLXkdzds0~aA@}je3NinYbtTL% zpg#Z07O`R0PW*H;84x}Orf+N|m>UmGctK5HGqb}lhvy?S?G0v44%%Kod=2MF5Y7QL zfYE>p)L;B_IHKW~doAcBrH2z6 z#=rEmUi*-nU1#2>q}0qp!2(s3-Zh)nT!G~K*Z4*wG&?JTlpL^UUR-&hkLP{ud;qNi z+^Ey`9&*6pX-3JS_32ew*qY6)XB1)B*U68o1-TB{f_AZ#=^F1P~n?5^GeJ`5C z@ZF#o5G?tayXIi?96jg`VB>#V5|Yw&2cvUPbixG9ngE@J1N`U(6njE@4Wvn+6bMBp z_(x7ZRppV|yAI2B%A3#AA6@cdD_DCwzfvAmTiO@-y}Um7G`;D&O@fcHa>M5@j^jK5){J?A~?L4njX4Omob309e9Ng~3Kv|YRLXj{XrY%Ys z{Gc!0YEP}-Xa$}oKj#HpcY1UXO)5F-aG+E*qsy##DWmi8l}q12OVy7>u5C7ejc2JN2 z*36ETkXc6Csre-6O>D)WB{)EeF@WJv3UA-w)hq_G!X)+RiN`~TB&-bG`oh%m#Q!`= z(2hk%O=u8~4toE(@Wp~tb;kW)GXDoew0wK_KOv$UfgBZVT2$(^0tl8W+t5p4^3Ugw zx9F@yy)Nr_Z-`s%@`}1SxVQOQA>jmBp=Z=#=v7u$mT!|2Tq(?mW9(yV8_C1>hBq-0 zRQ^Fj8D04w>l0Fz{3{C|P&_DwmVonar)>>+2W@cPmTztF+P`5EO zJpEwSJDtx`r&1X@Q4icJfm}Iq4UjQtKW9+hW07Cvkk?YAk4&spy0KX0L7x_JrhuV?RI-iV z{Mq1ccJ9{Wz0Uz-E{lLKzdtB>VJ7F}-gi##>i!qhBdlzyW3Llbw;1iW4Wk?^~RMVv`T z6n5nUuGvSRc?r7Uw-jX9p{Ms8LtqjD=`-Rb2)F=1(jN>4^H;)sF`$@uB>XGmz+6d- z=Wx7$UK&WL!T28rf;r^F!XP0{fav);yG^I{oZ9ONnANUuvZoPU?l_f2!8KAgDF60n;@LSJ#uZq>@g6yq2P|Llz&xG%i!6s?&NtqXcgb~9C{>AFdBuqxE}yNPaYE7F+et8N*(5xxII5+JkXYWax~dUpDXZCxge$FIZYmD9^6#7)d2pok=f`1JH0fR zw8h=K_HLBd<**SA$+e!;Mi*or0ik#_Ed|Dy(Nr6T;Q^ogK89FFe`xdo?O{Xy3V}2O zExHVIxW!DGuvo5vIMl@Q-cEXZzmw+0$rYYss-^|0GE)cZv*Hz)b zJ!g0BltyoaI3k#`tqt_m?&F;}v9rBBKcB{9Tz}KhP+whL)8Gy>d!-aOwLLNM18CZD z26S_I2d*K&jGkmAVit^tao4=dy7ZtJoKJEyyCM#=m{{-d^aexGngZu=L=NYO|7@uv z3=@t@nCp35K9^%#zQNeR^^+ueKjJS)7|1y`uSUG};P?>vuiO?aM)C65gVzN?Ig zIJ58Iv?L5~IgIF^<+yoz{@*xYJcm2F^+b{Lu_9@N62D}!Sc)tMw>$B^Sr$oNJw#@{ zVK1@jhxkmI&83Ug1A9EQc?RLdd~{BDWQA1mnz|%F1Iy&y_N_8UJg~fS`jmNhz!ck+ z^qhy+Zji(B&$$^4t6#f;=5*ppH@gNnf^ z58U6_S&M%FoOxkkX>qa6x4O8dCjYysrj3o4^zG|+Re`Cd-U=sXvKzn^`(nU<5pb5! z8`&U7oT}5YPp3$A*-hg2@cCh^{Zgo@9F&r(;z=e~(aHdzFbL&EXvp}EeE3|Wb6^4* zEC0|dmE9QsJeiPhVTiO5YebFf(&ASt3PO^;+sHbbg$2Kbg}NbHy=x{!wWT6ozsm51 z^&DQX?EkT;)F-HwJG#lp!gptYaBWPk3FQRg1^IG5&28tMTh4%$d+G{NsNya^C$ zUh81bYCJhHF)_^@1Q=@>Z0wMtA_o-}?uRPx0j8;4EKv1To5$dp@SVwUXc^ei%K)mi zm^cN5Emvn)cfh4WxNiXB7z|x@PzEnUO{6&4>wJ6i@nyFILdu1m`K^~JpqI2Y-3jz8 zETmq0s@qdin!KCtPb0Hq^=>+Q_>VKII(F@ji<^U5zA-lf%VLRAeo}wBnfZ=sE_OeG zm?N2~5o4=}WFS6A4qG~KR5UsMUmO)X%Z_2rWGMp+6?os*(`KXF>U}0GC8bIB3`1hB zz}DiH+!w|?X;)3n5BctAu?A`xdxHz_d%=WoUM#g~LVm2jf`Nw^G_8idw-2bR^)(T% zP&NkQYDE@x+}x0h%?i1GzF;INc%RjD>2Vbes0IDlRWw1YI0Dg@iMEGln@Q}6GEAJB z9lEiI~EMI{?M z+aDEO_-pJ#H!r71*2|X|F#{gZZu7Qi6@x$&-sU5>w|+ctJbUFZL`w?k(xZ(rwjP2z zKbze6vPj(a#g6L$pvUZZy&Zg6wXnrQc7McSuaH&G#=KAD?;T$6L+QZd9C#?EnGE|j5o&f7r zx+1RzPPf~r@h>}UJo1dr(CMbBPFvEu(A9NnSAB6|fgTj$rf>qpR}yUi4{2%&)pWKn z&ZJu`TC=_&add`qBJ}(hdfOC^rJww5%uccvuL$>f5d*p+Ru@~}?QLb;cZqqB!uZ4Y zm2l(Jy&(%}ASgsWDf8Uv0d6F?cpY7}vz?W)yFi5ieYoA_%8nJAs)@}HLB?jw0CzO3 zu5e%ejIcQ=ox}<8I}Z~tg_Ry#OZkL1gsa8Teg(i=GrICX3)g5am7~3wYLM2|v;fB$ zJA$$kf3R+g4yF(mKZ%rPXQTS2gvUm>)r`#!JU@92U+lGQcQtG~awB3Lj7yLs$N?#5fS$yL zcw!*wzT16G8XmrIk$YRDJN;UqP6o5qZaFZ%0D2!;6Oz}*T{PV*<}F5uccJkR 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%0A)U(u+CqqJJx$KKx-bVpG*z}lq&ea(oA8lZ`x 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 Q&X zft@BjgGk>6GFXK;+ui!s6nE3upOk>5Yty>rki$!hz}5qVf)VtIMZRUBoF* zcJ6_3N}$CNia)BuHV$Nkz6beiz;X)>J(JeyN)IS1fzg(ek#TX&=Y*lx_T=E8p2||e zwYOB%V~G2Qk4hr#L;4Is^6tZjVvzc*#bp4#=rZD2n3*Zxb<~Uerxvhj-?0Au?V}gq zr2d_=h*f3*ff$2X78Q!EzX-jC4ql)c4(i09#(`l#eCs4g9=6v7!SBPb07{P^%gcC5 X$*M;ALA(4{g!koSl%%sIpZNSg00Qjx literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_sample_small.png b/doc/qxentityeditor/resource/qxee_sample_small.png new file mode 100644 index 0000000000000000000000000000000000000000..eca696a42205b301339fed26f4555e728a4e47b7 GIT binary patch literal 176076 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 AU;qFB literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_source_control_output_directory.png b/doc/qxentityeditor/resource/qxee_source_control_output_directory.png new file mode 100644 index 0000000000000000000000000000000000000000..129084f0a7389b576081dff1e63f5adb5b464959 GIT binary patch literal 14529 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 RkgN#>AfqB(E@>9@zW@hENyq>I literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_splitter.png b/doc/qxentityeditor/resource/qxee_splitter.png new file mode 100644 index 0000000000000000000000000000000000000000..4ecc8975a7b9cb438fc387ef6d17245a113619ca GIT binary patch literal 179182 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 literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_tag_project_state_01.png b/doc/qxentityeditor/resource/qxee_tag_project_state_01.png new file mode 100644 index 0000000000000000000000000000000000000000..67098cfd0b67be6c5100a9c38d23d961c9c350e3 GIT binary patch literal 7776 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 qnhBKcooKuc3sqvYnp(EkAqt_2AI literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_tag_project_state_02.png b/doc/qxentityeditor/resource/qxee_tag_project_state_02.png new file mode 100644 index 0000000000000000000000000000000000000000..d3ea5ec1823c47b48d06071d84c5142eaa42ab5f GIT binary patch literal 13755 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 zÞ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

    ?m~hSOqe%Ugd-6!5cZo9rXJj-D#-3xjEN7 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


    LLgZL4Np;2+5?vVD~<0>-OS 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≦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∨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 p0?3vDhXU38 + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic + editor (data model designer and source code generator) + + + + + + + + + + + + + + + + + + + + + +
    QxOrm + + + Windows + Linux + Macintosh + C++
    +


    + + + + + + + + + + + + + + + + + + +
    HomeDownloadQuick sample + Tutorial (4) + + + + + + +
    + +
    +
    + Manual (2) + + + + + + +
    + +
    +
    ForumOur customers
    +
    + + + + + + + + + + + + + + + + + +
    + QxOrm >> Our customers + + + + + + + + + + + + + + +
    + Current version :  + + QxOrm 1.5.0 - QxOrm library online class documentation - GitHub +
    + + + QxEntityEditor 1.2.8 +
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    +
    + Note : QxOrm library is an open-source software downloaded every day : so it's not possible to + give a list of all users and all applications based on QxOrm library.
    + Here are some companies which use QxOrm library and QxEntityEditor application in their commercial + products :
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + AGCO + + AGCO :
    + AGCO Corporation is an American agricultural equipment manufacturer based in Duluth, Georgia, + United States. As a leading global manufacturer of agricultural equipment, AGCO offers a full + line of tractors, combines, hay tools, sprayers, forage and tillage equipment, which are + distributed through more than 3,100 independent dealers and distributors in more than 140 + countries worldwide. +
    + [Description based on Wikipedia webpage] + +

    +
    + ENSCO + + ENSCO :
    + ENSCO, Inc. is a provider of engineering services, products, and advanced technologies for + national security, transportation safety and asset management, information sciences, data + management including information management systems for weather monitoring, aerospace and + avionics, and R and D for private corporations and local, state, and federal agencies and + governments, including the United States Department of Defense. +
    + [Description based on Wikipedia webpage] + +

    +
    + Smiths Detection + + Smiths Detection :
    + Smiths Group plc is a British multinational diversified engineering company headquartered in + London, United Kingdom. It has operations in over 50 countries and employs around 23,550 + staff.
    + Smiths Group has five divisions : Smiths Detection is the world's largest manufacturer of + sensors for the detection of explosives, weapons, chemical agents, biohazards, narcotics and + contraband. +
    + [Description based on Wikipedia webpage] + +

    +
    + Veran Medical + + Veran Medical :
    + Veran Medical Technologies is focused on comprehensive oncology program. + Veran's technology is defining the new standard in early definitive diagnosis of cancer, + providing your hospital with a comprehensive navigation solution. + Established in 2005, Veran Medical Technologies is a privately held soft tissue navigation + company focused on the early diagnosis of cancer. +
    + Veran's mission is to extend life through earlier diagnosis and the delivery of minimally + invasive therapies for interventional oncology procedures with unique navigation + technologies. +
    + Veran is dedicated to increasing patient survival while providing cost effective healthcare + solutions that defined the next standard of care. +

    +
    + Altran AIS + + Altran AIS :
    + Altran Technologies, SA is a global consulting firm founded in 1982 in France. Altran + operates primarily in high technology and innovation consultancy, which account for nearly + 75% of its turnover. Administrative and information consultancy accounts for 20% of its + turnover with strategy and management consulting making up the rest. +
    + [Description based on Wikipedia webpage] + +

    +
    + Intermountain Healthcare + + Intermountain Healthcare + :
    + Intermountain Health Care, Inc., officially doing business as Intermountain Healthcare, is a + non-profit healthcare system and is the largest healthcare provider in the Intermountain + West. Until 2005 it was known as Intermountain Health Care or more commonly IHC; it is now + often referred to as simply Intermountain for short. Intermountain Healthcare provides + hospital and other medical services in Utah and Idaho and also offers integrated managed care + under the insurance brand SelectHealth. Intermountain Healthcare is headquartered in Salt + Lake City, Utah, and (as of 2014) employed over 33,000 people. +
    + [Description based on Wikipedia webpage] + +

    +
    + GSI Electronics Inc. + + GSI Electronics Inc. + :
    + GSI Electronics Inc. develops, manufactures and distributes innovative technological products + for the agricultural industry. GSI Electronics Inc. unique expertise allows them to offer + accurate, simple and diverse electronic, data processing and mechanical solutions for + improving agricultural production. +

    +
    + Sagemcom + + Sagemcom :
    + A French high-tech group of international dimensions, Sagemcom operates on the broadband + (digital home, set-top boxes, Internet routers, telephony and multimedia terminals), telecoms + and energy (smartgrid and energy management) and retail. +

    +
    + Motius + + Motius :
    + Motius is a unique German R&D company. It solves technical problems and develops products for + international customers through a pool of elite senior students, academic researchers and + young engineers. +

    +
    + GSVitec + + GSVitec :
    + Production of high-speed video and measurement data acquisition devices and software.
    + Production of High Power LED light solutions especially for high speed video. Production and + design of High Power custom LED solutions (crash test, military, r&d).
    + Software solutions for synchronous high speed data and video acquisition and recording.
    +

    +
    + Promenade Software + + Promenade Software :
    + Promenade Software is a company comprised of highly skilled, experienced software engineers. + Whether it is for a medical device, avionics test system, or other sensitive system, + Promenade Software knows that their work is critical. + Critical to your company, your customer, and everyone involved. Promenade Software has the + skills and ready-made tools to deliver your product reliably and efficiently. +
    + Unlike other software service providers, Promenade Software took the time to create the + ready-made libraries and test tools that increase your system's quality, while saving you + money and development time. +

    +
    + IVE mbH + + IVE mbH :
    + Since founding IVE mbH, Consulting Company for Traffic and Railway Engineering Ltd., in 1998 + IVE proficiently supports clients from the development of first ideas to the final + realisation and the practical use of projects. + Based on transport and railway expertise, IVE mbH derives concepts and future-oriented + sustainable solutions responding to the problems of clients, strengthening their position and + preparing them for the global competition. +

    +
    + NauchPribor + + NauchPribor :
    + NauchPribor is a high-tech company aimed on developing and producing low dose x-ray devices + for medical and security fields.
    + Founded more than 40 years ago, now this company is one of the most advanced in + business.
    + It has customers from all over the world. Headquartered resides in Orel, Russia.
    +

    +
    + Tang + + Tang :
    + Tang A/S develops a PMS system for the veterinary business.
    + Their products are selling in Denmark, Norway and Sweden.
    +

    +
    +
    + All organization names, logos and trademarks used in this web page are for identification purposes only. + All trademarks and logos are the property of their respective owners. +
    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/doc/qxorm_en/download.html b/doc/qxorm_en/download.html new file mode 100644 index 0000000..1d43e1c --- /dev/null +++ b/doc/qxorm_en/download.html @@ -0,0 +1,1254 @@ + + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic editor + (data model designer and source code generator) + + + + + + + + + + + + + + + + + + + + + +
    QxOrm + + + + Windows + Linux + Macintosh + C++
    +
    + + + + + + + + + + + + + + + + + + +
    HomeDownloadQuick sample + Tutorial (4) + + + + + + +
    + +
    +
    + Manual (2) + + + + + + +
    + +
    +
    ForumOur customers
    +
    + + + + + + + + + + + + + + + + + +
    + QxOrm >> Download + + + + + + + + + + + + + + +
    + Current version :  + + QxOrm 1.5.0 - QxOrm library online class documentation - GitHub +
    + + + QxEntityEditor 1.2.8 +
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    + + + + + + + + + +
    QxOrm is a C++ library available under a dual license : +
      +
    • GNU/GPLv3 license : open-source and + free (to develop an open-source project or to evaluate QxOrm library) ;
    • +
    • Proprietary license to distribute an application based on + QxOrm library without any restrictions of GNU/GPLv3 license.
    • +
    + + + + + + + + + +
    Download QxOrm + + + Download QxOrm 1.5.0 (source + test + documentation)
    + This version has been tested with Visual Studio on Windows, GCC on Linux and Mac + OS X, MinGW on Windows, and Clang on Mac OS X : a C++11 compiler is required. +
    +
    +
    +
    qt_ambassador
    + + QxOrm library has been accepted into the Qt Ambassador + Program + +
    +

    QxEntityEditor is a graphic editor for QxOrm library : QxEntityEditor + provides a graphic way to manage the data model.
    + QxEntityEditor is multi-platform (available for Windows, Linux and Mac OS X) and generates native + code for all environments : desktop (Windows, Linux, Mac OS X), embedded and mobile (Android, iOS, Windows + Phone, Raspberry Pi, etc.). +

    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    QxEntityEditorQxEntityEditor 1.2.8 - Windows 32 bitsQxEntityEditorQxEntityEditor 1.2.8 - Windows 64 bits
    QxEntityEditorQxEntityEditor 1.2.8 - Windows 32 bits (portable)QxEntityEditorQxEntityEditor 1.2.8 - Windows 64 bits (portable)
    QxEntityEditorQxEntityEditor 1.2.8 - Linux 32 bitsQxEntityEditorQxEntityEditor 1.2.8 - Linux 64 bits
    QxEntityEditorQxEntityEditor 1.2.8 - Mac OS X
    +

    +

    Note : by default, QxEntityEditor application is limited to 5 entities per + project. + To get a license key and create unlimited entities per project, please contact us using this e-mail : ic-east.com. + Price of a QxEntityEditor license key : 500€ (per developer, 12 months of free + updates).
    +
    + A user manual (documentation) for QxEntityEditor application is + available. +

    + QxEntityEditor +

    +
    +

    + QxOrm is based on Qt framework : +

    + + + + + + + + + +
    QtQt : cross-platform application development framework : GUI (QtGui), + network (QtNetwork), XML (QtXml), database (QtSql)...
    + Qt provides excellent support and documentation. Using Qt, you can write simple and powerful C++ + code.
    + Qt is produced by Digia's Qt Development Frameworks division and is available under LGPL license.
    + QxOrm is compatible with many Qt's objects : QObject, QString, QDate, QTime, QDateTime, QList, + QHash, QSharedPointer, QScopedPointer...
    + It is recommended to install the latest version of Qt available at the following address : http://www.qt.io/
    +

    + +
    + Here are all changes logs of QxOrm library and QxEntityEditor application : + +
    + QxOrm library history : + + + + + + + +
    + +

    Changes in version 1.5.0:

    +
    + - Fix qx::dao::fetch_by_id_with_relation() when a table alias is used (table alias is now used in + the WHERE part instead of table name)
    + - Fix qx::IxDataMember::getType() method when used in a multi-thread environment
    + - New method qx::IxSqlRelation::linkRelationKeyTo() for MongoDB database to simulate lazy loading + for relationships (GitHub #107)
    + - Fix qx::QxSqlDatabase and multi-thread issue when the OS assigns an old and destroyed thread + identifier to a new one (GitHub #42) + add method qx::QxSqlDatabase::removeDatabaseByThread() to + call at the end of a thread execution
    + - Fix qx::QxSimpleCrypt class with Qt version >= 5.10 and error "Attempted to overwrite a + QRandomGenerator to system() or global()"
    + - Fix database not opened using async queries (GitHub #109)
    +
    + +

    Changes in version 1.4.9:

    +
    + - Fix compilation issue with Qt 6.2+ (due to Qt JIRA : https://bugreports.qt.io/browse/QTBUG-92910), + more details in GitHub issue #54
    + - Support QSqlQuery::execBatch() method to improve performance inserting/updating/deleting a list of + C++ instances in database (new optional parameter 'bUseExecBatch' available for functions : + qx::dao::insert, qx::dao::update, qx::dao::delete_by_id)
    + - New method in qx::QxSqlQuery class named setFctOnBeforeSqlPrepare() to define a custom callback + function to modify SQL query before preparing in database
    + - New callbacks functions available in qx::IxDataMember interface to customize SQL generated per + data member (see an example in ./test/qxBlogCompositeKey/src/blog.cpp file)
    + - Fix an issue in qx::QxSqlRelationLinked::hierarchyResolveOutput() which could call + getIdFromQuery() for nothing, which could generate some warnings in Qt SQL driver (for example : + QPSQLResult::data: column XX out of range)
    + - Fix a compilation issue of QxRestApi and QxHttpTransaction modules with Qt QStringBuilder option + enabled (GitHub issues #50, #83)
    + - Improve SQL DISTINCT to support relationships (unit test available in qxBlog sample project)
    + - New feature to fetch relationships only in LEFT OUTER/INNER JOIN and WHERE clauses (so no columns + in SELECT part) : use {NULL} syntax to define no relation columns in SELECT part (unit test + available in qxBlog sample project)
    + - Fix a crash which could occur using qx::QxSession and multi-threaded environment
    + - Add a new parameter caseSensitive (default value : false) to the method + qx::QxSqlQuery::getSqlResultAt()
    + - Fix an issue with JSON REST API (QxRestApi module) and MongoDB database (more details in GitHub + issue #70)
    + - Improve qx::QxClassX::registerAllClasses() : init all validator instances (can fix some issues in + a multi-thread environment)
    + - New settings available in the qx::QxSqlDatabase singleton class + (setSqlDelimiterForTableNameAlias() and setSqlDelimiterForColumnNameAlias()) to add delimiters to + SQL aliases (more details in GitHub issue #57)
    + - Fix linking error which could occur with Qt6 and MSVC2019 (should fix GitHub issues #98, #91, #89, + #90, #62, #65)
    + - Fix all checks with QT_VERSION (using macro QT_VERSION_CHECK), should fix checks from Qt 5.10 to + Qt 5.15 (GitHub issue #81)
    +
    + +

    Changes in version 1.4.8:

    +
    + - Support Qt6 (tested with MSVC 2019 + CMake 3.19 on Windows) ==> QxOrm library is compatible + with Qt4, Qt5 and Qt6
    + - Support PIMPL idiom for persistent classes (useful to reduce compilation times, to provide a + stable ABI, ascendant compatibility for shared libraries, to reduce binaries size)
    + - Documentation about PIMPL idiom for persistent classes : + https://www.qxorm.com/qxorm_en/manual.html#manual_455
    + - New sample project named qxBlogPImpl in ./test/ directory of QxOrm package to show how to + implement persistent classes using the PIMPL idiom
    + - Possibility to ignore soft delete behavior during a session execution (useful to fetch logical + deleted instances for example) : qx::QxSession::ignoreSoftDelete()
    + - Fix SQL DISTINCT keyword in qx::QxSqlQuery class (when DISTINCT is used, then the primary key is + not fetched)
    + - Fix an issue which could happen after enabling _QX_ENABLE_QT_NETWORK compilation option (due to + QT_NO_SSL/QT_NO_OPENSSL used in QxThread.cpp file)
    + - Fix an issue in qx::QxSqlDatabase and multi-thread context when a thread identifier is reused + (GitHub issue #42)
    + - Add new behavior to qx::QxSession class with setAutoRollbackWhenDestroyed() option : if true, then + database rollback is called when session instance destructor (or close() method) is invoked (instead + of commit by default, GitHub issue #43)
    +
    + +

    Changes in version 1.4.7:

    +
    + - Fix a MongoDB issue with qx::dao::fetch_by_id_with_relation() (wrong id fetched)
    + - Fix an issue with qx::dao::fetch_all when a custom list of columns with the primary key is + used
    + - Fix identifier JSON serialization for MongoDB with properties registered as Q_PROPERTY (Qt + property meta system)
    + - Fix a crash (seg fault) using qx::IxPersistable with multiple inheritance (for example QObject + + qx::IxPersistable) due to some static_cast to void * in QxFactory module (fix also for QxService + module and multiple inheritance)
    + - Remove automatic relationship lazy fetch when not requested in query
    + - Fix an issue with QJson and QDataStream serialization when class doesn't contain any identifier + (so not a database class) and is a wrapper of another registered class (can have same address + pointer as parent)
    + - Improve qx::QxSqlQuery::freeText() method to add custom text to build SQL query : support + placeholders (add second parameter const QVariantList & values)
    + - Improve performance in a multi-threads environment (mutex in qx::IxSqlQueryBuilder)
    + - New setting to display more details (execution times) in qx::dao functions logs (to enable this + new feature : qx::QxSqlDatabase::getSingleton()->setDisplayTimerDetails(true))
    + - Improve MongoDB integration to fetch a list of items : build C++ instances as soon as possible + without putting data in a buffer (better performance + less memory usage)
    + - Improve MongoDB integration : support qx::dao::delete_by_query() function with MongoDB aggregation + framework
    + - Improve qx::QxSqlQuery class (or its qx_query alias) : new addJoinQuery() method to insert SQL + sub-queries inside LEFT OUT JOIN / INNER JOIN (more details in documentation here : + https://www.qxorm.com/qxorm_en/manual.html#manual_3855)
    + - Improve qx::QxSqlQuery class : new constructors with placeholders support + new methods to embed + SQL sub-queries (in_Select, notIn_Select, isEqualTo_Select, isNotEqualTo_Select)
    + - Improve qx::QxCollection class : qx::QxCollection is now a thread-safe container (fix for example + QxModelView module when model is fetched in a different thread than the view)
    +
    + +

    Changes in version 1.4.6:

    +
    + - New QxHttpServer module : C++/Qt standalone multi-threaded HTTP 1.1 web server (support SSL/TLS, + persistent connections, cookies, sessions, chunked responses, URL dispatcher/routing, no other + dependency except QtNetwork) : https://www.qxorm.com/qxorm_en/manual.html#manual_96
    + - New QxRestApi module : provide a REST API to send requests in JSON format from external + application (web services), from web-site (written in Angular for example), from QML or from + scripting langage (like Python) : https://www.qxorm.com/qxorm_en/manual.html#manual_97
    + - With QxHttpServer and QxRestApi modules, QxOrm library can now be used to create web applications, + especially single-page applications (SPA) with famous Javascript frameworks like AngularJS, React, + Meteor.js, etc...
    + - New project example named qxBlogRestApi : QML application with a list of REST requests to show how + to send JSON queries from Javascript to QxRestApi module + HTTP web server application to show how + to create web applications using QxHttpServer module
    + - QxRestApi module supports : all CRUD operations, complex queries, several levels of relationships, + custom JSON output format, call dynamically native C++ functions registered in QxOrm context, + instance validation, call custom database queries
    + - Improve JSON serialization engine : possibility to define a custom filter to not export all + properties (https://www.qxorm.com/qxorm_en/manual.html#manual_606)
    + - Possibility to define a custom SQL table alias for complex queries with relationships using syntax + <my_table_alias> (https://www.qxorm.com/qxorm_en/manual.html#manual_3850)
    + - Improve SQL generator for Oracle database : manage last insert id using RETURNING INTO syntax (thx + to Romain Macureau and Abdennour Boutrig)
    + - Fix an issue with stored procedure and output parameters
    + - New function available : qx::dao::count_with_relation<T>()
    + - Fix JSON serialization in multi-thread environment
    + - Change JSON QDateTime and QTime serialization format : use Qt::ISODateWithMs instead of + Qt::ISODate (Qt 5.8 or +)
    + - Improve QxService module : support SSL/TLS secure connections + keep-alive connections
    + - Remove *.suo files (MSVC++ temporary project files) from QxOrm package
    +
    + +

    Changes in version 1.4.5:

    +
    + - Support MongoDB database : QxOrm library becomes a C++/Qt Object Document Mapper ODM library !
    + - For more details about MongoDB integration, see QxOrm manual + (https://www.qxorm.com/qxorm_en/manual.html#manual_95) and new sample project available in + ./test/qxBlogMongoDB/ directory
    + - QxOrm library is now available on GitHub (official repository) : + https://github.com/QxOrm/QxOrm
    + - Fix an issue in qx::IxSqlQueryBuilder class when QxOrm library is used in a multi-thread + environment
    + - Support latest version of boost (1.66)
    + - Update boost portable binary serialization classes to version 5.1 (provided by + https://archive.codeplex.com/?p=epa)
    + - Fix an issue building SQL query for Oracle database (doesn't support AS keyword for table + alias)
    + - Improve qx::QxClassX::registerAllClasses() function : possibility to initialize all relations + (useful to work with introspection engine)
    + - Improve qx::IxPersistable interface : provide new methods toJson() / fromJson()
    + - Improve documentation/website : change http://www.qxorm.com by https://www.qxorm.com + everywhere
    + - Fix fetching relations with soft delete putting SQL condition in the JOIN part instead of WHERE + part
    + - Fix SQL generator for Oracle database : use new limit/pagination syntax (version Oracle > + 12.1)
    + - Improve SQL generator interface : add 'onBeforeSqlPrepare()' method to modify/log SQL queries in + custom classes
    + - Add an option in qx::QxSqlDatabase class to format SQL query (pretty-printing) before logging it + (can be customized creating a qx::dao::detail::IxSqlGenerator sub-class)
    + - Fix an issue with boost/std::optional (to manage NULL database values) and some databases : if + optional is empty, then create a NULL QVariant based on QVariant::Type
    + - Add an option in qx::QxSqlDatabase class to insert square brackets (or any other delimiters) in + SQL queries for table name and/or column name (to support specific database keywords)
    + - Improve introspection engine : add getType() method in qx::IxDataMember interface to get C++ type + of a property dynamically
    + - Improve qx::QxSqlDatabase singleton settings class to make easier working with several databases : + now there are 3 levels of settings : global >> per thread >> per database (see + 'bJustForCurrentThread' and 'pJustForThisDatabase' optional parameters in all setXXXX() methods)
    + - Fix QxOrm.pri for MinGW compiler on Windows : an issue could occurred to export some symbols from + shared library (some Qt signals for example)
    + - Add an option in qx::QxSqlDatabase singleton class to display only slow SQL queries (see + setTraceSqlOnlySlowQueriesDatabase() and setTraceSqlOnlySlowQueriesTotal() methods)
    +
    + +

    Changes in version 1.4.4:

    +
    + QxOrm library doesn't depend on boost framework anymore (the boost dependency has been fully + removed, replaced by some C++11 features).
    + So QxOrm library is now a pure Qt library which depends only on QtCore and QtSql by default.
    + For backward compatibility, QxOrm library still supports some boost classes (boost smart-pointers, + unordered containers, boost::optional, etc...) : you have to define _QX_ENABLE_BOOST compilation + option to enable these features.
    +
    + Main advantages are :
    + - QxOrm becomes a much lighter library
    + - easier to install (because you don't have to deal with boost anymore)
    + - reduce compilation times
    + - reduce output binary size
    +
    + Thx also to Jimmy Taker for several improvments and new features in QxModelView module !
    +
    + - QxOrm library now requires a C++11 compiler (please note that QxOrm doesn't require a full + compliant C++11 compiler : for example, QxOrm can be built and used with MSVC 2012, GCC 4.5 or Clang + 3.2)
    + - Implement PIMPL idiom for some QxOrm classes to reduce compilation times and output binary + size
    + - New class named qx::any to replace boost::any (basic implementation of boost::any written by + Kevlin Henney)
    + - qx_shared_ptr alias doesn't exist anymore : it is replaced everywhere by std::shared_ptr
    + - QxModelView module : all models based on qx::IxModel class can now be sorted (on all columns), + please note that you can also use QSortFilterProxyModel Qt class to sort your model
    + - QxModelView module - qx::QxModel<T> : fix setData() with e_auto_update_on_field_change + option when an error occurred saving data in database, now previous value is restored if an error + occurred
    + - QxModelView module - qx::IxModel : fix setHeaderData() using it with default role (Qt::EditRole) + changes the header in a header view (role Qt::DisplayRole)
    + - QxModelView module - qx::IxModel : if a description is registered in QxOrm context, then it is + displayed in header for each property
    + - QxModelView module : new feature available to add automatically an empty row at the end of the + table to insert quickly new items (setShowEmptyLine() method)
    + - QxModelView module : possibility to define an intermediate base class between qx::IxModel and + qx::QxModel<T> to provide your own model features, for example imagine you develop a drag&drop + feature in a class named IxModelDragDrop, you can now create a QxOrm model like this (see the second + template parameter) : qx::IxModel * pModel = new qx::QxModel<MyPersistantClass, + IxModelDragDrop>();
    + - QxOrm.pro : fix DESTDIR parameter on Windows
    + - QxOrm.pri and QxOrm.cmake : add a section to enable QT_USE_QSTRINGBUILDER to optimize QString + operations
    + - QxOrm library is now tested with MSVC 2015 compiler (support all MSVC versions from 2012)
    + - Fix a bug in QxSqlError.h file with a qPrintable() call on a temporary object
    + - Provide more details in logs after executing a SQL query : time to execute query in database + + time to fetch C++ instances
    + - Support std::optional<T> (if your compiler supports C++17 features) to manage NULL database + value : new header available named <QxExtras/QxStdOptional.h> to include just after + <QxOrm.h> header file (ideally in a precompiled header)
    +
    + +

    Changes in version 1.4.3:

    +
    + - Support CMake : new CMakeLists.txt file added to build QxOrm library with CMake
    + - Improve SQL error messages when qx::dao functions return a database error
    + - New parameter in singleton class qx::QxSqlDatabase to log SQL bound values + (setTraceSqlBoundValues) : by default, bound values are logged when an error occurred
    + - New syntax to select columns to not fetch : -{ col_1, col_2, etc... }
    + - New function qx::dao::call_query_without_prepare() to execute specific SQL queries without + prepared statement
    + - Improve QxModelView module : all QxOrm models (based on qx::IxModel interface) can be serialized + to JSON format (including all relationships levels) : this is now another way to work with + relationships and QML (thanks to JSON.parse() and JSON.stringify() javascript functions) without + using nested models concept (so without using QxEntityEditor model/view generated classes)
    + - Improve qxBlogModelView sample project and QxOrm manual to show how to access to relationships + data in QML (nested models or JSON)
    + - Fix a memory leak in qx::QxSqlRelation class
    + - Reduce output binary size (~20%) and compilation times (~20%) to build persistent classes based on + QxOrm library
    + - Support unity build concept to reduce compilation times to build QxOrm library and C++ persistent + classes generated by QxEntityEditor application : for more details, see _QX_UNITY_BUILD compilation + option in QxOrm.pri or QxOrm.cmake configuration file
    + - Improve QxConvert module : possibility to store in database complex QVariant properties which + contain QVariantMap, QVariantHash or QVariantList types (JSON format)
    + - Fix an issue with some databases when a foreign key is also a part of the primary key
    + - Fix an issue with QSharedPointer and boost::serialization when a same raw pointer is shared by + several QSharedPointer during deserialization process
    +
    + +

    Changes in version 1.4.2:

    +
    + - Support JSON serialization : each C++ class registered in QxOrm context can be + serialized/deserialized in JSON format (JSON feature requires Qt5)
    + - For more details about JSON serialization, read QxOrm manual here : + https://www.qxorm.com/qxorm_en/manual.html#manual_606
    + - With JSON serialization and QxService module : it is now possible to create REST Web Services to + send data to a javascript engine (web pages for example)
    + - Fix some compilation errors with recent (and less permissive) compilers and latest versions of + boost and Qt
    + - Fix relationship initialization assertion with complex, deep and circular relationships in large + database schema
    + - Improve QDataStream serialization : should be faster now and fix an issue with circular instances + dependencies
    + - Fix a bug fetching 1-n and n-n relationships when root is a container of stack objects (it worked + only with pointers or smart-pointers, for example : QList<blog> vs + QList<std::shared_ptr<blog>>)
    + - Improve qx::dump() function : possibility to display a C++ instance state in XML or JSON + format
    +
    + +

    Changes in version 1.4.1:

    +
    + !!! IMPORTANT NOTE ABOUT THIS VERSION !!! : it is strongly recommended to read the QxOrm.pri + configuration file of this new version (compilation options have changed compared to previous + versions).
    + Now, by default, QxOrm library is a much lighter library : QxOrm depends only on QtCore and + QtSql (boost serialization is now optional and not required by default).
    + By default, serialization engine is now based on Qt QDataStream class (but you can still enable + boost serialization defining _QX_ENABLE_BOOST_SERIALIZATION compilation option in QxOrm.pri + configuration file).
    + So now, with default options :
    + - QxOrm 1.4.1 is much easier to install because you don't have to deal with boost serialization + extra dependency ;
    + - QxOrm 1.4.1 shared library is 3X smaller than 1.3.2 version ;
    + - Generated binaries which depends on QxOrm library are 25% smaller ;
    + - If you are not using serialization functions in current projects based on QxOrm library, then you + can define or not _QX_ENABLE_BOOST_SERIALIZATION compilation option without changing any line of + your source code.
    +
    + Here are all other changes of version 1.4.1:
    + - Improve relationships engine : possibility to select columns to fetch using syntax : my_relation { + col_1, col_2, etc... }
    + - Improve QxTraits module to reduce compilation times and build smaller binaries
    + - Improve QxOrm website adding possibility to search and replacing the old FAQ by a more organized + manual (user guide)
    + - New compilation option _QX_ENABLE_BOOST_SERIALIZATION to enable boost serialization dependency + (read QxOrm.pri configuration file for more details)
    + - New compilation option _QX_ENABLE_QT_NETWORK to enable QxService module (transfer persistent layer + over network) : read QxOrm.pri configuration file for more details
    + - New compilation option _QX_NO_RTTI to build QxOrm library without C++ RTTI type information
    + - Support QDataStream Qt serialization engine (used by default when _QX_ENABLE_BOOST_SERIALIZATION + compilation option is not defined)
    + - Improve qx_query class (SQL queries) : new method (named customOperator()) which gives the + possibility to define a custom operator (for example <@ for PostgreSQL ltree type)
    + - Fix a program startup issue due to 'static initialization order fiasco' creating singletons (it + was an issue with some compilers during the shared library link process)
    + - New namespace qx::dao::throwable : same functions as qx::dao namespace, but they throw a + qx::dao::sql_error exception when a SQL error occurred (instead of returning a QSqlError + instance)
    + - Add a qAssertMsg() macro to put a more explicit error message when throwing an assertion
    + - Include all *.inl files (template implementation) in QxOrm.pro project file : QtCreator can now + index these *.inl files in its project treeview
    + - Rename QxStringCvt to QxConvert : so if you persist custom types to database, you have to rename + from QxStringCvt_FromVariant, QxStringCvt_ToVariant to QxConvert_FromVariant, + QxConvert_ToVariant
    +
    + +

    Changes in version 1.3.2:

    +
    + - Support C++11 types (need to set compilation options in QxOrm.pri config file to enable these + features)
    + - With _QX_CPP_11_SMART_PTR compilation option : std::unique_ptr, std::shared_ptr, std::weak_ptr
    + - With _QX_CPP_11_CONTAINER compilation option : std::unordered_map, std::unordered_set, + std::unordered_multimap, std::unordered_multiset
    + - With _QX_CPP_11_TUPLE compilation option : std::tuple
    +
    + +

    Changes in version 1.3.1:

    +
    + - New class qx::QxModelService<T, S> in QxModelView module to connect a Qt model to services + to execute client-server requests (can be used with QML and QtWidgets views)
    + - Add some useful methods to qx::IxModel class and fix several issues with the QxModelView + module
    + - Support last version of MinGW with large precompiled header bug : new compilation option + _QX_NO_PRECOMPILED_HEADER (to enable in QxOrm.pri file)
    + - Fix issue when loading several shared libraries on Windows with services registered in QxService + module
    + - Fix the qx::QxSqlQuery serialization process used by QxService module to send requests over + network
    + - Fix an issue with qx::QxCollection<Key, Value> class when inserting an item at last + position
    +
    + +

    Changes in version 1.2.9:

    +
    + - Improve nested models in QxModelView module to be able to use several relationships levels in + QML
    +
    + +

    Changes in version 1.2.8:

    +
    + - New function qx::model_view::create_nested_model (QxModelView module) used by QxEntityEditor to + manage complex data structure to work with relationships in QML views and Qt model/view + architecture
    + - New section in the QxOrm.pri file with some tips to reduce output binaries size
    + - Fix the call of triggers to have the inserted ID inside the trigger function with PostgreSQL
    +
    + +

    Changes in version 1.2.7:

    +
    + - New module QxModelView : now, all classes registered in QxOrm context can be used with Qt + model/view architecture (Qt widgets and/or QML views)
    + - qx::IxModel interface provides an easy way to work on QML with QxOrm library and interact with + databases
    + - For more details about the new module QxModelView, goto the FAQ : 'How to use QxModelView module + to interact with Qt model/view architecture (Qt widgets and/or QML views) ?'
    + - New function qx::dao::save_with_relation_recursive(), useful to save a full tree structure for + example
    + - Remove the dependency on the STL compatibility functions in Qt (QT_NO_STL), which may not be + available (thanks to KDE Plasma Media Center team for the patch)
    + - Support database table defined into a schema (using qx::IxDataMember::setName() function)
    +
    + +

    Changes in version 1.2.6:

    +
    + - First version full compatible with QxEntityEditor application : the graphic editor for QxOrm + library !
    + - For more details about QxEntityEditor, go to QxOrm website : https://www.qxorm.com/
    + - Thanks to the recent release of Qt 5.2, QxOrm library can now be used on Android and iOS
    + - Improve relationships and triggers engine
    + - Triggers onBeforeFetch() and onAfterFetch() called when fetching relationships
    + - Fix release mode detection during compilation : should improve performance on some + environments
    + - Add serialization for QSqlError, qx::QxSqlQuery, qx::QxInvalidValue and qx::QxInvalidValueX + classes
    +
    + +

    Changes in version 1.2.5:

    +
    + - New license : go to download page of QxOrm website for more details
    + - Support Qt5
    + - New compiler supported : Clang (tested on Mac OS X)
    + - Now each QxOrm version will be tested in 32-bit and 64-bit mode
    + - Improve QxOrm introspection engine : possibility to register static class methods
    + - Improve QxService module : now it's easy to add an authentication process on server side
    + - New class qx::exception to get error code + error description with services methods throwing an + exception
    + - New settings available in QxOrm.pri config file (whithout changing QxConfig.h file)
    + - Possibility to implement specifics database SQL functions overriding qx_query class
    + - Fix an issue when fetching multiple levels of relationship and NULL pointers
    + - Fix a bug with MS SQL Server database and update queries using auto-increment id
    +
    + +

    Changes in version 1.2.4:

    +
    + - New relationship engine to fetch easily many levels of relationships per query
    + - For more details about this new engine, goto the FAQ : 'How to use relationship engine to fetch + datas from many tables ?'
    + - Add 2 functions : qx::dao::execute_query and qx::dao::call_query to call a stored procedure or a + custom SQL query
    + - For more details about this new feature, goto the FAQ : 'How to execute a stored procedure or a + custom SQL query ?'
    + - Add support for boost::optional type to manage NULL database value without using QVariant type
    + - New class : qx::QxDaoAsync to make easier to execute queries in asynchronous way + (multi-thread)
    + - For more details about this new class, goto the FAQ : 'How to use qx::QxDaoAsync class to execute + queries in asynchronous way (multi-thread) ?'
    +
    + +

    Changes in version 1.2.3:

    +
    + - New interface 'qx::IxPersistable' (abstract class) to simplify polymorphism using QxOrm + library
    + - For more details about this new interface, goto the FAQ : 'How to use qx::IxPersistable interface + ?'
    + - New methods into 'qx::IxCollection' interface to iterate over each items without knowing its + type
    + - New option into 'QxOrm.pri' file to build QxOrm library statically (see '_QX_STATIC_BUILD' + option)
    + - New triggers : 'qx::dao::on_before_fetch' and 'qx::dao::on_after_fetch' (for more details, goto + the FAQ : 'How to define a Trigger with QxOrm ?')
    + - Add 'std::type_info' class information to introspection engine
    + - Some minor bugs fixed ('qx::dao::sql_error' exception message, SQL query column alias, mutex, + etc.)
    +
    + +

    Changes in version 1.2.2:

    +
    + - New module to provide a validation engine : QxValidator module
    + - For more details about QxValidator module, goto the FAQ of QxOrm library : 'How to use QxValidator + module to validate automatically an instance ?'
    + - Fix last insert ID with PostgreSQL using 'RETURNING' keyword : fetch inserted ID instead of + OID
    + - Improve SQL generator providing the good SQL type for all databases
    + - Add support for special database keywords using '[', ']' and '"' characters
    +
    + +

    Changes in version 1.2.1:

    +
    + - Improve 'qx::QxSqlQuery' class : new engine to build queries without writing SQL, for more + details, see the FAQ 'How to build a query without writing SQL with the class qx::QxSqlQuery ?'
    + - Improve 'qx::QxSession' class : provide persistent methods (CRUD) without using 'qx::dao::xxx' + functions, for more details, see the FAQ 'How to use a session (qx::QxSession class) to manage + automatically database transactions (using C++ RAII) ?'
    + - Implement 'repository' pattern to provide a common interface for persistent methods (CRUD) with 3 + new classes : 'qx::IxRepository', 'qx::QxRepository<T>' and 'qx::QxRepositoryX'
    + - Possibility to serialize a QVariant 'UserType' with serialization engine of QxOrm library
    + - Improve thread-safe 'qx::cache' : add insertion date-time into the cache to verify that an element + must be updated or not, for more details, see the FAQ 'How to use the cache (functions into + namespace qx::cache) of QxOrm library ?'
    + - FAQ updated on QxOrm website with now 28 questions and answers
    +
    + +

    Changes in version 1.1.9:

    +
    + - Possibility to register automatically Qt meta-properties (using Q_PROPERTY macro) to QxOrm context + without writing mapping function per class (void qx::register_class<T>())
    + - Strong integration with Qt introspection/moc engine : for more details about this new feature, + goto the FAQ 'How to register automatically Qt meta-properties to QxOrm context ?'
    + - Improve introspection/reflection engine : see the FAQ (How to use introspection engine (or + reflection engine) of QxOrm library ?) for more details
    + - Possibility to add meta-data (using a property bag) to introspection engine : see 'IxClass', + 'IxDataMember' and 'IxFunction' classes for more details
    + - Add function 'qx::QxClassX::dumpSqlSchema()' to explain how to create your own SQL schema based on + C++ classes
    + - New class 'qx::QxSimpleCrypt' to provide encryption/decryption (thanks very much to Andre Somers) + : so it's now possible to store encrypted data into database without using an external library
    + - QxService module : new feature to encrypt/decrypt data before transfering it over network
    +
    + +

    Changes in version 1.1.8:

    +
    + - QxOrm library can now be used on Mac (thanks very much to Dominique Billet) : see + 'osx_build_all_debug.sh' and 'osx_build_all_release.sh' scripts to build QxOrm library and all + samples in './test/' directory
    + - Add 'qx::QxSession' class : define a session to manage automatically database transactions (using + C++ RAII), see the FAQ for more details
    + - Add 'qx::QxDateNeutral', 'qx::QxTimeNeutral' and 'qx::QxDateTimeNeutral' classes : helper classes + to store date-time value into database under neutral format => cross database compatibility
    +
    + +

    Changes in version 1.1.7:

    +
    + - Add soft delete behavior : see the FAQ (How to define a soft delete behavior ?) for more details + about this new feature
    + - Add functions into namespace 'qx::dao' to update an element with a SQL condition : + update_by_query, update_optimized_by_query, etc.
    + - Fix a bug when QVariant type is used for a property of a persistent class : so, it's now possible + to insert NULL value into database
    +
    + +

    Changes in version 1.1.6:

    +
    + - QxOrm library online documentation available : 'https://www.qxorm.com/doxygen/index.html'
    + - Possibility to disable QtGui dependency using compilation option in 'QxConfig.h' file : + _QX_ENABLE_QT_GUI_DEPENDENCY
    + - Possibility to disable QtNetwork dependency (so QxService module too) using compilation option in + 'QxConfig.h' file : _QX_ENABLE_QT_NETWORK_DEPENDENCY
    + - Provide a new macro to register abstract class into QxOrm context : + QX_REGISTER_ABSTRACT_CLASS()
    +
    + +

    Changes in version 1.1.5:

    +
    + - New feature available : 'QxService' module to create C++ application server
    + - 'QxService' provides an easy and powerful way to create services and to transfer data over + network
    + - New tutorial available to explain how 'QxService' module works
    + - New sample available at './test/qxClientServer' directory
    + - QxOrm can be built with 'CONFIG += no_keywords' flag in '*.pro' files
    + - Bug fix with 'qx::dao::create_table<>' function and relation 'many-to-many'
    + - QxOrm should now build fine with GCC <= 4.2
    +
    + +

    Changes in version 1.1.4:

    +
    + - New parameter in functions 'qx::dao::fetch_by_id', 'qx::dao::fetch_all', 'qx::dao::fetch_by_query' + and 'qx::dao::update' to define a list of properties to fetch/update (by default, all properties are + fetched/updated)
    + - Support multi-columns primary key (composite key) : see sample './test/qxBlogCompositeKey/'
    + - Improve strategy of inheritance : QxOrm supports 'Concrete Table Inheritance' strategy ('Concrete + Table Inheritance' becomes default strategy)
    + - New smart-pointer 'qx::dao::ptr<T>' based on Qt 'QSharedPointer<T>' to provide 2 new + features : 'is dirty' and 'update optimized'
    + - 'qx::dao::ptr<T>' can be used with a simple object and with many containers (stl, boost, Qt + and 'qx::QxCollection' containers)
    + - 'qx::dao::ptr<T>' keeps original values from database and provides a 'isDirty()' method to + retrieve all properties changed
    + - 'qx::dao::update_optimized' must be used with 'qx::dao::ptr<T>' to save into database only + properties changed
    +
    + +

    Changes in version 1.1.3:

    +
    + - This version works fine with MinGW on Windows
    +
    + +

    Changes in version 1.1.2:

    +
    + - License LGPL
    + - Fix compilation problems on Linux and boost > 1.38
    + - Fix sql query with MySql database
    + - Disable assert when qx::dao functions return an error
    +
    + +

    Changes in version 1.1.1:

    +
    + - This version supports Visual Studio 2010
    +
    + +

    Changes in version 1.1.0:

    +
    + - First release
    +
    +

    +
    + QxEntityEditor application history : + + + + + + + +
    + +

    Changes in version QxEntityEditor 1.2.8:

    +
    + - New export plugin 'QxEESourceControlExport' : export a *.qxee QxEntityEditor project file to a + list of JSON files which can be easily check-in in a source control repository (Git, Perforce, CVS, + etc...)
    + - New import plugin 'QxEESourceControlImport' : import a list of JSON files (generated using + 'QxEESourceControlExport' export plugin) inside a *.qxee QxEntityEditor project file
    + - These 2 new plugins allow a team development to work simultaneously on a same *.qxee + QxEntityEditor project file
    + - A typical workflow could be (with 2 developers named dev A and dev B) :
    + * dev A and dev B work on a same *.qxee QxEntityEditor project file
    + * dev A create/modify/change some entities in QxEntityEditor application
    + * dev B create/modify/change other entities in QxEntityEditor application
    + * dev A and dev B export the *.qxee project file using the new 'QxEESourceControlExport' export + plugin
    + * Once exported, dev A and dev B check-in the JSON files inside the source control manager (Git, + Perforce, CVS, etc...) on their own branch
    + * dev A and dev B integrate from their own branch to the DEV (or MAIN or MASTER) branch (if there + are some conflicts to resolve, it should be easy because JSON files are easy to read and + understand)
    + * dev A and dev B can now get latest JSON files from the DEV (or MAIN or MASTER) branch : these JSON + files contain both modifications from dev A and dev B
    + * dev A and dev B can import these JSON files inside QxEntityEditor application using the new + 'QxEESourceControlImport' import plugin
    + - These 2 new plugins can be executed using command line (like all other QxEntityEditor plugins), + here are 2 examples :
    + * Export : QxEntityEditor --no_gui --project="C:\Temp\qxBlog.qxee" --plugin=QxEESourceControlExport + --QxEESourceControlExport_path="C:\Temp\source_control\"
    + * Import : QxEntityEditor --no_gui --project="C:\Temp\qxBlog.qxee" --plugin=QxEESourceControlImport + --QxEESourceControlImport_path="C:\Temp\source_control\qxBlog.qxee.export.json"
    +
    + +

    Changes in version QxEntityEditor 1.2.7:

    +
    + - Fix an issue with MySQL database schema import process when MySQL server is configured to answer + with column names in UPPERCASE (may fix other imports depending on specific database + configuration)
    +
    + +

    Changes in version QxEntityEditor 1.2.6:

    +
    + - Support PIMPL (private implementation or d-pointer) idiom in the C++ export plugin
    + - Documentation about PIMPL idiom for persistent classes : + https://www.qxorm.com/qxorm_en/manual_qxee.html#cpp_export_settings_parameters
    + - Fix import database schema for PostgreSQL version 12 and +
    + - Fix some assertions on macOS and linux versions due to libavoid library compiled without NDEBUG + compilation option (should improve performance too)
    + - Add CMake 'target_precompile_headers' command for generated CMakeLists.txt files to support + precompiled headers building with CMake (reduce compilation times)
    +
    + +

    Changes in version QxEntityEditor 1.2.5:

    +
    + - Fix an issue with fonts on macOS Catalina 10.15
    + - Add command line parameter --font : define application font with syntax + <family>||<pointSize>||<weight>||<italic> (only <family> is required), + for example : Courier New||14
    + - Add command line parameter --style_sheet : define application style sheet (more details here : + https://doc.qt.io/qt-5/stylesheet-reference.html), for example : QWidget { background-color: black + }
    + - Fix meta-data (property bag) C++ export : keep same order between several C++ exports (this is now + easier to check differences between 2 C++ exports using WinMerge or any other comparison/diff + tool)
    +
    + +

    Changes in version QxEntityEditor 1.2.4:

    +
    + - New feature in Javascript engine to customize exports : include QxRestApi module to query *.qxee + project file
    + - Fix a property/relation sorting issue in C++ export after importing a database schema (which could + be annoying to make some C++ files diffs between 2 exports)
    + - Fix a crash which could occurred sometimes importing a new table on an existing schema
    +
    + +

    Changes in version QxEntityEditor 1.2.3:

    +
    + - Fix a crash which appears sometimes with complex database schema to draw relationships (orthogonal + way)
    + - Improve QxEntityEditor command line parameters : possibility to import/export without using GUI + (useful to manage a Jenkins server for example)
    + - For more details about command line parameters, go to QxEntityEditor documentation : + https://www.qxorm.com/qxorm_en/manual_qxee.html#qxee_command_line
    +
    + +

    Changes in version QxEntityEditor 1.2.2:

    +
    + - Fix an issue after importing a project (from database or JSON file) which forced user to refresh + or reload current project
    + - Improve performance (fetching less data from database) when loading relationships from generated + persistent classes (bLoadFromDatabase parameter)
    + - Change model/view export plugin to take into account some improvments available in the new version + of QxOrm library
    + - Support changes of new QxOrm version : remove boost dependency by default and enable C++11 + features
    + - Improve Javascript engine to customize exports : add 3 functions available in js script to get all + QxEntityEditor settings (at global level, project level and plugin level)
    + - Check entities schema before exporting to C++ project : if a design error is detected, then + display a message to user
    + - Fix SQL type field in property settings : now you can put parenthesis (for example VARCHAR(255) is + now allowed)
    + - DDL export for MySQL (and MariaDB) : add ` character to escape column name and table name
    + - Add full path to project in recent projects list and main window title bar
    + - Now it is easier to select a relationship from entities diagram with a simple click on it
    +
    + +

    Changes in version QxEntityEditor 1.2.1:

    +
    + - Support CMake : each C++ project generated by QxEntityEditor (persistent classes, services, + model/view) provide a CMakeLists.txt file to build with CMake
    + - New online QxEntityEditor manual (user guide) available at : + https://www.qxorm.com/qxorm_en/manual_qxee.html
    + - Improve Javascript engine to customize export generated files : add a parameter named + 'output_location' to know where generated files are located + add functions to get a list of all + entities/enums of a project (see ./samples/custom_script.js file for more details)
    + - Improve database import process (SQLite, MySQL/MariaDB, PostgreSQL, Oracle and MSSQLServer) : fix + an issue importing composite foreign keys + foreign keys embedded in primary key ==> so it is now + possible to import more complex database schema
    + - Improve DDL database schema export process : fix an issue with 1-1 relationship + manage composite + keys
    + - New menu "Naming convention" to provide a fast way to rename all entities/properties/enums + (without breaking mapping to database) : support snake_case, camelCase and PascalCase (or upper + camel case), this new menu can be useful after an import from database process for example
    + - Each screen of QxEntityEditor provides now a fast access to the online manual (user guide) in its + associated topic (new button "Documentation" + shortcut pressing F1 key everywhere)
    + - New buttons undo/redo in the main QxEntityEditor toolbar to undo or redo actions done on entities, + enumerations, comments, layout (undo/redo feature can be disabled to improve performance on large + projects)
    + - Support _QX_UNITY_BUILD compilation option to reduce compilation times of generated C++ projects + (recommended with CMake which doesn't support natively precompiled headers)
    + - Fix an issue importing a relationship where target entity doesn't have a primary key
    + - Fix an issue with C++ services export plugin and JSON serialization : "Unable to create nude + pointer for input parameter"
    + - Fix an issue with abstract entities and C++ model/view export plugin and C++ services export + plugin
    + - Possibility to put hexadecimal values to define an enumeration
    + - New Javascript sample file in directory ./samples/ named q_property.js to show how to add + automatically Q_PROPERTY definition for each property generated by QxEntityEditor
    +
    + +

    Changes in version QxEntityEditor 1.1.9:

    +
    + - New import plugin to connect to MySQL or MariaDB database without having to deal with ODBC + settings
    + - New import plugin to connect to PostgreSQL database without having to deal with ODBC settings
    + - New import plugin to load a QxEntityEditor project from a JSON file
    + - Fix SQLite import process to manage composite keys
    + - Improve Javascript engine to customize export process : new types available by script to manage + files and directories + possibility to get/set environment variables (read custom_script.js file in + ./samples/ directory for script examples)
    + - Improve Javascript engine to customize export process : new events/actions available by script : + PLUGIN_EXPORT_START, PLUGIN_EXPORT_END, AFTER_CLOSING_FILE
    + - New message box displayed if directory doesn't exist when starting an export process (to create it + automatically)
    + - Fix an issue with the "Tag project state" function (project historic) : each tag double the size + of a *.qxee project file (now each tag is optimized)
    + - Fix C++ services export process when C++11 features are enabled in QxOrm.pri configuration + file
    + - Possibility to export a *.qxee project file to a JSON text file (can be useful to store a text + representation in a source control, like Perforce, VSS, CVS, Git, etc...)
    + - Possibility to use an environment variable to define QxOrm library location (using the same syntax + as qmake, for example $$(QXORM_DIR))
    +
    + +

    Changes in version QxEntityEditor 1.1.8:

    +
    + - Improve import plugins : reduce import process time : now, you can import hundred of entities in + few seconds into a QxEntityEditor project
    + - Relationship n-1 : possibility to define a database column name different than the relationship + name
    + - New file qxBlogExec.zip in the ./samples/ directory of QxEntityEditor package : this is a C++/Qt + example project which depends on the qxBlog.qxee generated files
    + - Improve import by ODBC plugin screen : new schema/namespace level in the list of tables/views + treeview
    + - Fix a performance issue to load large diagram with new style to draw relationships : you can now + load quickly a project with hundred of entities
    + - Add new C++11 types to manage relationships, decoration, collection (std::shared_ptr, + std::unordered_map and std::unordered_set) : C++11 features must be enabled in QxOrm.pri config file + to use these classes
    + - Import process more permissive : possibility to import tables without primary key and tables + without column
    +
    + +

    Changes in version QxEntityEditor 1.1.7:

    +
    + - New Navigator window (under project treeview) : useful to navigate over large diagram
    + - New way to draw relationships (orthogonal lines between 2 entities) + display the relationship + type on each side (there is an option to use the old drawing style from previous version)
    + - Possibility to define a background color by namespace : useful to group all entities in the + diagram associated to a same namespace
    + - New feature to customize entities/enumerations/notes colors at several levels
    + - Define items colors at project level (menu Tools >> Project settings >> Colors + tab)
    + - Define items colors at namespace level (right-click on the diagram >> Define colors by + namespace)
    + - Define colors at item level : right-click on an item (entity, enumeration or comment) >> + Define item colors
    + - New action to organize automatically the diagram layout, useful after an import process for + example (menu View >> Organize diagram layout)
    + - Improve the DDL SQL export plugin : new option to export relationships as foreign keys constraints + in database
    + - Support the new compilation option _QX_NO_PRECOMPILED_HEADER of QxOrm library (workaround for a + known bug of recent versions of MinGW on Windows and large precompiled header)
    + - Import database by ODBC plugin : fix the import from MS SQL Server database when tables are not + located in the default schema (dbo)
    + - QxEntityEditor Mac OS X version : fix an issue to load the QxEEPrinter plugin
    + - Export plugin to C++ model/view project : new option to generate models based on the new QxOrm + library class qx::QxModelService<T, S> (models based on services to execute client-server + requests)
    +
    + +

    Changes in version QxEntityEditor 1.1.6:

    +
    + - New javascript engine to customize the C++ and DDL SQL export process writing your own custom + script
    + - Integrated debugger to debug your own custom javascript files (for example, putting a breakpoint + or logging some traces)
    + - New plugin to print the entities diagram as a PNG image file and PDF file
    + - New property parameter UNIQUE (in property params window) used by the DDL SQL generator to create + the database schema
    + - Improve nested models in QxModelView module to be able to use several relationships levels in + QML
    + - Possibility to change the order in the list of properties and list of relationships of an + entity
    + - Change encoding of generated files : now files are UTF-8
    + - Possibility to put a relative path to the QxEntityEditor project file (*.qxee file) in each plugin + location settings
    +
    + +

    Changes in version QxEntityEditor 1.1.5:

    +
    + - New C++ model/view export plugin to manage complex data structure to work with relationships in + QML views and Qt model/view architecture (using QxModelView module of QxOrm library)
    + - Now, with this new export plugin, working on QML with C++ entities has never been so easy !
    + - New function, menu "Tools >> Plugins scripts", to define your own custom scripts + before/after a plugin execution
    + - New command line parameter --log_sql to trace all SQL queries executed by QxEntityEditor : a + QxEntityEditor project file (*.qxee) is just a SQLite database, so it's easy to write your own + script to customize some default behaviour
    + - New option in the C++ export plugin to generate or not the custom directory with all custom files + for each entity
    + - Scrollbar available in the entities viewer
    +
    + +

    Changes in version QxEntityEditor 1.1.4:

    +
    + - Improve import by ODBC plugin to manage relationship, schema and composite key for SQLite, MySQL, + PostgreSQL, Oracle and MS SQL Server databases
    + - New import from SQLite plugin to import SQLite database structure into QxEntityEditor project + without having to create an ODBC DSN connexion
    + - Improve C++ export plugin : add a set of useful methods in generated classes + option to manage + relative path to QxOrm library
    + - New menu to rename a namespace (move all entities to another namespace) and delete a list of + entities by namespace
    + - Fix a bug when executing QxEntityEditor in command line (no GUI)
    + - Add a viewer mode to open a QxEntityEditor project with unlimited entities count without having a + license key (read-only mode)
    +
    + +

    Changes in version QxEntityEditor 1.1.3 (first official release):

    +
    + - New plugin to transfer your data model over network and create quickly client/server applications, + using QxService module
    + - New options to define entity, enum and comment width in entities viewer
    +
    + +

    Changes in version QxEntityEditor 1.1.2 (BETA):

    +
    + - New plugin to generate DDL SQL script (database schema) for SQLite, MySQL, PostgreSQL, Oracle and + MS SQL Server databases
    + - The new DDL SQL plugin manages automatically schema evolution for each project version (ALTER + TABLE, ADD COLUMN, DROP INDEX, etc.)
    + - Add post-it or comments in the entities viewer
    + - Option to display or not property type in the entities viewer
    +
    + +

    Changes in version QxEntityEditor 1.1.1 (BETA):

    +
    + - First BETA version of QxEntityEditor application
    + - Provide a graphic way to manage the data model for QxOrm library
    + - Multi-platform (available for Windows, Linux and Mac OS X) and generate native code for all + environments : desktop (Windows, Linux, Mac OS X), embedded and mobile (Android, iOS, Windows Phone, + Raspberry Pi, etc.)
    + - Based on plugins and provides many ways to import/export the data model
    + - Generate C++ persistent classes automatically (registered in QxOrm context)
    +
    +

    +
    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/doc/qxorm_en/download_details.php b/doc/qxorm_en/download_details.php new file mode 100644 index 0000000..9ef1611 --- /dev/null +++ b/doc/qxorm_en/download_details.php @@ -0,0 +1,161 @@ + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic editor (data model designer and source code generator) + + + + + + + + + + + + + + + + +
    QxOrm + Windows + Linux + Macintosh + C++
    +
    + + + + + + + + + + + + + +
    HomeDownloadQuick sample + Tutorial (4) +
    + +
    +
    + Manual (2) +
    + +
    +
    ForumOur customers
    +
    + + + + + + + + + + +
    QxOrm >> Download details + + + + + + + + + +
    Current version : QxOrm 1.5.0 - QxOrm library online class documentation - GitHub
    QxEntityEditor 1.2.8
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    + + + + + + + + +
    QxOrm library is available with full source code and can be used free of charge under the terms of the GNU General Public License (GPL) version 3. +

    + From Wikipedia : The GNU General Public License (GNU GPL or GPL) is the most widely used free software license, which guarantees end users (individuals, organizations, companies) the freedoms to use, study, share (copy), and modify the software. Software that ensures that these rights are retained is called free software. The GPL grants the recipients of a computer program the rights of the Free Software Definition and uses copyleft to ensure the freedoms are preserved whenever the work is distributed, even when the work is changed or added to. The GPL is a copyleft license, which means that derived works can only be distributed under the same license terms. +

    + If the GPLv3 license is too restrictive for your project, you can purchase the QxOrm Proprietary License (QXPL). Additional benefits of the QxOrm Proprietary License include : +
      +
    • Application source code stays private ;
    • +
    • High compatibility with different commercial and open-source licenses ;
    • +
    • Possibility to use QxOrm without delivering the full source of the library code to end users ;
    • +
    • Possibility to keep own modifications to QxOrm private ;
    • +
    • Possibility to create products without mentioning QxOrm to the end users ;
    • +
    • Protection against reverse engineering of the product.
    • +
    +
    qt_ambassador
    + QxOrm library has been accepted into the Qt Ambassador Program +
    + The QxOrm QXPL license is a per project (or per application) license, no matter developers count working on the project, and no matter deployed instances count of your software (royalty-free). + For example, if you develop 3 differents applications based on QxOrm library, then you have to purchase 3 differents QXPL licenses (one per project).
    +
    + Price of a QxOrm Proprietary License : 400€.
    +
    + If you have any questions, or if you want to purchase the QXPL license, please contact us using this e-mail : ic-east.com.
    +
    + Download QxOrm 1.5.0 +
    + +
    +
    +

    + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + diff --git a/doc/qxorm_en/faq.html b/doc/qxorm_en/faq.html new file mode 100644 index 0000000..934ece5 --- /dev/null +++ b/doc/qxorm_en/faq.html @@ -0,0 +1,3606 @@ + + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic editor + (data model designer and source code generator) + + + + + + + + + + + + + + + + + + + + + +
    QxOrm + + + + Windows + Linux + Macintosh + C++
    +
    + + + + + + + + + + + + + + + + + + +
    HomeDownloadQuick sample + Tutorial (4) + + + + + + +
    + +
    +
    + Manual (2) + + + + + + +
    + +
    +
    ForumOur customers
    +
    + + + + + + + + + + + + + + + + + +
    + QxOrm >> Faq + + + + + + + + + + + + + + +
    + Current version :  + + QxOrm 1.5.0 - QxOrm library online class documentation - GitHub +
    + + + QxEntityEditor 1.2.8 +
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    + + + + + + + + + +
    + + qt_ambassador
    + + QxOrm library has been accepted into the Qt Ambassador + Program + +
    +
    + What is QxOrm ?

    + + + + + + + + + +
    + QxOrm is a C++ library designed to provide Object Relational Mapping (ORM) feature to C++ + users.
    + QxOrm is developed by XDL Team, a software development engineer since 2003.
    +
    + QxOrm provides many functionalities starting from a simple C++ setting function by class + : +
      +
    • + persistence + : communication with databases (support 1-1, 1-n, n-1 and n-n + relationships) +
    • +
    • + serialization + : binary, XML and JSON format +
    • +
    • + reflection + (or + introspection + ) : access to classes definitions, retrieve properties and call classes methods +
    • +
    +
    +
    + What is QxEntityEditor ?

    + + + + + + + + + +
    + QxEntityEditor is a graphic editor for QxOrm library : QxEntityEditor provides a + graphic way to manage the data model.
    + QxEntityEditor is multi-platform (available for Windows, Linux and Mac OS X) and generates + native code for all environments : desktop (Windows, Linux, Mac OS X), embedded and mobile (Android, + iOS, Windows Phone, Raspberry Pi, etc.).
    + A presentation video of QxEntityEditor application is + available.
    +
    + QxEntityEditor is based on plugins and provides many ways to import/export your data model : +
      +
    • generate C++ persistent classes automatically (registered in QxOrm context) ;
    • +
    • generate DDL SQL script automatically (database schema) for SQLite, MySQL, PostgreSQL, Oracle + and MS SQL Server ;
    • +
    • manage schema evolution for each project version (ALTER TABLE, ADD COLUMN, DROP + INDEX, etc.) ;
    • +
    • transfer your data model over network and create quickly client/server applications, using QxService module ;
    • +
    • import existing database structure (using ODBC connection) for SQLite, MySQL, PostgreSQL, Oracle + and MS SQL Server databases ;
    • +
    • because each project is different, QxEntityEditor provides several ways to customize generated + files (especially a javascript engine and an integrated debugger).
    • +
    + QxEntityEditor +

    + QxEntityEditor is developed by XDL Team, a software development engineer since 2003.
    +

    +
    +
    + How to contact QxOrm to report a bug or ask a question ?

    + + + + + + + + + +
    + If you find a bug or if you have a question about QxOrm library, you can send an e-mail to : + support@qxorm.com.
    + A forum dedicated to QxOrm is available here. +
    +

    + How to install and build QxOrm library ?

    + + + + + + + + + +
    + A tutorial to install a development environment with QxOrm library on + Windows is available.
    +
    + QxOrm uses qmake process from Qt library to create makefile and build the + project.
    + qmake is portable and multi-platform, so it works perfectly on Windows, Linux (Unix) and + Mac.
    + To build QxOrm library, just execute following commands :
    + qmake
    + make debug
    + make release
    +
    + On Windows, *.vcproj and *.sln files are available for Visual C++ 2008, + Visual C++ 2010 and Visual C++ 2012.
    + *.pro files are readable by Qt Creator, and some plugins are available to interface to + other C++ IDE.
    + mingw_build_all_debug.bat and mingw_build_all_release.bat scripts in the directory + ./tools/ can quickly built QxOrm library and all tests with MinGW compiler on + Windows.
    + gcc_build_all_debug.sh and gcc_build_all_release.sh scripts in the directory + ./tools/ can quickly built QxOrm library and all tests with GCC compiler on + Linux.
    + osx_build_all_debug.sh and osx_build_all_release.sh scripts in the directory + ./tools/ can quickly built QxOrm library and all tests on Mac (thanks very much to + Dominique Billet).
    +
    + Note : depending on your development environment, it may be necessary to modify + QxOrm.pri file to set boost package configuration :
    + QX_BOOST_INCLUDE_PATH = $$quote(D:/Dvlp/_Libs/Boost/1_42/include)
    + QX_BOOST_LIB_PATH = $$quote(D:/Dvlp/_Libs/Boost/1_42/lib_shared)
    + QX_BOOST_LIB_SERIALIZATION_DEBUG = "boost_serialization-vc90-mt-gd-1_42"
    + QX_BOOST_LIB_SERIALIZATION_RELEASE = "boost_serialization-vc90-mt-1_42"
    +
    +

    + What are the databases supported by QxOrm ?

    + + + + + + + + + +
    + QxOrm uses the engine QtSql of Qt based on a system of plug-in.
    + A detailed list of supported databases is available on the website of Qt here.
    The plug-in ODBC + (QODBC) ensures compatibility with many databases.
    For optimal performances, it is possible + to use a plug-in specific to a database : +
      +
    • QMYSQL : MySQL +
    • +
    • QPSQL : PostgreSQL (versions 7.3 and above) +
    • +
    • QOCI : Oracle Call Interfaces Driver +
    • +
    • QSQLITE : SQLite version 3 +
    • +
    • QDB2 : IBM DB2 (version 7.1 and above) +
    • +
    • QIBASE : Borland InterBase +
    • +
    • QTDS : Sybase Adaptive Server +
    • +
    +
    +
    + Why QxOrm is dependent on two libraries : boost and Qt ?

    + + + + + + + + + +
    + QxOrm uses many functionalities available in excellent libraries : boost and + Qt.
    + In addition, these two libraries are used in many projects both professional and open source.
    + A large number of forums, tutorials, and a whole community are available to answer any issue that + could arise.
    + The QxOrm objective is not to redevelop features that already exist but to provide a powerful + tool for access to databases such as it exists in other languages (Java with Hibernate, + .Net with NHibernate, Ruby, Python, etc...).

    + + + + + + + + + +
    QtQt : cross-platform application development framework : GUI + (QtGui), network (QtNetwork), XML (QtXml), database (QtSql)...
    + Qt provides excellent support and documentation. Using Qt, you can write simple and powerful + C++ code.
    + Qt is produced by Digia's Qt Development Frameworks division and is available under LGPL + license.
    + QxOrm is compatible with many Qt's objects : QObject, QString, QDate, QTime, QDateTime, + QList, QHash, QSharedPointer, QScopedPointer...
    + It is recommended to install the latest version of Qt available at the following address : http://www.qt.io/
    +

    + + + + + + + + + +
    boostboost : many of boost's founders are on the C++ standard committee + and several boost libraries have been accepted for incorporation into C++1x (new standard for + the C++ programming language). + The boost's libraries are aimed at a wide range of C++ users and application domains.
    + QxOrm uses the following boost's features (header files *.hpp only, boost + serialization dependency is optional) : smart_pointer, type_traits, + multi_index_container, unordered_container, any, tuple, foreach, function.
    + It is recommended to get the latest version of boost available at the following address : http://www.boost.org/ +
    +
    +

    + Why does QxOrm require a precompiled header to be used ?

    + + + + + + + + + +
    + QxOrm uses the techniques of C++ meta-programming to provide most of its + functionalities.
    + You do not need to know how to use meta-programming to work with QxOrm library.
    + Indeed, QxOrm is simple to use and the C++ code written with Qt and QxOrm is easy to read, therefore + easy to develop and to maintain.

    + However, meta-programming is costly in compilation times.
    + By using a precompiled.h file, your project will be compiled much more quickly.
    + Last but not least, another advantage is that the file QxOrm.h includes the basic + functionalities of libraries boost and Qt.
    + It is thus not necessary anymore to write #include <QtCore/QString.h> to use the class + QString of Qt for example.
    + In the same way, there is no need anymore to write #include <boost/shared_ptr.hpp> to use + smart pointers of boost library. +
    +

    + Is it possible to reduce compilation times of my project ?

    + + + + + + + + + +
    + Yes, if the serialization of your data in XML format is not used in your project, you + can disable this functionality.
    + The compilation times will be then reduced but you will not have anymore access to the namespace + qx::serialization:xml.
    + To disable XML serialization, it is necessary to open the QxOrm.pri config file and to + remove (or comment) the compilation option _QX_ENABLE_BOOST_SERIALIZATION_XML.
    + A recompilation of QxOrm library is necessary to take into account this modification.
    +
    + Another possibility is to use the polymorphic classes of the library boost::serialization (instead of template).
    + This feature reduces compilation times and the size of the executable that is generated.
    + However, the speed of execution of your program will be reduced since part of the work carried out + during compilation will be done during the execution of your application.
    + To use this feature with QxOrm, you must enable the compilation option + _QX_ENABLE_BOOST_SERIALIZATION_POLYMORPHIC in the QxOrm.pri config file.
    + Warning : the serialization functions will be then accessible from the following + namespace : qx::serialization::polymorphic_binary, + qx::serialization::polymorphic_text and qx::serialization::polymorphic_xml.
    + A recompilation of QxOrm library is necessary to take into account this modification.
    +
    + It is also possible to use Q_PROPERTY macro to define properties for a class inherited from + QObject type.
    + In this case, there is two different ways to register properties in QxOrm context and you can reduce + noticeably compilation times of your project.
    + For more details about this feature, click here.
    +
    + Note : it is important to check if all optimizations provided by the compiler are enabled, for + example to compile using several processors : +
      +
    • MSVC++ : use environment variable SET CL=/MP
    • +
    • GCC and Clang : put processors count as parameter to the make process, for + example to use 8 processors : SET MAKE_COMMAND=make -j8
    • +
    +
    +

    + What are all types of serialization available ?

    + + + + + + + + + +
    + QxOrm is based on boost + serialization library.
    + There are several types of serialization available : binary, XML, text, etc...
    + QxOrm.pri config file can enable and/or disable some types of serialization.
    +
    + Each type of serialization has its own characteristics :
    + * binary : smallest, fastest, non-portable
    + * text : larger, slower, portable
    + * XML : largest, slowest, portable
    +
    + Note : binary type is not portable, so you can't transfer data between Windows and Unix + for example.
    + If you need to transfer data over network between different platforms, you have to use text or + XML serialization.
    + QxOrm provides another solution : portable_binary serialization.
    + portable_binary has the same characteristics as binary type and can serialize data in a + portable way.
    + However, portable_binary is not provided officially by boost library, so it's necessary + to test before using in a production software. +
    +

    + Why does QxOrm provide a new type of container qx::QxCollection<Key, + Value> ?

    + + + + + + + + + +
    + There are many containers in stl, boost and Qt libraries.
    + It is therefore legitimate to ask this question : what is qx::QxCollection<Key, Value> + ?
    + qx::QxCollection<Key, + Value> is a new container (based on the excellent library boost::multi_index_container) which has the following functionalities : +
      +
    • preserves the insertion order of elements in the list +
    • +
    • quick access to an element by its index : is equivalent to std::vector<T> or + QList<T> for example +
    • +
    • quick access to an element by a key (hash-map) : is equivalent to QHash<Key, + Value> or boost::unordered_map<Key, Value> for example +
    • +
    • sort by Key type and by Value type
    • +
    + Note : qx::QxCollection<Key, Value> is compatible with the foreach macro + provided by Qt library and the BOOST_FOREACH macro provided by boost library.
    + However, each element returned by these 2 macros corresponds to an object of type std::pair<Key, + Value>.
    + To obtain a more natural and more readable result, it is advised to use the _foreach macro : + this macro uses BOOST_FOREACH for all the containers except for qx::QxCollection<Key, + Value>.
    + In this case, the returned element corresponds to the Value type (cf. sample).
    + The macro _foreach is compatible with all containers (stl, Qt, + boost...) since it uses the macro BOOST_FOREACH.

    + + Additional note : qx::QxCollection<Key, Value> is particularly suited to receive + data resulting from a database.
    Indeed, these data can be sorted (by using ORDER BY in a + sql request for example), it is thus important to preserve the insertion order of the elements in the + list.
    + Furthermore, each data resulting from a database has a unique id. It is thus important to be able to + access quickly to an element based on this single identifier (hash-map).

    + + Sample :
    + + + + + + + +
    +
    /* definition of drug class with 3 properties : code, name, description */
    +class drug { public: QString code; QString name; QString desc; };
    +
    +/* smart pointer of drug */
    +typedef boost::shared_ptr<drug> drug_ptr;
    +
    +/* collection of drugs by code */
    +qx::QxCollection<QString, drug_ptr> lstDrugs;
    +
    +/* create 3 new drugs */
    +drug_ptr d1; d1.reset(new drug()); d1->code = "code1"; d1->name = "name1"; d1->desc = "desc1";
    +drug_ptr d2; d2.reset(new drug()); d2->code = "code2"; d2->name = "name2"; d2->desc = "desc2";
    +drug_ptr d3; d3.reset(new drug()); d3->code = "code3"; d3->name = "name3"; d3->desc = "desc3";
    +
    +/* insert drugs into the collection */
    +lstDrugs.insert(d1->code, d1);
    +lstDrugs.insert(d2->code, d2);
    +lstDrugs.insert(d3->code, d3);
    +
    +/* iterate with '_foreach' keyword */
    +_foreach(drug_ptr p, lstDrugs)
    +{ qDebug() << qPrintable(p->name) << " " << qPrintable(p->desc); }
    +
    +/* iterate with 'for' keyword */
    +for (long l = 0; l < lstDrugs.count(); ++l)
    +{
    +   drug_ptr p = lstDrugs.getByIndex(l);
    +   QString code = lstDrugs.getKeyByIndex(l);
    +   qDebug() << qPrintable(p->name) << " " << qPrintable(p->desc);
    +}
    +
    +/* iterate with 'QxCollectionIterator' java style */
    +qx::QxCollectionIterator<QString, drug_ptr> itr(lstDrugs);
    +while (itr.next())
    +{
    +   QString code = itr.key();
    +   qDebug() << qPrintable(itr.value()->name) << " " << qPrintable(itr.value()->desc);
    +}
    +
    +/* sort ascending by key and sort descending by value */
    +lstDrugs.sortByKey(true);
    +lstDrugs.sortByValue(false);
    +
    +/* access drug by code */
    +drug_ptr p = lstDrugs.getByKey("code2");
    +
    +/* access drug by index */
    +drug_ptr p = lstDrugs.getByIndex(2);
    +
    +/* test if drug exists and if collection is empty */
    +bool bExist = lstDrugs.exist("code3");
    +bool bEmpty = lstDrugs.empty();
    +
    +/* remove the second drug from collection */
    +lstDrugs.removeByIndex(2);
    +
    +/* remove the drug with "code3" */
    +lstDrugs.removeByKey("code3");
    +
    +/* clear the collection */
    +lstDrugs.clear();
    +
    +
    +

    + Why does QxOrm provide a new smart-pointer qx::dao::ptr<T> + ?

    + + + + + + + + + +
    + QxOrm can be used with smart-pointers of boost and Qt libraries.
    + QxOrm smart-pointer is based on QSharedPointer and provides new features with + 'qx::dao::...' functions.
    + qx::dao::ptr<T> + keeps automatically values from database.
    + So it's possible to detect if an instance has been modified using the method 'isDirty()' : this + method can return list of properties changed.
    + qx::dao::ptr<T> can also be used with the function 'qx::dao::update_optimized()' + to update in database only properties changed.
    + qx::dao::ptr<T> can be used with a simple object and with many containers : stl, + boost, Qt and qx::QxCollection<Key, Value>.
    +
    + Sample :
    + + + + + + + +
    +
       // Test 'isDirty()' method
    +   qx::dao::ptr<blog> blog_isdirty = qx::dao::ptr<blog>(new blog());
    +   blog_isdirty->m_id = blog_1->m_id;
    +   daoError = qx::dao::fetch_by_id(blog_isdirty);
    +   qAssert(! daoError.isValid() && ! blog_isdirty.isDirty());
    +
    +   blog_isdirty->m_text = "blog property 'text' modified => blog is dirty !!!";
    +   QStringList lstDiff; bool bDirty = blog_isdirty.isDirty(lstDiff);
    +   qAssert(bDirty && (lstDiff.count() == 1) && (lstDiff.at(0) == "blog_text"));
    +   if (bDirty) { qDebug("[QxOrm] test dirty 1 : blog is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
    +
    +   // Update only property 'm_text' of 'blog_isdirty'
    +   daoError = qx::dao::update_optimized(blog_isdirty);
    +   qAssert(! daoError.isValid() && ! blog_isdirty.isDirty());
    +   qx::dump(blog_isdirty);
    +
    +   // Test 'isDirty()' method with a container
    +   typedef qx::dao::ptr< QList<author_ptr> > type_lst_author_test_is_dirty;
    +
    +   type_lst_author_test_is_dirty container_isdirty = type_lst_author_test_is_dirty(new QList<author_ptr>());
    +   daoError = qx::dao::fetch_all(container_isdirty);
    +   qAssert(! daoError.isValid() && ! container_isdirty.isDirty() && (container_isdirty->count() == 3));
    +
    +   author_ptr author_ptr_dirty = container_isdirty->at(1);
    +   author_ptr_dirty->m_name = "author name modified at index 1 => container is dirty !!!";
    +   bDirty = container_isdirty.isDirty(lstDiff);
    +   qAssert(bDirty && (lstDiff.count() == 1));
    +   if (bDirty) { qDebug("[QxOrm] test dirty 2 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
    +
    +   author_ptr_dirty = container_isdirty->at(2);
    +   author_ptr_dirty->m_birthdate = QDate(1998, 03, 06);
    +   bDirty = container_isdirty.isDirty(lstDiff);
    +   qAssert(bDirty && (lstDiff.count() == 2));
    +   if (bDirty) { qDebug("[QxOrm] test dirty 3 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
    +
    +   // Update only property 'm_name' at position 1, only property 'm_birthdate' at position 2 and nothing at position 0
    +   daoError = qx::dao::update_optimized(container_isdirty);
    +   qAssert(! daoError.isValid() && ! container_isdirty.isDirty());
    +   qx::dump(container_isdirty);
    +
    +   // Fetch only property 'm_dt_creation' of blog
    +   QStringList lstColumns = QStringList() << "date_creation";
    +   list_blog lst_blog_with_only_date_creation;
    +   daoError = qx::dao::fetch_all(lst_blog_with_only_date_creation, NULL, lstColumns);
    +   qAssert(! daoError.isValid() && (lst_blog_with_only_date_creation.size() > 0));
    +
    +   if ((lst_blog_with_only_date_creation.size() > 0) && (lst_blog_with_only_date_creation[0] != NULL))
    +   { qAssert(lst_blog_with_only_date_creation[0]->m_text.isEmpty()); }
    +
    +   qx::dump(lst_blog_with_only_date_creation);
    +
    +
    +

    + Should I use QString or std::string ?

    + + + + + + + + + +
    + QxOrm advises to use the QString class for the management of the character strings.
    + Even if boost provides many functionalities with its module boost::string_algo, the QString class is easier to use and + supports many formats : ASCII, Utf8, Utf16...
    + However, QxOrm is compatible with std::string and std::wstring if you prefer to + use this kind of character strings. +
    +

    + Is it necessary to use smart-pointers ?

    + + + + + + + + + +
    + QxOrm strongly advises to use boost or Qt smart-pointers.
    + The C++ language does not have Garbage Collector like Java or C# for example.
    + The use of smart-pointers simplifies the memory management in C++.
    + The ideal in a C++ program is not to have any call to delete or delete[].
    + Furthermore, smart-pointer is a new functionality of the new C++ standard : C++1x.
    + It is thus essential to know the following classes today : + +
    +

    + The primary key is long type by default. Is it possible to use a key of + QString type or other ?

    + + + + + + + + + +
    + It is possible to define a unique id of QString type or other with QxOrm library.
    + By default, the unique id is long type.
    + To indicate that a class has a single identifier of QString type or other, it is necessary to + specialize the template qx::trait::get_primary_key.
    + To simplify, you can use the macro : QX_REGISTER_PRIMARY_KEY(myClass, QString).
    +
    + Warning : the macro QX_REGISTER_PRIMARY_KEY must be used before the macro + QX_REGISTER_HPP_... in the definition of your class, otherwise a compilation error + occurs.
    +
    + Here is an example with author class of qxBlog tutorial and a QString primary key + :
    +
    + + + + + + + +
    +
    #ifndef _QX_BLOG_AUTHOR_H_
    +#define _QX_BLOG_AUTHOR_H_
    + 
    +class author
    +{
    +public:
    +// -- properties
    +   QString  m_id;
    +   QString  m_name;
    +// -- constructor, virtual destructor
    +   author() { ; }
    +   virtual ~author() { ; }
    +};
    +
    +QX_REGISTER_PRIMARY_KEY(author, QString)
    +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0)
    +
    +#endif // _QX_BLOG_AUTHOR_H_
    +
    +
    +

    + How to define a 'multi-columns primary key' (composite key) + ?

    + + + + + + + + + +
    + QxOrm supports 'multi-columns primary key'.
    + The class id must be defined with following type :
    + * QPair or std::pair to define 2 columns
    + * boost::tuple to define from 2 columns to 9 columns
    +
    + It is necessary to use the macro QX_REGISTER_PRIMARY_KEY() to specialize the template + and to map class id with multi-columns in database.
    + The list of multi-columns names must be defined with '|' character : + 'column1|column2|column3|etc...'.
    +
    + Sample with class 'author' from project 'qxBlogCompositeKey', this class has an + id mapped to 3 columns in database :
    +
    + + + + + + + +
    +
    #ifndef _QX_BLOG_AUTHOR_H_
    +#define _QX_BLOG_AUTHOR_H_
    +
    +class blog;
    +
    +class QX_BLOG_DLL_EXPORT author
    +{
    +
    +   QX_REGISTER_FRIEND_CLASS(author)
    +
    +public:
    +
    +// -- composite key (multi-column primary key in database)
    +   typedef boost::tuple<QString, long, QString> type_composite_key;
    +   static QString str_composite_key() { return "author_id_0|author_id_1|author_id_2"; }
    +
    +// -- typedef
    +   typedef boost::shared_ptr<blog> blog_ptr;
    +   typedef std::vector<blog_ptr> list_blog;
    +
    +// -- enum
    +   enum enum_sex { male, female, unknown };
    +
    +// -- properties
    +   type_composite_key   m_id;
    +   QString              m_name;
    +   QDate                m_birthdate;
    +   enum_sex             m_sex;
    +   list_blog            m_blogX;
    +
    +// -- contructor, virtual destructor
    +   author() : m_id("", 0, ""), m_sex(unknown) { ; }
    +   virtual ~author() { ; }
    +
    +// -- methods
    +   int age() const;
    +
    +// -- methods "get" to composite key
    +   type_composite_key getId() const    { return m_id; }
    +   QString getId_0() const             { return boost::tuples::get<0>(m_id); }
    +   long getId_1() const                { return boost::tuples::get<1>(m_id); }
    +   QString getId_2() const             { return boost::tuples::get<2>(m_id); }
    +
    +// -- methods "set" to composite key
    +   void setId_0(const QString & s)     { boost::tuples::get<0>(m_id) = s; }
    +   void setId_1(long l)                { boost::tuples::get<1>(m_id) = l; }
    +   void setId_2(const QString & s)     { boost::tuples::get<2>(m_id) = s; }
    +
    +};
    +
    +QX_REGISTER_PRIMARY_KEY(author, author::type_composite_key)
    +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0)
    +
    +typedef boost::shared_ptr<author> author_ptr;
    +typedef qx::QxCollection<author::type_composite_key, author_ptr> list_author;
    +
    +#endif // _QX_BLOG_AUTHOR_H_
    +
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +#include "../include/author.h"
    +#include "../include/blog.h"
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_BLOG(author)
    +
    +namespace qx {
    +template <> void register_class(QxClass<author> & t)
    +{
    +   t.id(& author::m_id, author::str_composite_key());
    +
    +   t.data(& author::m_name, "name");
    +   t.data(& author::m_birthdate, "birthdate");
    +   t.data(& author::m_sex, "sex");
    +
    +   t.relationOneToMany(& author::m_blogX, blog::str_composite_key(), author::str_composite_key());
    +
    +   t.fct_0<int>(& author::age, "age");
    +}}
    +
    +int author::age() const
    +{
    +   if (! m_birthdate.isValid()) { return -1; }
    +   return (QDate::currentDate().year() - m_birthdate.year());
    +}
    +
    +
    +
    +

    + How to register private or protected members into QxOrm context + ?

    + + + + + + + + + +
    + To register private or protected members into QxOrm context + (qx::register_class<T> function), it's necessary to declare some friend class.
    + To simplify writing C++ template, QxOrm library provides this macro : + QX_REGISTER_FRIEND_CLASS(myClass).
    + An example using this macro can be found in ./test/qxDllSample/dll1/ directory of QxOrm package + with CPerson class :
    +
    + + + + + + + +
    +
    namespace qx {
    +namespace test {
    +
    +class QX_DLL1_EXPORT CPerson : public QObject
    +{
    +
    +   Q_OBJECT
    +   QX_REGISTER_FRIEND_CLASS(qx::test::CPerson)
    +
    +   // etc...
    +
    +};
    +
    +} // namespace test
    +} // namespace qx
    +
    +
    +
    +

    + How to enable/disable the module QxMemLeak for automatic detection of memory + leaks ?

    + + + + + + + + + +
    + QxMemLeak module provides a fast detection of memory leaks in Debug mode once the + execution of the program is finished (with indication of the file and the line => style MFC from + Microsoft).
    + This module is developed by Wu Yongwei + and has undergone some modifications to be integrated in QxOrm.
    + If another tool is already used in your projects (Valgrind for example), this functionality + should not be activated.
    + To enable/disable QxMemLeak module, all is needed is to modify the constant + _QX_USE_MEM_LEAK_DETECTION defined in the QxConfig.h. file.
    + A recompilation of QxOrm library is necessary to take into account this modification. +
    +

    + How to manage inheritance and database ?

    + + + + + + + + + +
    + With ORM tools, there is usually 3 strategies to manage inheritance and database : + + QxOrm works by default with Concrete Table Inheritance strategy (others are not + supported yet).
    + Many tutorials and forums are available on internet to more details about ORM inheritance and + database.
    + You can find a sample in the directory ./test/qxDllSample/dll2/ with the class + BaseClassTrigger. +
    +

    + How to define a 'Trigger' with QxOrm ?

    + + + + + + + + + +
    + With QxOrm Trigger, it is possible to execute process before and/or after an insert, + update or delete query in the database.
    + You can find a sample in the directory ./test/qxDllSample/dll2/ with the class + BaseClassTrigger.
    + The class BaseClassTrigger contains 5 properties : m_id, m_dateCreation, + m_dateModification, m_userCreation and m_userModification.
    + Each property will be automatically auto-updated for all derived classes from BaseClassTrigger + (see Foo class and Bar class in the same project).
    + It is necessary to specialize 'QxDao_Trigger' template to work with this feature.
    +
    + + + + + + + +
    +
    #ifndef _QX_BASE_CLASS_TRIGGER_H_
    +#define _QX_BASE_CLASS_TRIGGER_H_
    +
    +class QX_DLL2_EXPORT BaseClassTrigger
    +{
    +
    +   QX_REGISTER_FRIEND_CLASS(BaseClassTrigger)
    +
    +protected:
    +
    +   long        m_id;
    +   QDateTime   m_dateCreation;
    +   QDateTime   m_dateModification;
    +   QString     m_userCreation;
    +   QString     m_userModification;
    +
    +public:
    +
    +   BaseClassTrigger() : m_id(0)  { ; }
    +   virtual ~BaseClassTrigger()   { ; }
    +
    +   long getId() const                     { return m_id; }
    +   QDateTime getDateCreation() const      { return m_dateCreation; }
    +   QDateTime getDateModification() const  { return m_dateModification; }
    +   QString getUserCreation() const        { return m_userCreation; }
    +   QString getUserModification() const    { return m_userModification; }
    +
    +   void setId(long l)                              { m_id = l; }
    +   void setDateCreation(const QDateTime & dt)      { m_dateCreation = dt; }
    +   void setDateModification(const QDateTime & dt)  { m_dateModification = dt; }
    +   void setUserCreation(const QString & s)         { m_userCreation = s; }
    +   void setUserModification(const QString & s)     { m_userModification = s; }
    +
    +   void onBeforeInsert(qx::dao::detail::IxDao_Helper * dao);
    +   void onBeforeUpdate(qx::dao::detail::IxDao_Helper * dao);
    +
    +};
    +
    +QX_REGISTER_HPP_QX_DLL2(BaseClassTrigger, qx::trait::no_base_class_defined, 0)
    +
    +namespace qx {
    +namespace dao {
    +namespace detail {
    +
    +template <>
    +struct QxDao_Trigger<BaseClassTrigger>
    +{
    +
    +   static inline void onBeforeInsert(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { if (t) { t->onBeforeInsert(dao); } }
    +   static inline void onBeforeUpdate(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { if (t) { t->onBeforeUpdate(dao); } }
    +   static inline void onBeforeDelete(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onBeforeFetch(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onAfterInsert(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onAfterUpdate(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onAfterDelete(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onAfterFetch(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +
    +};
    +
    +} // namespace detail
    +} // namespace dao
    +} // namespace qx
    +
    +#endif // _QX_BASE_CLASS_TRIGGER_H_
    +
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +#include "../include/BaseClassTrigger.h"
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_DLL2(BaseClassTrigger)
    +
    +namespace qx {
    +template <> void register_class(QxClass<BaseClassTrigger> & t)
    +{
    +   IxDataMember * pData = NULL;
    +
    +   pData = t.id(& BaseClassTrigger::m_id, "id");
    +
    +   pData = t.data(& BaseClassTrigger::m_dateCreation, "date_creation");
    +   pData = t.data(& BaseClassTrigger::m_dateModification, "date_modification");
    +   pData = t.data(& BaseClassTrigger::m_userCreation, "user_creation");
    +   pData = t.data(& BaseClassTrigger::m_userModification, "user_modification");
    +}}
    +
    +void BaseClassTrigger::onBeforeInsert(qx::dao::detail::IxDao_Helper * dao)
    +{
    +   Q_UNUSED(dao);
    +   m_dateCreation = QDateTime::currentDateTime();
    +   m_dateModification = QDateTime::currentDateTime();
    +   m_userCreation = "current_user_1";
    +   m_userModification = "current_user_1";
    +}
    +
    +void BaseClassTrigger::onBeforeUpdate(qx::dao::detail::IxDao_Helper * dao)
    +{
    +   Q_UNUSED(dao);
    +   m_dateModification = QDateTime::currentDateTime();
    +   m_userModification = "current_user_2";
    +}
    +
    +
    +
    +

    + How to register an abstract class into QxOrm context ?

    + + + + + + + + + +
    + A C++ abstract class (with at least one pure virtual method) cannot be mapped to a table of a database + (because it cannot be instantiated).
    + However, in some case, it can be interesting to define properties into abstract class used by a + persistent object (by inheritance).
    + A sample of abstract class registered in QxOrm context is available in the directory + ./test/qxDllSample/dll2/ of QxOrm package with the class BaseClassTrigger.
    + To register an abstract class into QxOrm context, you have to : +
      +
    • register the class with 'void register_class' like any other class ; +
    • +
    • use macro QX_REGISTER_ABSTRACT_CLASS(className) just after the class definition. +
    • +
    +
    +

    + How to register a class defined into a namespace into QxOrm context + ?

    + + + + + + + + + +
    + If a class is defined into a namespace, a compilation error occurs using macros : + QX_REGISTER_HPP and QX_REGISTER_CPP.
    + To avoid this compilation error, it is necessary to use followings macros : + QX_REGISTER_COMPLEX_CLASS_NAME_HPP and QX_REGISTER_COMPLEX_CLASS_NAME_CPP.
    + You can find a sample in the directory ./test/qxDllSample/dll1/ of QxOrm package with the class + CPerson defined into namespace qx::test :
    +
    + * QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QX_DLL1(qx::test::CPerson, QObject, 0, + qx_test_CPerson) +
    +

    + How to define a soft delete behavior ?

    + + + + + + + + + +
    + A soft delete doesn't remove rows from database (this is not a physical delete) : a new column is + added to the table definition to flag a row as deleted or not.
    + This column can contain a boolean (1 means row deleted, 0 or NULL means row not deleted), or can + contain deletion date-time (if empty or NULL, row is not deleted).
    + So you can reactivate a deleted row by setting NULL or empty value into database.
    +
    + To define a soft delete behavior with QxOrm library, you have to use the class qx::QxSoftDelete + in function mapping by class qx::register_class<T>.
    + Here is an example with the class Bar containing 2 properties m_id and m_desc + :
    +
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<Bar> & t)
    +{
    +   t.setSoftDelete(qx::QxSoftDelete("deleted_at"));
    +
    +   t.id(& Bar::m_id, "id");
    +   t.data(& Bar::m_desc, "desc");
    +}}
    +
    +
    + SQL queries builded by QxOrm library will take into account this soft delete parameter to add + conditions (don't fetch deleted item, don't delete physically a row, etc.).
    + For example, if you execute this code with the class Bar :
    +
    + + + + + + + +
    +
    Bar_ptr pBar; pBar.reset(new Bar());
    +pBar->setId(5);
    +QSqlError daoError = qx::dao::delete_by_id(pBar);     qAssert(! daoError.isValid());
    +qx_bool bDaoExist = qx::dao::exist(pBar);             qAssert(! bDaoExist);
    +daoError = qx::dao::delete_all<Bar>();                qAssert(! daoError.isValid());
    +long lBarCount = qx::dao::count<Bar>();               qAssert(lBarCount == 0);
    +daoError = qx::dao::destroy_all<Bar>();               qAssert(! daoError.isValid());
    +
    +
    + You will obtain following output trace :
    +
    + + + + + + + +
    +
    [QxOrm] sql query (93 ms) : UPDATE Bar SET deleted_at = '20110617115148615' WHERE id = :id
    +[QxOrm] sql query (0 ms) : SELECT Bar.id AS Bar_id_0, Bar.deleted_at FROM Bar WHERE Bar.id = :id AND (Bar.deleted_at IS NULL OR Bar.deleted_at = '')
    +[QxOrm] sql query (78 ms) : UPDATE Bar SET deleted_at = '20110617115148724'
    +[QxOrm] sql query (0 ms) : SELECT COUNT(*) FROM Bar WHERE (Bar.deleted_at IS NULL OR Bar.deleted_at = '')
    +[QxOrm] sql query (110 ms) : DELETE FROM Bar
    +
    +
    + Note : to delete physically a row from database, you have to use followings functions : + qx::dao::destroy_by_id() and qx::dao::destroy_all().
    +
    + Other note : it is recommended to define into database an index on column deleted_at to + optimize execution of SQL queries.
    +
    +

    + How to use a session (qx::QxSession class) to manage automatically database + transactions (using C++ RAII) ?

    + + + + + + + + + +
    + A database transaction is a sequence of operations performed as a single logical + unit of work.
    + If no errors occurred during the execution of the transaction then the system commits the + transaction.
    + If an error occurs during the transaction, or if the user specifies a rollback operation, the + data manipulations within the transaction are not persisted to the database.
    +
    + The qx::QxSession + class of QxOrm library is designed to manage automatically database transactions (using C++ + RAII) :
    +
    + + + + + + + +
    +
    { // Start a scope where a new session is instantiated
    +
    +  // Create a session : a valid database connexion by thread is automatically assigned to the session and a transaction is opened
    +  qx::QxSession session;
    +
    +  // Execute some operations with database (using += operator of qx::QxSession class and session database connexion)
    +  session += qx::dao::insert(my_object, session.database());
    +  session += qx::dao::update(my_object, session.database());
    +  session += qx::dao::fetch_by_id(my_object, session.database());
    +  session += qx::dao::delete_by_id(my_object, session.database());
    +
    +  // If the session is not valid (so an error occured) => display first error
    +  if (! session.isValid()) { qDebug("[QxOrm] session error : '%s'", qPrintable(session.firstError().text())); }
    +
    +} // End of scope : session is destroyed (transaction => automatically commit or rollback if there is an error)
    +
    +
    + Note : a session can throw a qx::dao::sql_error exception when a SQL error occured (by + default, there is no exception). You can setup this feature using :
    + * qx::QxSession constructor (for a specific session) ;
    + * qx::QxSqlDatabase::getSingleton()->setSessionThrowable(bool b) parameter (for all + sessions).
    +
    + Other note : don't forget to pass the session database connexion to each qx::dao::xxx + functions (using session.database() method).
    + Moreover, you can manage your own database connexion (from a connexion pool for example) using + constructor of qx::QxSession class.
    +
    + qx::QxSession class provides also persistent methods (CRUD) to make easier to write C++ + code.
    + Here is the same example using methods of qx::QxSession class instead of functions into + namespace qx::dao :
    +
    + + + + + + + +
    +
    { // Start a scope where a new session is instantiated
    +
    +  // Create a session : a valid database connexion by thread is automatically assigned to the session and a transaction is opened
    +  qx::QxSession session;
    +
    +  // Execute some operations with database
    +  session.insert(my_object);
    +  session.update(my_object);
    +  session.fetchById(my_object);
    +  session.deleteById(my_object);
    +
    +  // If the session is not valid (so an error occured) => display first error
    +  if (! session.isValid()) { qDebug("[QxOrm] session error : '%s'", qPrintable(session.firstError().text())); }
    +
    +} // End of scope : session is destroyed (transaction => automatically commit or rollback if there is an error)
    +
    +
    +
    +

    + How to persist a type without its source code (class from an external library for + example) ?

    + + + + + + + + + +
    + QxOrm library can persist every types, not only classes registered in QxOrm context using + qx::register_class<T>().
    +
    + It's necessary to write serialization functions from boost framework, using the non intrusive + method (because source code is not available or is read-only). + For more details on boost serialization module, goto + official website.
    +
    + For example, imagine that you have the class 'ExtObject3D' from an external library and the + source code is not available or is read-only. + Here is the code to can persist an instance of 'ExtObject3D' type into database :
    +
    + + + + + + + +
    +
    #ifndef _PERSIST_EXTOBJECT3D_H_
    +#define _PERSIST_EXTOBJECT3D_H_
    +
    +#include "ExtObject3D.h"
    +
    +#include <boost/serialization/serialization.hpp>
    +#include <boost/serialization/split_free.hpp>
    +#include <boost/serialization/nvp.hpp>
    + 
    +namespace boost {
    +namespace serialization {
    +
    +template <class Archive>
    +void save(Archive & ar, const ExtObject3D & t, unsigned int version)
    +{
    +   Q_UNUSED(version);
    +   double x(t.getX()), y(t.getY()), z(t.getZ()), angle(t.getAngle());
    +
    +   ar << boost::serialization::make_nvp("x", x);
    +   ar << boost::serialization::make_nvp("y", y);
    +   ar << boost::serialization::make_nvp("z", z);
    +   ar << boost::serialization::make_nvp("angle", angle);
    +}
    +
    +template <class Archive>
    +void load(Archive & ar, ExtObject3D & t, unsigned int version)
    +{
    +   Q_UNUSED(version);
    +   double x(0.0), y(0.0), z(0.0), angle(0.0);
    +
    +   ar >> boost::serialization::make_nvp("x", x);
    +   ar >> boost::serialization::make_nvp("y", y);
    +   ar >> boost::serialization::make_nvp("z", z);
    +   ar >> boost::serialization::make_nvp("angle", angle);
    +
    +   t.setX(x);
    +   t.setY(y);
    +   t.setZ(z);
    +   t.setAngle(angle);
    +}
    +
    +} // namespace serialization
    +} // namespace boost
    + 
    +BOOST_SERIALIZATION_SPLIT_FREE(ExtObject3D)
    +
    +#endif // _PERSIST_EXTOBJECT3D_H_
    +
    +
    + Now you can persist an instance of 'ExtObject3D' type into database : so you can have a + 'ExtObject3D' property in a persistent class registered in QxOrm context. + This property can be mapped with a column of type TEXT or VARCHAR into database.
    +
    + The default behaviour of QxOrm library is : the instance is serialized to XML format before to be + inserted or updated into database. + This default behaviour can be useful, for example if you want to save a collection of items without to + make relation (so you don't have to manage another table into database). + For example, with a property of type std::vector<mon_objet> in a persistent class without + relation, the list of items will be saved into database under XML format.
    +
    + Note : the default behaviour can be easily modified for a specific type. + QtSql engine uses QVariant type to link C++ code and database. + QVariant type can contain text, numeric, binary, etc. + So it can be interesting to specialize the default behaviour (XML serialization) if you want to save + datas under binary format or to optimize your application (XML serialization is not very fast). + You just have to write (with boost serialization functions) a conversion into/from QVariant + type, for example with 'ExtObject3D' class :
    +
    + + + + + + + +
    +
    namespace qx {
    +namespace cvt {
    +namespace detail {
    +
    +template <> struct QxConvert_ToVariant< ExtObject3D > {
    +static inline QVariant toVariant(const ExtObject3D & t, const QString & format, int index)
    +{ /* Ici je convertis ExtObject3D en QVariant */ } };
    +
    +template <> struct QxConvert_FromVariant< ExtObject3D > {
    +static inline qx_bool fromVariant(const QVariant & v, ExtObject3D & t, const QString & format, int index)
    +{ /* Ici je convertis QVariant en ExtObject3D */; return qx_bool(true); } };
    +
    +} // namespace detail
    +} // namespace cvt
    +} // namespace qx
    +
    +
    +
    +

    + How to use introspection engine (or reflection engine) of QxOrm library + ?

    + + + + + + + + + +
    + All classes registered in QxOrm context using qx::register_class<T>() function can be + used by introspection engine (or reflection engine) of QxOrm library. + Introspection engine can provide dynamically (so during program execution) some informations about + types. + Those informations are called meta-datas and can list all classes characteristics (properties, + methods, etc.). + Many programming languages (for example Java or C#) have natively this mechanism, but not C++, that's + why QxOrm library emulates an introspection engine.
    +
    + Here is a list of QxOrm library classes to access to meta-datas : +
      +
    • qx::QxClassX : + singleton class to iterate over all classes registered in QxOrm context using + qx::register_class<T>() ; +
    • +
    • qx::IxClass : interface + for a class registered in QxOrm context ;
    • +
    • qx::IxDataMemberX : list of properties associated to a class ;
    • +
    • qx::IxDataMember : + interface for a class property ;
    • +
    • qx::IxFunctionX : + list of methods associated to a class ;
    • +
    • qx::IxFunction : + interface for a class method.
    • +
    + An instance of qx::IxClass type contains the list of class properties + (qx::IxDataMemberX) and the list of class methods (qx::IxFunctionX).
    +
    + Introspection engine of QxOrm library provides : +
      +
    • to create dynamically an instance of a class using class name under string format + (qx::create()) ;
    • +
    • to access/modify dynamically the value of an object field (qx::IxDataMember::getValue() + and qx::IxDataMember::setValue()) ;
    • +
    • to invoke dynamically a class method (qx::IxFunction::invoke()) ;
    • +
    • to access to the class hierarchy (qx::IxClass::getBaseClass()).
    • +
    + Note : QxService module + of QxOrm library (click here to go to the tutorial) to + create easily a C++ application server is based on introspection engine to call dynamically services + methods (client request) on server side.
    +
    + Here is a sample using introspection engine : how to dump all classes, properties and methods + registered in QxOrm context ?
    +
    + + + + + + + +
    +
    QString QxClassX::dumpAllClasses()
    +{
    +   QxClassX::registerAllClasses();
    +   QxCollection<QString, IxClass *> * pAllClasses = QxClassX::getAllClasses();
    +   if (! pAllClasses) { qAssert(false); return ""; }
    +
    +   QString sDump;
    +   long lCount = pAllClasses->count();
    +   qDebug("[QxOrm] start dump all registered classes (%ld)", lCount);
    +   _foreach(IxClass * pClass, (* pAllClasses))
    +   { if (pClass) { sDump += pClass->dumpClass(); } }
    +   qDebug("[QxOrm] %s", "end dump all registered classes");
    +
    +   return sDump;
    +}
    +
    +QString IxClass::dumpClass() const
    +{
    +   QString sDump;
    +   sDump += "-- class '" + m_sKey + "' (name '" + m_sName + "', ";
    +   sDump += "description '" + m_sDescription + "', version '" + QString::number(m_lVersion) + "', ";
    +   sDump += "base class '" + (getBaseClass() ? getBaseClass()->getKey() : "") + "')\n";
    +
    +   long lCount = (m_pDataMemberX ? m_pDataMemberX->count() : 0);
    +   sDump += "\t* list of registered properties (" + QString::number(lCount) + ")\n";
    +   if (m_pDataMemberX)
    +   {
    +      IxDataMember * pId = this->getId();
    +      for (long l = 0; l < lCount; l++)
    +      {
    +         IxDataMember * p = m_pDataMemberX->get(l); if (! p) { continue; }
    +         IxSqlRelation * pRelation = p->getSqlRelation();
    +         QString sInfos = p->getKey() + ((p == pId) ? QString(" (id)") : QString());
    +         sInfos += (pRelation ? (QString(" (") + pRelation->getDescription() + QString(")")) : QString());
    +         sDump += "\t\t" + sInfos + "\n";
    +      }
    +   }
    +
    +   lCount = (m_pFctMemberX ? m_pFctMemberX->count() : 0);
    +   sDump += "\t* list of registered functions (" + QString::number(lCount) + ")\n";
    +   if (m_pFctMemberX)
    +   {
    +      _foreach_if(IxFunction_ptr p, (* m_pFctMemberX), (p))
    +      { QString sKey = p->getKey(); sDump += "\t\t" + sKey + "\n"; }
    +   }
    +
    +   qDebug("%s", qPrintable(sDump));
    +   return sDump;
    +}
    +
    +
    + Using the function qx::QxClassX::dumpAllClasses() with qxBlog tutorial, you will obtain following output :
    +
    + + + + + + + +
    +
    [QxOrm] start dump all registered classes (4)
    +-- class 'author' (name 'author', description '', version '0', base class '')
    +	* list of registered properties (5)
    +		author_id (id)
    +		name
    +		birthdate
    +		sex
    +		list_blog (relation one-to-many)
    +	* list of registered functions (1)
    +		age
    +
    +-- class 'blog' (name 'blog', description '', version '0', base class '')
    +	* list of registered properties (6)
    +		blog_id (id)
    +		blog_text
    +		date_creation
    +		author_id (relation many-to-one)
    +		list_comment (relation one-to-many)
    +		list_category (relation many-to-many)
    +	* list of registered functions (0)
    +
    +-- class 'comment' (name 'comment', description '', version '0', base class '')
    +	* list of registered properties (4)
    +		comment_id (id)
    +		comment_text
    +		date_creation
    +		blog_id (relation many-to-one)
    +	* list of registered functions (0)
    +
    +-- class 'category' (name 'category', description '', version '0', base class '')
    +	* list of registered properties (4)
    +		category_id (id)
    +		name
    +		description
    +		list_blog (relation many-to-many)
    +	* list of registered functions (0)
    +
    +[QxOrm] end dump all registered classes
    +
    +
    + Note : you can add some informations to introspection engine using property bag + mechanism. + Indeed, qx::IxClass, qx::IxDataMember and qx::IxFunction classes contain a list + of QVariant items associated to a QString key (see qx::QxPropertyBag class for + more details).
    +
    +
    +

    + How to register automatically Qt meta-properties (using Q_PROPERTY macro) to + QxOrm context ?

    + + + + + + + + + +
    + All classes inherited from QObject type can use Q_PROPERTY macro : + those properties become meta-properties. + This is how Qt framework provides an introspection engine using the moc process. + Meta-properties can be used for example by QML engine, QtScript, etc.
    +
    + QxOrm library needs to register each properties per class in the mapping function void + qx::register_class<T>() to provide all features (persistence, XML and binary + serialization, etc.). + It's possible to register automatically all Qt meta-properties into QxOrm context without to manage + any mapping function per class void qx::register_class<T>() : + QX_REGISTER_ALL_QT_PROPERTIES() macro works with Qt introspection engine to iterate over all + meta-properties.
    +
    + Here is an example with TestQtProperty class into ./test/qxDllSample/dll1/include/ + directory of QxOrm package :
    +
    + + + + + + + +
    +
    #ifndef _QX_TEST_QT_META_PROPERTY_H_
    +#define _QX_TEST_QT_META_PROPERTY_H_
    + 
    +class QX_DLL1_EXPORT TestQtProperty : public QObject
    +{
    +
    +   Q_OBJECT
    +   Q_PROPERTY(int id READ id WRITE setId)
    +   Q_PROPERTY(long number READ number WRITE setNumber)
    +   Q_PROPERTY(QString desc READ desc WRITE setDesc)
    +   Q_PROPERTY(QDateTime birthDate READ birthDate WRITE setBirthDate)
    +   Q_PROPERTY(QVariant photo READ photo WRITE setPhoto)
    +
    +protected:
    +
    +   int         m_id;
    +   long        m_number;
    +   QString     m_desc;
    +   QDateTime   m_birthDate;
    +   QVariant    m_photo;
    +
    +public:
    +
    +   TestQtProperty() : QObject(), m_id(0), m_number(0) { ; }
    +   virtual ~TestQtProperty() { ; }
    +
    +   int id() const                { return m_id; }
    +   long number() const           { return m_number; }
    +   QString desc() const          { return m_desc; }
    +   QDateTime birthDate() const   { return m_birthDate; }
    +   QVariant photo() const        { return m_photo; }
    +
    +   void setId(int i)                         { m_id = i; }
    +   void setNumber(long l)                    { m_number = l; }
    +   void setDesc(const QString & s)           { m_desc = s; }
    +   void setBirthDate(const QDateTime & dt)   { m_birthDate = dt; }
    +   void setPhoto(const QVariant & v)         { m_photo = v; }
    + 
    +};
    +
    +QX_REGISTER_HPP_QX_DLL1(TestQtProperty, QObject, 0)
    +
    +#endif // _QX_TEST_QT_META_PROPERTY_H_
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/TestQtProperty.h"
    +
    +#include <QxOrm_Impl.h>
    + 
    +QX_REGISTER_CPP_QX_DLL1(TestQtProperty)
    +QX_REGISTER_ALL_QT_PROPERTIES(TestQtProperty, "id")
    +
    +
    + If you don't want to use QX_REGISTER_ALL_QT_PROPERTIES macro, you can write 4 lines of code + :
    +
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<TestQtProperty> & t)
    +{ qx::register_all_qt_properties<TestQtProperty>(t, "id"); }
    +} // namespace qx
    +
    +
    + Note : the second parameter of QX_REGISTER_ALL_QT_PROPERTIES macro is the name of the + property mapped to the primary key into database. + If this parameter is empty, then the class doesn't have any primary key or the primary key has been + registered in a base class.
    +
    + All properties defined with Q_PROPERTY macro can be registered in QxOrm context in two + different ways :
    + 1- with the classic method : t.data(& MyQObject::my_property, "my_property", 0);
    + 2- or without writing the data-member pointer : t.data("my_property", 0);
    +
    + You can use the first or the second method to register your properties into QxOrm context and access + to the same functionalities using the common interface qx::IxDataMember. + You can also mix Qt meta-properties and classic registration data-member into the same mapping + function void qx::register_class<T>(). + Each registration method has some advantages and disadvantages.
    +
    + Here is the list of advantages using the second registration method into QxOrm context : +
      +
    • much more faster to compile ;
    • +
    • reduce exec size ;
    • +
    • strong integration with Qt introspection/moc engine ;
    • +
    • no need to manage any mapping function per class using QX_REGISTER_ALL_QT_PROPERTIES + macro.
    • +
    + Here is the list of disadvantages compared to the classic registration method : +
      +
    • need to inherit from QObject class to use Q_PROPERTY macro ;
    • +
    • program execution more slower (QVariant type versus C++ template) ;
    • +
    • doesn't support relation between tables into database (one-to-one, one-to-many, + many-to-one and many-to-many) ; +
    • +
    • cannot access to the data-member pointer of a class (need to convert to QVariant type + before to access or to modify a value).
    • +
    +
    +

    + How to build a query without writing SQL with the class qx::QxSqlQuery + ?

    + + + + + + + + + +
    + The class qx::QxSqlQuery (or its typedef qx_query) is used to communicate + with database (to filter, to sort, etc.) in two different ways : +
      +
    • writing manually SQL query ;
    • +
    • using C++ methods with a syntax similar to SQL (same concept than the great library SubSonic for .Net). +
    • +
    + With the first method (writing manually SQL query), you can use some optimizations specific for each + database.
    + The second method (using C++ code to build SQL query) binds automatically SQL parameters without using + qx::QxSqlQuery::bind() function.
    +
    + Here is an example with qx::QxSqlQuery class writing manually a SQL query :
    +
    + + + + + + + +
    +
    // Build a SQL query to fetch only 'author' of type 'female'
    +qx::QxSqlQuery query("WHERE author.sex = :sex");
    +query.bind(":sex", author::female);
    +
    +QList<author> list_of_female;
    +QSqlError daoError = qx::dao::fetch_by_query(query, list_of_female);
    +for (long l = 0; l < list_of_female.count(); l++)
    +{ /* here we can work with the collection provided by database */ }
    +
    +
    + QxOrm library provides 3 styles to write SQL parameters.
    + This style can be modified for a project using the following method + qx::QxSqlDatabase::getSingleton()->setSqlPlaceHolderStyle() :
    +
      +
    • ph_style_2_point_name : "WHERE author.sex = :sex" (default style) ;
    • +
    • ph_style_at_name : "WHERE author.sex = @sex" ;
    • +
    • ph_style_question_mark : "WHERE author.sex = ?".
    • +
    + Here is the same example using C++ code of the class qx::QxSqlQuery (or its typedef + qx_query) to build query automatically :
    +
    + + + + + + + +
    +
    // Build a SQL query to fetch only 'author' of type 'female'
    +qx_query query;
    +query.where("author.sex").isEqualTo(author::female);
    +
    +QList<author> list_of_female;
    +QSqlError daoError = qx::dao::fetch_by_query(query, list_of_female);
    +for (long l = 0; l < list_of_female.count(); l++)
    +{ /* here we can work with the collection provided by database */ }
    +
    +
    + With C++ methods of qx::QxSqlQuery class, you don't have to bind any SQL parameter, and the + syntax is similar to real SQL.
    + All SQL parameters will be provided to database automatically with the following style : + qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle().
    +
    + Here is an example with many methods of qx::QxSqlQuery class (or its typedef qx_query) + :
    +
    + + + + + + + +
    +
    qx_query query;
    +query.where("sex").isEqualTo(author::female)
    +     .and_("age").isGreaterThan(38)
    +     .or_("last_name").isNotEqualTo("Dupont")
    +     .or_("first_name").like("Alfred")
    +     .and_OpenParenthesis("id").isLessThanOrEqualTo(999)
    +     .and_("birth_date").isBetween(date1, date2)
    +     .closeParenthesis()
    +     .or_("id").in(50, 999, 11, 23, 78945)
    +     .and_("is_deleted").isNotNull()
    +     .orderAsc("last_name", "first_name", "sex")
    +     .limit(50, 150);
    +
    +
    + This code will produce following SQL for MySQL, PostgreSQL and SQLite databases + (for Oracle and SQLServer, there is a specific process for limit() method) :
    +
    + + + + + + + +
    +
    WHERE sex = :sex_1_0 
    +AND age > :age_3_0 
    +OR last_name <> :last_name_5_0 
    +OR first_name LIKE :first_name_7_0 
    +AND ( id <= :id_10_0 AND birth_date BETWEEN :birth_date_12_0_1 AND :birth_date_12_0_2 ) 
    +OR id IN (:id_15_0_0, :id_15_0_1, :id_15_0_2, :id_15_0_3, :id_15_0_4) 
    +AND is_deleted IS NOT NULL 
    +ORDER BY last_name ASC, first_name ASC, sex ASC 
    +LIMIT :limit_rows_count_19_0 OFFSET :offset_start_row_19_0
    +
    +
    + Here is the list of all functions available to use qx::QxSqlQuery class (or its typedef + qx_query) :
    +
    + + + + + + + +
    +
    // with functions into namespace qx::dao
    +qx::dao::count<T>()
    +qx::dao::fetch_by_query<T>()
    +qx::dao::update_by_query<T>()
    +qx::dao::delete_by_query<T>()
    +qx::dao::destroy_by_query<T>()
    +qx::dao::fetch_by_query_with_relation<T>()
    +qx::dao::fetch_by_query_with_all_relation<T>()
    +qx::dao::update_by_query_with_relation<T>()
    +qx::dao::update_by_query_with_all_relation<T>()
    +qx::dao::update_optimized_by_query<T>()
    +
    +// with qx::QxSession class
    +qx::QxSession::count<T>()
    +qx::QxSession::fetchByQuery<T>()
    +qx::QxSession::update<T>()
    +qx::QxSession::deleteByQuery<T>()
    +qx::QxSession::destroyByQuery<T>()
    +
    +// with qx::QxRepository<T> class
    +qx::QxRepository<T>::count()
    +qx::QxRepository<T>::fetchByQuery()
    +qx::QxRepository<T>::update()
    +qx::QxRepository<T>::deleteByQuery()
    +qx::QxRepository<T>::destroyByQuery()
    +
    +
    + Note : those functions have 2 other optionals parameters : +
      +
    • const QStringList & columns : to indicate columns to fetch (by default, all columns are + fetched) ;
    • +
    • const QStringList & relation : to indicate relations to fetch (one-to-one, + one-to-many, many-to-one and many-to-many defined into void + qx::register_class<T>() mapping function by class), by default there is no relation + fetched. +
    • +
    +
    +

    + How to use the cache (functions into namespace qx::cache) of QxOrm + library ?

    + + + + + + + + + +
    + Cache engine provided by QxOrm library (QxCache module) is thread-safe and can store easily any kind of + objects.
    + Functions to access to the cache engine are inside namespace qx::cache.
    + qx::cache engine can provide a program optimization : you can for example store items fetched + by a query to database.
    +
    + Each item into the cache is associated with a key of type QString : this key provides a quick + access to an item stored into the cache.
    + If a new item is inserted with a key already in the cache, then the old item associated with this key + is removed automatically from the cache.
    +
    + Cache engine of QxOrm library doesn't manage memory : there is no delete called by the cache + engine.
    + This is why it's strongly recommended (but not an obligation) to store smart-pointers into the cache : + for example, boost::shared_ptr<T> of boost library or QSharedPointer<T> of Qt library.
    +
    + Cache engine can have a max cost to avoid too much memory usage : each item inserted to the cache can + be associated with a cost (for example, element's count of a collection).
    + When the limit (max cost) of the cache engine is reached, first items inserted to the cache are + automatically removed (insertion order) until limit of the cache is ok.
    +
    + It's also possible to associate a date-time insertion when an item is added to the cache.
    + If there is no date-time, then the current date-time is taken into account.
    + This feature provides a way to verify that an item stored into the cache must be updated or not.
    +
    + Here is an example using cache engine of QxOrm library (functions into namespace + qx::cache) :
    +
    + + + + + + + +
    +
    // Define max cost of cache engine to 500
    +qx::cache::max_cost(500);
    +
    +// Fetch a list of 'author' from database
    +boost::shared_ptr< QList<author> > list_author;
    +QSqlError daoError = qx::dao::fetch_all(list_author);
    +
    +// Insert the list of 'author' to the cache
    +qx::cache::set("list_author", list_author);
    +
    +// Fetch a list of 'blog' from database
    +QSharedPointer< std::vector<blog> > list_blog;
    +daoError = qx::dao::fetch_all(list_blog);
    +
    +// Insert the list of 'blog' to the cache (cost = 'blog' count)
    +qx::cache::set("list_blog", list_blog, list_blog.count());
    +
    +// Pointer to an object of type 'comment'
    +comment_ptr my_comment;
    +my_comment.reset(new comment(50));
    +daoError = qx::dao::fetch_by_id(my_comment);
    +
    +// Insert 'comment' to the cache with a date-time insertion
    +qx::cache::set("comment", my_comment, 1, my_comment->dateModif());
    +
    +// Get the list of 'blog' stored into the cache
    +list_blog = qx::cache::get< QSharedPointer< std::vector<blog> > >("list_blog");
    +
    +// Get the list of 'blog' without providing the type
    +qx_bool bGetOk = qx::cache::get("list_blog", list_blog);
    +
    +// Remove list of 'author' from cache
    +bool bRemoveOk = qx::cache::remove("list_author");
    +
    +// Get items count stored into the cache
    +long lCount = qx::cache::count();
    +
    +// Get current cost of items stored into the cache
    +long lCurrentCost = qx::cache::current_cost();
    +
    +// Verify that an element with the key "comment" exists into the cache
    +bool bExist = qx::cache::exist("comment");
    +
    +// Get 'comment' stored into the cache with its date-time insertion
    +QDateTime dt;
    +bGetOk = qx::cache::get("comment", my_comment, dt);
    +
    +// Clear the cache
    +qx::cache::clear();
    +
    +
    +
    +

    + How to build SQL schema (create and update tables) based on C++ persistents classes + registered in QxOrm context ?

    + + + + + + + + + +
    + It's recommended to use QxEntityEditor application to manage SQL schema + generation.
    +
    + QxOrm library doesn't provide a generator to create and to update automatically tables into + database.
    + Indeed, qx::dao::create_table<T> function must be used only to create prototypes or + samples.
    + It's strongly recommended to work with a tool provided by each SGBD to design and to manage tables + into database (for example Navicat with MySql, pgAdmin with PostgreSQL, + SQLite Manager with SQLite, etc.).
    + Moreover, each tool provided by each SGBD can add some optimizations to the database (add some indexes + for example).
    +
    + But sometimes, it can be useful to not have to manage manually tables into database.
    + In this case, it's possible to create a C++ function to iterate over all persistents classes + registered in QxOrm context (using introspection engine of QxOrm library) : so you can build a SQL + script to create and to update tables into database.
    +
    + QxOrm library provides an example of a C++ function : based on this function, you can create your own + function to build SQL schema.
    + This QxOrm function is written in the file ./src/QxRegister/QxClassX.cpp and is called QString + qx::QxClassX::dumpSqlSchema().
    + This QxOrm function builds a SQL script and returns a QString value : it's also possible to + modify the function to generate a file with SQL script or to execute each SQL process directly to the + SGBD.
    +
    + Here is a sample implementation provided by dodobibi to manage a + PostgreSQL database : this sample works with a version number to add columns to existing + tables, to add some indexes to existing columns, etc.
    + When you start your application, a version number is provided and incremented when a new version of + your application is released :
    +
    + + + + + + + +
    +
    QApplication app(argc, argv);
    +app.setProperty("DomainVersion", 1);
    +
    +
    + A table into the database must be created to store this version number.
    + A C++ persistent class is mapped to this table :
    +
    + + + + + + + +
    +
    #ifndef _DATABASE_VERSION_H_
    +#define _DATABASE_VERSION_H_
    + 
    +class MY_DLL_EXPORT DatabaseVersion
    +{
    +public:
    +  QString name;
    +  long version;
    +};
    +
    +QX_REGISTER_HPP_MY_APP(DatabaseVersion, qx::trait::no_base_class_defined, 0)
    +
    +#endif // _DATABASE_VERSION_H_
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +#include "../include/DatabaseVersion.h"
    +#include <QxOrm_Impl.h>
    + 
    +QX_REGISTER_CPP_MY_APP(DatabaseVersion)
    +
    +namespace qx {
    +template <> void register_class(QxClass<DatabaseVersion> & t)
    +{
    +  t.id(& DatabaseVersion::name, "name");
    +  t.data(& DatabaseVersion::version, "version");
    +}}
    +
    +
    + With DatabaseVersion class, it's possible to verify that the database must be updated or + not.
    + This is the goal of isDatabaseVersionOld() function :
    +
    + + + + + + + +
    +
    bool isDatabaseVersionOld()
    +{
    +  DatabaseVersion dbVersion;
    +  dbVersion.name = "MyAppName";
    +  QSqlError err = qx::dao::fetch_by_id(dbVersion);
    +  if (err.isValid()) { qAssert(false); return false; }
    +  return (dbVersion.version < qApp->property("DomainVersion").toInt());
    +}
    +
    +
    + If isDatabaseVersionOld() function returns true when you start your application, then + you must update your SQL schema :
    +
    + + + + + + + +
    +
    void updateDatabaseVersion()
    +{
    +  try
    +  {
    +    int domainVersion = qApp->property("DomainVersion").toInt();
    +
    +    // Connect to the database with a user with modifications rights on SQL schema
    +    QSqlDatabase db = qx::QxSqlDatabase::getSingleton()->getDatabaseCloned();
    +    db.setUserName("MyAdminLogin");
    +    db.setPassword("MyAdminPassword");
    +
    +    // Create a session, open automatically a transaction and throw an exception when an error occured
    +    qx::QxSession session(db, true, true);
    +
    +    // Fetch the database version with a lock to protect the database
    +    DatabaseVersion dbVersion;
    +    session.fetchByQuery(qx_query("WHERE name='MyAppName' FOR UPDATE"), dbVersion);
    +
    +    // When unlocked for other users, verify that the database must be updated or not
    +    if (dbVersion.version >= domainVersion) { return; }
    +
    +    // Execute each SQL process with "query" variable
    +    QSqlQuery query(db);
    +
    +    // Fetch all C++ persistents classes registered in QxOrm context
    +    qx::QxCollection<QString, qx::IxClass *> * pAllClasses = qx::QxClassX::getAllClasses();
    +    if (! pAllClasses) { qAssert(false); return; }
    +
    +    // Fetch all tables into database (this is a Qt function)
    +    QStringList tables = db.tables();
    +
    +    for (long k = 0; k < pAllClasses->count(); k++)
    +    {
    +      qx::IxClass * pClass = pAllClasses->getByIndex(k);
    +      if (! pClass) { continue; }
    +
    +      // Filter non persitents classes
    +      if (pClass->isKindOf("qx::service::IxParameter") || pClass->isKindOf("qx::service::IxService")) { continue; }
    +
    +      // Filter already updated classes
    +      if (pClass->getVersion() <= dbVersion.version) { continue; }
    +
    +      // If table doesn't exist, create it and set the owner
    +      if (! tables.contains(pClass->getName()))
    +      {
    +        query.exec("CREATE TABLE " + pClass->getName() + " ( ) WITH (OIDS = FALSE);"
    +                   "ALTER TABLE " + pClass->getName() + " OWNER TO \"MyAdminLogin\";");
    +        session += query.lastError();
    +      }
    +
    +      // If a column doesn't exist, add it to the table
    +      qx::IxDataMemberX * pDataMemberX = pClass->getDataMemberX();
    +      for (long l = 0; (pDataMemberX && (l < pDataMemberX->count_WithDaoStrategy())); l++)
    +      {
    +        qx::IxDataMember * p = pDataMemberX->get_WithDaoStrategy(l);
    +        if (! p || (p->getVersion() <= dbVersion.version)) { continue; }
    +
    +        query.exec("ALTER TABLE " + pClass->getName() + " ADD COLUMN " + p->getName() + " " + p->getSqlType() + ";");
    +        session += query.lastError();
    +
    +        if (p->getIsPrimaryKey()) // PRIMARY KEY
    +        {
    +          query.exec("ALTER TABLE " + pClass->getName() + " ADD PRIMARY KEY (" + p->getName() + ");");
    +          session += query.lastError();
    +        }
    +
    +        if (p->getAllPropertyBagKeys().contains("INDEX")) // INDEX
    +        {
    +          query.exec("CREATE INDEX " + pClass->getName() + "_" + p->getName() + "_idx" + 
    +                     " ON " + pClass->getName() + " USING " + p->getPropertyBag("INDEX").toString() + " (" + p->getName() + ");");
    +          session += query.lastError();
    +        }
    +
    +        if (p->getNotNull()) // NOT NULL
    +        {
    +          query.exec("ALTER TABLE " + pClass->getName() + " ALTER COLUMN " + p->getName() + " SET NOT NULL;");
    +          session += query.lastError();
    +        }
    +
    +        if (p->getAutoIncrement()) // AUTO INCREMENT
    +        {
    +          query.exec("CREATE SEQUENCE " + pClass->getName() + "_" + p->getName() + "_seq" + "; "
    +                     "ALTER TABLE " + pClass->getName() + "_" + p->getName() + "_seq" + " OWNER TO \"MyAdminLogin\"; "
    +                     "ALTER TABLE " + pClass->getName() + " ALTER COLUMN " + p->getName() + " " +
    +                     "SET DEFAULT nextval('" + pClass->getName() + "_" + p->getName() + "_seq" + "'::regclass);");
    +          session += query.lastError();
    +        }
    +
    +        if (p->getDescription() != "") // DESCRIPTION
    +        {          query.exec("COMMENT ON COLUMN " + pClass->getName() + "." + p->getName() + " IS $$" + p->getDescription() + "$$ ;");
    +          session += query.lastError();
    +        }
    +      }
    +    }
    +
    +    // Save current version of the database
    +    dbVersion.version = domainVersion;
    +    session.save(dbVersion);
    +
    +    // End of "try" scope : session is destroyed => commit or rollback automatically
    +    // Moreover, a commit or a rollback unlock the process for all users
    +  }
    +  catch (const qx::dao::sql_error & err)
    +  {
    +    QSqlError sqlError = err.get();
    +    qDebug() << sqlError.databaseText();
    +    qDebug() << sqlError.driverText();
    +    qDebug() << sqlError.number();
    +    qDebug() << sqlError.type();
    +  }
    +}
    +
    +
    + Note : this code (like qx::QxClassX::dumpSqlSchema() function) can be modified to provide more + features.
    + For example, it could be interesting to create by default another table (like DatabaseVersion + table) to store the list of all persistents classes registered in QxOrm context : instead of using + "db.tables()" Qt function, it could be possible to fetch all tables with more informations + (version number for each table, columns count registered in QxOrm context, table description, etc.). +
    +
    +

    + How to associate a SQL type to a C++ class ?

    + + + + + + + + + +
    + Each database provides its own SQL types to store datas.
    + QxOrm library associates by default some C++ classes frequently used in a program :
    +
    + + + + + + + +
    +
    "bool" <-> "SMALLINT"
    +"qx_bool" <-> "SMALLINT"
    +"short" <-> "SMALLINT"
    +"int" <-> "INTEGER"
    +"long" <-> "INTEGER"
    +"long long" <-> "INTEGER"
    +"float" <-> "FLOAT"
    +"double" <-> "FLOAT"
    +"long double" <-> "FLOAT"
    +"unsigned short" <-> "SMALLINT"
    +"unsigned int" <-> "INTEGER"
    +"unsigned long" <-> "INTEGER"
    +"unsigned long long" <-> "INTEGER"
    +"std::string" <-> "TEXT"
    +"std::wstring" <-> "TEXT"
    +"QString" <-> "TEXT"
    +"QVariant" <-> "TEXT"
    +"QUuid" <-> "TEXT"
    +"QDate" <-> "DATE"
    +"QTime" <-> "TIME"
    +"QDateTime" <-> "TIMESTAMP"
    +"QByteArray" <-> "BLOB"
    +"qx::QxDateNeutral" <-> "TEXT"
    +"qx::QxTimeNeutral" <-> "TEXT"
    +"qx::QxDateTimeNeutral" <-> "TEXT"
    +
    +
    + If a SQL type provided by default by QxOrm library is not supported by the database, it can be easily + modified (globally for all the application) using the following collection :
    +
    + + + + + + + +
    +
    QHash<QString, QString> * lstSqlType = qx::QxClassX::getAllSqlTypeByClassName();
    +lstSqlType->insert("QString", "VARCHAR(255)");
    +lstSqlType->insert("std::string", "VARCHAR(255)");
    +// etc.
    +
    +
    + To modify a SQL type for a specific column of a table, you have to define the new SQL type in the + mapping function of QxOrm library :
    +
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<MyClass> & t)
    +{
    +  //...
    +  IxDataMember * p =  t.data(& MyClass::m_MyProperty, "my_property");
    +  p->setSqlType("VARCHAR(255)");
    +  //...
    +}}
    +
    +
    + For all classes not supported by default by QxOrm library (see the FAQ : How to persist a type without its source code (class from an external + library for example) ?), it's possible to associate a default SQL type using the following + macro (outside all namespace) :
    +
    + + + + + + + +
    +
    QX_REGISTER_TRAIT_GET_SQL_TYPE(MyClass, "my_sql_type")
    +
    +
    +
    +

    + How to use QxValidator module to validate automatically an instance + ?

    + + + + + + + + + +
    + QxValidator module of + QxOrm library provides a validation engine for classes registered in QxOrm context.
    + To use this validation engine, you have to define your constraints into the mapping function per class + : void qx::register_class.
    + If for an instance of class, at least one constraint violation is detected, then the instance is + invalid : the object cannot be saved into database (INSERT or UPDATE).
    +
    + It's also possible to use QxValidator module to validate an instance on the presentation layer + : if some datas from a user are invalids, an error message can be displayed, and it's not necessary to + try to send the instance to the data access layer.
    + The validation mechanism can be executed in different layers in your application without having to + duplicate any of these rules (presentation layer, data access layer).
    +
    + Here is a description of some classes defined into QxValidator module : + + QxValidator module manages automatically class inheritance : each constraint defined into a + base class is checked during validation process of a derived class.
    +
    + Here is an example using QxValidator module with a 'person' class :
    +
    + * 'person.h' file :
    + + + + + + + +
    +
    #ifndef _CLASS_PERSON_H_
    +#define _CLASS_PERSON_H_
    + 
    +class person
    +{
    +
    +public:
    +
    +   enum sex { male, female, unknown };
    +
    +   long        _id;
    +   QString     _firstName;
    +   QString     _lastName;
    +   QDateTime   _birthDate;
    +   sex         _sex;
    +
    +   person() : _id(0), _sex(unknown) { ; }
    +   person(long id) : _id(id), _sex(unknown) { ; }
    +   virtual ~person() { ; }
    +
    +private:
    +
    +   void isValid(qx::QxInvalidValueX & invalidValues);
    +
    +};
    +
    +QX_REGISTER_HPP_MY_EXE(person, qx::trait::no_base_class_defined, 0)
    +
    +#endif // _CLASS_PERSON_H_
    +
    +
    + * 'person.cpp' file :
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/person.h"
    +#include "../include/global_validator.h"
    +
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_MY_EXE(person)
    +
    +namespace qx {
    +template <> void register_class(QxClass<person> & t)
    +{
    +   t.id(& person::_id, "id");
    +
    +   t.data(& person::_firstName, "firstName");
    +   t.data(& person::_lastName, "lastName");
    +   t.data(& person::_birthDate, "birthDate");
    +   t.data(& person::_sex, "sex");
    +
    +   QxValidatorX<person> * pAllValidator = t.getAllValidator();
    +   pAllValidator->add_NotEmpty("firstName");
    +   pAllValidator->add_NotEmpty("lastName", "a person must have a lastname");
    +   pAllValidator->add_CustomValidator(& person::isValid);
    +   pAllValidator->add_CustomValidator_QVariant(& validateFirstName, "firstName");
    +   pAllValidator->add_CustomValidator_DataType<QDateTime>(& validateDateTime, "birthDate");
    +}}
    +
    +void person::isValid(qx::QxInvalidValueX & invalidValues)
    +{
    +   // This method is called automatically by 'QxValidator' module (validator engine of QxOrm library) :
    +   // - when you try to insert or update using 'qx::dao::xxx' functions
    +   // - when you call 'qx::validate()' function
    +
    +   // For registration, see 'pAllValidator->add_CustomValidator(& person::isValid);' into 'qx::register_class' function
    +
    +   // Here, you can verify some values of your instance
    +   // If a value is not valid, you must add an invalid value into the collection 'invalidValues'
    +
    +   // For example, if we want to check property '_sex' of a person :
    +   if ((_sex != male) && (_sex != female))
    +   { invalidValues.insert("person sex must be defined : male or female"); }
    +}
    +
    +
    + * 'global_validator.h' file :
    + + + + + + + +
    +
    // Example of global functions 'validateFirstName' and 'validateDateTime' used by 'QxValidator' module
    +// Those functions will be called automatically by validator engine of QxOrm library :
    +// - when you try to insert or update using 'qx::dao::xxx' functions
    +// - when you call 'qx::validate()' function
    + 
    +void validateFirstName(const QVariant & value, const qx::IxValidator * validator, qx::QxInvalidValueX & invalidValues)
    +{
    +   // Here you can test the value (converted to QVariant type)
    +   // If an invalid value is detected, just add a message into 'invalidValues' collection
    +
    +   // For example, if the value must be never equal to "admin" :
    +   if (value.toString() == "admin")
    +   { invalidValues.insert("value must not be equal to 'admin'"); }
    +}
    +
    +void validateDateTime(const QDateTime & value, const qx::IxValidator * validator, qx::QxInvalidValueX & invalidValues)
    +{
    +   // Here you can test the value (with its real type, in this example, the data-member is a 'QDateTime' type)
    +   // If an invalid value is detected, just add a message into 'invalidValues' collection
    +
    +   // For example, if the date-time must be valid :
    +   if (! value.isValid())
    +   { invalidValues.insert("date-time value must not be empty and must be valid"); }
    +}
    +
    +
    + * 'main.cpp' file :
    + + + + + + + +
    +
    person personValidate;
    +personValidate._lastName = "admin";
    +qx::QxInvalidValueX invalidValues = qx::validate(personValidate);
    +QString sInvalidValues = invalidValues.text();
    +qDebug("[QxOrm] test 'QxValidator' module :\n%s", qPrintable(sInvalidValues));
    +
    +
    + During program execution of this code, 'personValidate' instance is not valid : + 'invalidValues' collection contains 4 items :
    + - "property 'firstName' must not be empty" ;
    + - "person sex must be defined : male or female" ;
    + - "value must not be equal to 'admin'" ;
    + - "date-time value must not be empty and must be valid".
    +
    + QxValidator module provides some built-in constraints, which cover most of the basic data + checks.
    + As we'll see later, you're not limited to them, you can literally in a minute write your own + constraints : +
      +
    • add_NotNull() : checks if the value is not null ;
    • +
    • add_NotEmpty() : checks if the string is not empty ;
    • +
    • add_MinValue() : checks if the value is more than or equals to min ;
    • +
    • add_MaxValue() : checks if the value is less than or equals to max ;
    • +
    • add_Range() : checks if the value is between Min and Max (included) ;
    • +
    • add_MinDecimal() : checks if the decimal value is more than or equals to min ;
    • +
    • add_MaxDecimal() : checks if the decimal value is less than or equals to max ;
    • +
    • add_RangeDecimal() : checks if the decimal value is between Min and Max (included) ;
    • +
    • add_MinLength() : checks if the string length is more than or equals to min ;
    • +
    • add_MaxLength() : checks if the string length is less than or equals to max ;
    • +
    • add_Size() : checks if the string length is between the min-max range ;
    • +
    • add_DatePast() : checks if the date is in the past ;
    • +
    • add_DateFuture() : checks if the date is in the future ;
    • +
    • add_RegExp() : checks if the property matches the regular expression given a match flag ; +
    • +
    • add_EMail() : checks whether the string conforms to the email address specification.
    • +
    + Like 'person' class example, it's possible to define a custom validator : it's a function or a + class method called automatically by QxValidator module to validate a property or an instance + of class.
    + There are 3 kinds of custom validator : +
      +
    • add_CustomValidator() : class method, method signature must be "void + my_class::my_method(qx::QxInvalidValueX &)" ;
    • +
    • add_CustomValidator_QVariant() : global function with QVariant type (the property + is converted into QVariant type), function signature must be "void my_validator(const + QVariant &, const qx::IxValidator *, qx::QxInvalidValueX &)" ;
    • +
    • add_CustomValidator_DataType() : global function with real type, function signature must + be "void my_validator(const T &, const qx::IxValidator *, qx::QxInvalidValueX &)" ;
    • +
    + Note : each validator can be associated with a group (optional parameter for each function + add_XXX() of qx::IxValidatorX class).
    + So it's possible to create a context validation during program execution : for example, a person from + IHM A can have different validation rules than a person from IHM B.
    + To execute a validation process by group (for example "myGroup"), you have to call the + following function : "qx::QxInvalidValueX invalidValues = qx::validate(personValidate, + "myGroup");".
    +
    + Other note : QxValidator module provides default messages when a constraint violation is + detected.
    + It's possible to modify those default messages (for example, a traduction) using the following + collection : "QHash * lstMessage = QxClassX::getAllValidatorMessage();".
    + For example : "lstMessage->insert("min_value", "la valeur '%NAME%' doit �tre inf�rieure ou �gale � + '%CONSTRAINT%'");".
    + %NAME% and %CONSTRAINT% fields will be automatically replaced by the good value.
    + To modify a message for a specific validator (and not globally), you have to use the optional + parameter provided by each function add_XXX() of qx::IxValidatorX class.
    +
    +

    + How to use qx::IxPersistable interface ?

    + + + + + + + + + +
    + qx::IxPersistable + interface (or abstract class) provides only pure virtual methods.
    + Using qx::IxPersistable, you will have a common base class to call all persistents functions + without knowing the real type of current instance (polymorphism concept).
    + QxOrm library doesn't force developers to work with a base class to register a persistent type into + QxOrm context, however it's sometimes useful to have an interface to write some generic + algorithms.
    +
    + qx::IxPersistable class provides following virtual methods (for more details about those + methods, goto QxOrm library online class + documentation) :
    +
    +
    +
    virtual long qxCount(const qx::QxSqlQuery & query = qx::QxSqlQuery(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxFetchById(const QVariant & id = QVariant(), const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxFetchAll(qx::IxCollection & list, const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxFetchByQuery(const qx::QxSqlQuery & query, qx::IxCollection & list, const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxInsert(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxUpdate(const qx::QxSqlQuery & query = qx::QxSqlQuery(), const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxSave(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDeleteById(const QVariant & id = QVariant(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDeleteAll(QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDeleteByQuery(const qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDestroyById(const QVariant & id = QVariant(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDestroyAll(QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDestroyByQuery(const qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL);
    +virtual qx_bool qxExist(const QVariant & id = QVariant(), QSqlDatabase * pDatabase = NULL);
    +virtual qx::QxInvalidValueX qxValidate(const QStringList & groups = QStringList());
    +virtual qx::IxPersistableCollection_ptr qxNewPersistableCollection() const;
    +virtual qx::IxClass * qxClass() const;
    +
    +
    + For example, working with a list of qx::IxPersistable pointers, it's possible to save all items + into many tables of database, like this :
    +
    + + + + + + + +
    +
    QList<qx::IxPersistable *> lst = ...;
    +foreach(qx::IxPersistable * p, lst)
    +{
    +   QSqlError daoError = p->qxSave();
    +   if (daoError.isValid()) { /* an error occured */ }
    +   // etc...
    +}
    +
    +
    + To implement qx::IxPersistable interface, it's necessary to : +
      +
    • inherit persistent class from qx::IxPersistable ;
    • +
    • into class definition (myClass.h for example), add QX_PERSISTABLE_HPP(myClass) + macro ;
    • +
    • into class implementation (myClass.cpp for example), add + QX_PERSISTABLE_CPP(myClass) macro. +
    • +
    + For example, to implement qx::IxPersistable interface for author class from qxBlog tutorial, you have to write + :
    +
    + + + + + + + +
    +
    #ifndef _QX_BLOG_AUTHOR_H_
    +#define _QX_BLOG_AUTHOR_H_
    +
    +class blog;
    +
    +class QX_BLOG_DLL_EXPORT author : public qx::IxPersistable
    +{
    +   QX_PERSISTABLE_HPP(author)
    +public:
    +// -- typedef
    +   typedef boost::shared_ptr<blog> blog_ptr;
    +   typedef std::vector<blog_ptr> list_blog;
    +// -- enum
    +   enum enum_sex { male, female, unknown };
    +// -- properties
    +   QString     m_id;
    +   QString     m_name;
    +   QDate       m_birthdate;
    +   enum_sex    m_sex;
    +   list_blog   m_blogX;
    +// -- contructor, virtual destructor
    +   author() : m_id(0), m_sex(unknown) { ; }
    +   virtual ~author() { ; }
    +// -- methods
    +   int age() const;
    +};
    +
    +QX_REGISTER_PRIMARY_KEY(author, QString)
    +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0)
    +
    +typedef boost::shared_ptr<author> author_ptr;
    +typedef qx::QxCollection<QString, author_ptr> list_author;
    +
    +#endif // _QX_BLOG_AUTHOR_H_
    +
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/author.h"
    +#include "../include/blog.h"
    +
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_BLOG(author)
    +QX_PERSISTABLE_CPP(author)
    +
    +namespace qx {
    +template <> void register_class(QxClass<author> & t)
    +{
    +   t.id(& author::m_id, "author_id");
    +
    +   t.data(& author::m_name, "name");
    +   t.data(& author::m_birthdate, "birthdate");
    +   t.data(& author::m_sex, "sex");
    +
    +   t.relationOneToMany(& author::m_blogX, "list_blog", "author_id");
    +
    +   t.fct_0<int>(& author::age, "age");
    +}}
    +
    +int author::age() const
    +{
    +   if (! m_birthdate.isValid()) { return -1; }
    +   return (QDate::currentDate().year() - m_birthdate.year());
    +}
    +
    +
    +
    + Note : project test from ./test/qxDllSample/dll1/ directory provides a kind of 'super + base class' : qx::QxPersistable class implements qx::IxPersistable + interface and inherits from QObject.
    + So SIGNAL-SLOT concept from Qt library can be used with this class and could be an interesting + way to use QxOrm triggers.
    + qx::QxPersistable class provides also some virtual methods to override to manage for example + data validation process from QxValidator module.
    + For information, qx::QxPersistable class is not a part of QxOrm library, but you can copy-past + it into your own project to use all its features : + +
    +

    + How to use relationship engine to fetch datas from many tables ?

    + + + + + + + + + +
    + QxOrm library supports 4 kind of relationships to link C++ classes registered in QxOrm context : + one-to-one, one-to-many, many-to-one and many-to-many.
    + For more details to define relationships, you can take a look at qxBlog tutorial.
    + We will explain here how to fetch datas from many tables (QxDao module, functions of qx::dao namespace) : + + The first parameter of fetch_by_id_with_relation, fetch_all_with_relation and fetch_by_query_with_relation functions is the list of relationships to + fetch.
    + This list of relationships can contain those items : +
      +
    • relation key : each relation is associated to a key defined into + qx::register_class<T> setting function ; +
    • +
    • "*" keyword means "fetch all relationships defined into + qx::register_class<T> function (1 level of relationships)" ;
    • +
    • "->" keyword means "LEFT OUTER JOIN" join type between 2 tables ;
    • +
    • ">>" keyword means "INNER JOIN" join type between 2 tables.
    • +
    + Note : using "*" keyword to indicate "fetch all relationships defined into + qx::register_class<T> function", those calls are similar : +
      +
    • qx::dao::fetch_by_id_with_relation("*", ...) == + qx::dao::fetch_by_id_with_all_relation(...) ; +
    • +
    • qx::dao::fetch_by_query_with_relation("*", ...) == + qx::dao::fetch_by_query_with_all_relation(...) ; +
    • +
    • qx::dao::fetch_all_with_relation("*", ...) == + qx::dao::fetch_all_with_all_relation(...). +
    • +
    +
    + Example : from qxBlog tutorial, it's possible to fetch all those datas with only 1 query :
    +
    + 1- fetch a blog and its author ;
    + 2- for the author fetched, fetch all blog he wrote ;
    + 3- for each blog written by author fetched, fetch all comment + associated.
    +
    + + + + + + + +
    +
    blog_ptr my_blog = blog_ptr(new blog(10));
    +QSqlError daoError = qx::dao::fetch_by_id_with_relation("author_id->list_blog->list_comment", my_blog);
    +
    +
    + This code builds the following SQL query : +
    +
    SELECT blog.blog_id AS blog_blog_id_0, blog.blog_text AS blog_blog_text_0, blog.date_creation AS blog_date_creation_0, blog.author_id AS blog_author_id_0, 
    +       author_1.author_id AS author_1_author_id_0, author_1.name AS author_1_name_0, author_1.birthdate AS author_1_birthdate_0, author_1.sex AS author_1_sex_0, 
    +       blog_2.blog_id AS blog_2_blog_id_0, blog_2.author_id AS blog_2_author_id_0, blog_2.blog_text AS blog_2_blog_text_0, blog_2.date_creation AS blog_2_date_creation_0, 
    +       comment_4.comment_id AS comment_4_comment_id_0, comment_4.blog_id AS comment_4_blog_id_0, comment_4.comment_text AS comment_4_comment_text_0, comment_4.date_creation AS comment_4_date_creation_0 
    +FROM blog 
    +LEFT OUTER JOIN author author_1 ON author_1.author_id = blog.author_id 
    +LEFT OUTER JOIN blog blog_2 ON blog_2.author_id = author_1.author_id 
    +LEFT OUTER JOIN comment comment_4 ON comment_4.blog_id = blog_2.blog_id 
    +WHERE blog.blog_id = :blog_id
    +
    +

    + Another example : it's also possible to create a list of relationships to fetch, like this + :
    +
    + + + + + + + +
    +
    blog_ptr my_blog = blog_ptr(new blog(10));
    +QStringList relation;
    +relation << "author_id->list_blog->list_comment";
    +relation << "author_id->list_blog->list_category";
    +relation << "list_comment";
    +relation << "list_category";
    +QSqlError daoError = qx::dao::fetch_by_id_with_relation(relation, my_blog);
    +
    +
    + This code builds the following SQL query : +
    +
    SELECT blog.blog_id AS blog_blog_id_0, blog.blog_text AS blog_blog_text_0, blog.date_creation AS blog_date_creation_0, blog.author_id AS blog_author_id_0, 
    +       author_1.author_id AS author_1_author_id_0, author_1.name AS author_1_name_0, author_1.birthdate AS author_1_birthdate_0, author_1.sex AS author_1_sex_0, 
    +       blog_2.blog_id AS blog_2_blog_id_0, blog_2.author_id AS blog_2_author_id_0, blog_2.blog_text AS blog_2_blog_text_0, blog_2.date_creation AS blog_2_date_creation_0, 
    +       category_5.category_id AS category_5_category_id_0, category_5.name AS category_5_name_0, category_5.description AS category_5_description_0, 
    +       comment_6.comment_id AS comment_6_comment_id_0, comment_6.blog_id AS comment_6_blog_id_0, comment_6.comment_text AS comment_6_comment_text_0, comment_6.date_creation AS comment_6_date_creation_0, 
    +       category_7.category_id AS category_7_category_id_0, category_7.name AS category_7_name_0, category_7.description AS category_7_description_0 
    +FROM blog 
    +LEFT OUTER JOIN author author_1 ON author_1.author_id = blog.author_id 
    +LEFT OUTER JOIN blog blog_2 ON blog_2.author_id = author_1.author_id 
    +LEFT OUTER JOIN category_blog category_blog_5 ON blog_2.blog_id = category_blog_5.blog_id 
    +LEFT OUTER JOIN category category_5 ON category_blog_5.category_id = category_5.category_id 
    +LEFT OUTER JOIN comment comment_6 ON comment_6.blog_id = blog.blog_id 
    +LEFT OUTER JOIN category_blog category_blog_7 ON blog.blog_id = category_blog_7.blog_id 
    +LEFT OUTER JOIN category category_7 ON category_blog_7.category_id = category_7.category_id 
    +WHERE blog.blog_id = :blog_id
    +
    +

    + Another example : to fetch all relationships per level, "*" keyword must be used.
    + So to fetch all relationships on 3 levels, we can write :
    +
    + + + + + + + +
    +
    blog_ptr my_blog = blog_ptr(new blog(10));
    +QSqlError daoError = qx::dao::fetch_by_id_with_relation("*->*->*", my_blog);
    +
    +
    + This code builds the following SQL query : +
    +
    SELECT blog.blog_id AS blog_blog_id_0, blog.blog_text AS blog_blog_text_0, blog.date_creation AS blog_date_creation_0, blog.author_id AS blog_author_id_0, 
    +       author_1.author_id AS author_1_author_id_0, author_1.name AS author_1_name_0, author_1.birthdate AS author_1_birthdate_0, author_1.sex AS author_1_sex_0, 
    +       blog_2.blog_id AS blog_2_blog_id_0, blog_2.author_id AS blog_2_author_id_0, blog_2.blog_text AS blog_2_blog_text_0, blog_2.date_creation AS blog_2_date_creation_0, blog_2.author_id AS blog_2_author_id_0_2, 
    +       author_3.author_id AS author_3_author_id_0, author_3.name AS author_3_name_0, author_3.birthdate AS author_3_birthdate_0, author_3.sex AS author_3_sex_0, 
    +       comment_4.comment_id AS comment_4_comment_id_0, comment_4.blog_id AS comment_4_blog_id_0, comment_4.comment_text AS comment_4_comment_text_0, comment_4.date_creation AS comment_4_date_creation_0, 
    +       category_5.category_id AS category_5_category_id_0, category_5.name AS category_5_name_0, category_5.description AS category_5_description_0, 
    +       comment_6.comment_id AS comment_6_comment_id_0, comment_6.blog_id AS comment_6_blog_id_0, comment_6.comment_text AS comment_6_comment_text_0, comment_6.date_creation AS comment_6_date_creation_0, comment_6.blog_id AS comment_6_blog_id_0_6, 
    +       blog_7.blog_id AS blog_7_blog_id_0, blog_7.blog_text AS blog_7_blog_text_0, blog_7.date_creation AS blog_7_date_creation_0, blog_7.author_id AS blog_7_author_id_0_7, 
    +       author_8.author_id AS author_8_author_id_0, author_8.name AS author_8_name_0, author_8.birthdate AS author_8_birthdate_0, author_8.sex AS author_8_sex_0, 
    +       comment_9.comment_id AS comment_9_comment_id_0, comment_9.blog_id AS comment_9_blog_id_0, comment_9.comment_text AS comment_9_comment_text_0, comment_9.date_creation AS comment_9_date_creation_0, 
    +       category_10.category_id AS category_10_category_id_0, category_10.name AS category_10_name_0, category_10.description AS category_10_description_0, 
    +       category_11.category_id AS category_11_category_id_0, category_11.name AS category_11_name_0, category_11.description AS category_11_description_0, 
    +       blog_12.blog_id AS blog_12_blog_id_0, blog_12.blog_text AS blog_12_blog_text_0, blog_12.date_creation AS blog_12_date_creation_0, blog_12.author_id AS blog_12_author_id_0_12, 
    +       author_13.author_id AS author_13_author_id_0, author_13.name AS author_13_name_0, author_13.birthdate AS author_13_birthdate_0, author_13.sex AS author_13_sex_0, 
    +       comment_14.comment_id AS comment_14_comment_id_0, comment_14.blog_id AS comment_14_blog_id_0, comment_14.comment_text AS comment_14_comment_text_0, comment_14.date_creation AS comment_14_date_creation_0, 
    +       category_15.category_id AS category_15_category_id_0, category_15.name AS category_15_name_0, category_15.description AS category_15_description_0 
    +FROM blog 
    +LEFT OUTER JOIN author author_1 ON author_1.author_id = blog.author_id 
    +LEFT OUTER JOIN blog blog_2 ON blog_2.author_id = author_1.author_id 
    +LEFT OUTER JOIN author author_3 ON author_3.author_id = blog_2.author_id 
    +LEFT OUTER JOIN comment comment_4 ON comment_4.blog_id = blog_2.blog_id 
    +LEFT OUTER JOIN category_blog category_blog_5 ON blog_2.blog_id = category_blog_5.blog_id 
    +LEFT OUTER JOIN category category_5 ON category_blog_5.category_id = category_5.category_id 
    +LEFT OUTER JOIN comment comment_6 ON comment_6.blog_id = blog.blog_id 
    +LEFT OUTER JOIN blog blog_7 ON blog_7.blog_id = comment_6.blog_id 
    +LEFT OUTER JOIN author author_8 ON author_8.author_id = blog_7.author_id 
    +LEFT OUTER JOIN comment comment_9 ON comment_9.blog_id = blog_7.blog_id 
    +LEFT OUTER JOIN category_blog category_blog_10 ON blog_7.blog_id = category_blog_10.blog_id 
    +LEFT OUTER JOIN category category_10 ON category_blog_10.category_id = category_10.category_id 
    +LEFT OUTER JOIN category_blog category_blog_11 ON blog.blog_id = category_blog_11.blog_id 
    +LEFT OUTER JOIN category category_11 ON category_blog_11.category_id = category_11.category_id 
    +LEFT OUTER JOIN category_blog category_blog_12 ON category_11.category_id = category_blog_12.category_id 
    +LEFT OUTER JOIN blog blog_12 ON category_blog_12.blog_id = blog_12.blog_id 
    +LEFT OUTER JOIN author author_13 ON author_13.author_id = blog_12.author_id 
    +LEFT OUTER JOIN comment comment_14 ON comment_14.blog_id = blog_12.blog_id 
    +LEFT OUTER JOIN category_blog category_blog_15 ON blog_12.blog_id = category_blog_15.blog_id 
    +LEFT OUTER JOIN category category_15 ON category_blog_15.category_id = category_15.category_id 
    +WHERE blog.blog_id = :blog_id
    +
    +
    +
    +

    + How to execute a stored procedure or a custom SQL query ?

    + + + + + + + + + +
    + QxOrm library provides 2 functions to execute a stored procedure or a custom SQL query : + + The first parameter of those functions, of qx::QxSqlQuery type (or qx_query), contains the stored procedure or + the custom SQL query to execute.
    + For more informations about qx::QxSqlQuery class, goto here : How to + build a query without writing SQL with the class qx::QxSqlQuery ?
    +
    + qx::dao::execute_query<T>() function is a template function : T + type must be registered in QxOrm context (qx::register_class<T> function).
    + All datas returned by the stored procedure or the custom SQL query which could be associated with + members of the C++ class (of T type) will be fetched automatically.
    + An automatic search is done on the name of each fields returned by the query.
    + Here is an example from qxBlog project of QxOrm package :
    +
    + + + + + + + +
    +
    // Call a custom SQL query or a stored procedure and fetch automatically properties (with a collection of items)
    +qx_query testStoredProcBis("SELECT * FROM author");
    +daoError = qx::dao::execute_query(testStoredProcBis, authorX);
    +qAssert(! daoError.isValid()); qAssert(authorX.count() > 0);
    +qx::dump(authorX);
    +
    +

    + qx::dao::call_query() function + is not a template function : you have to iterate over each result using qx::QxSqlQuery class + (or qx_query).
    + To get an output value parameter (must be pass as QSql::Out or QSql::InOut) returned by + a stored procedure, just call the following method : QVariant qx::QxSqlQuery::boundValue(const + QString & sKey) const;.
    +
    + To iterate over all resultset, just use the following methods :
    + * long qx::QxSqlQuery::getSqlResultRowCount() const;
    + * long qx::QxSqlQuery::getSqlResultColumnCount() const;
    + * QVariant qx::QxSqlQuery::getSqlResultAt(long row, long column) const;
    + * QVariant qx::QxSqlQuery::getSqlResultAt(long row, const QString & column) const;
    + * QVector qx::QxSqlQuery::getSqlResultAllColumns() const;
    + * void qx::QxSqlQuery::dumpSqlResult();
    +
    + Here is an example using qx::dao::call_query() function :
    +
    + + + + + + + +
    +
    qx_query query("CALL MyStoredProc(:param1, :param2)");
    +query.bind(":param1", "myValue1");
    +query.bind(":param2", 5024, QSql::InOut);
    +QSqlError daoError = qx::dao::call_query(query);
    +QVariant vNewValue = query.boundValue(":param2");
    +query.dumpSqlResult();
    +
    +
    +
    +

    + How to use qx::QxDaoAsync class to execute queries in asynchronous way + (multi-thread) ?

    + + + + + + + + + +
    + Sometimes, it's necessary to execute some queries to database in asynchronous way (multi-thread), for + example to avoid to freeze a GUI if a query is too long to execute.
    + To make easier to work with asynchronous queries, QxOrm library provides qx::QxDaoAsync + class.
    + This class executes a query in another thread and returns the queryFinished() SIGNAL + when query is terminated.
    + To use qx::QxDaoAsync class, you just have to : +
      +
    • be careful to work only with classes implementing qx::IxPersistable interface ;
    • +
    • create an instance of qx::QxDaoAsync type (for example, a property of a QWidget + derived class) ;
    • +
    • connect a SLOT to the qx::QxDaoAsync::queryFinished() SIGNAL (for example, + a SLOT of a QWidget derived class) ;
    • +
    • run a query using one of qx::QxDaoAsync::asyncXXXX() methods.
    • +
    + Here is an example with a class called MyWidget :
    +
    + + + + + + + +
    +
    class MyWidget : public QWidget
    +{ Q_OBJECT
    +
    +   //...
    +   qx::QxDaoAsync m_daoAsync;
    +   //...
    +Q_SLOTS:
    +   void onQueryFinished(const QSqlError & daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams);
    +   //...
    +
    +};
    +
    +
    + And here is the implementation of MyWidget class :
    +
    + + + + + + + +
    +
    MyWidget::MyWidget() : QObject()
    +{
    +   //...
    +   QObject::connect((& m_daoAsync), SIGNAL(queryFinished(const QSqlError &, qx::dao::detail::QxDaoAsyncParams_ptr)), 
    +                    this, SLOT(onQueryFinished(const QSqlError &, qx::dao::detail::QxDaoAsyncParams_ptr)));
    +   //...
    +}
    +
    +void MyWidget::onQueryFinished(const QSqlError & daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams)
    +{
    +   if (! pDaoParams) { return; }
    +   qx::QxSqlQuery query = pDaoParams->query;
    +   if (! daoError.isValid()) { ; }
    +   // If the async query is associated to a simple object, just use 'pDaoParams->pInstance' method
    +   qx::IxPersistable_ptr ptr = pDaoParams->pInstance;
    +   // If the async query is associated to a list of objects, just use 'pDaoParams->pListOfInstances' method
    +   qx::IxPersistableCollection_ptr lst = pDaoParams->pListOfInstances;
    +   //...
    +}
    +
    +
    +
    +

    + How to use QxModelView module to interact with Qt model/view + architecture (Qt widgets and/or QML views) ?

    + + + + + + + + + +
    + QxModelView module + provides an easy way to work with Qt + model/view engine with all classes registered in QxOrm context : +
      +
    • Qt widgets : QTableView or QListView for example to display/modify a database + table content ;
    • +
    • QML : each property defined in QxOrm context is exposed to QML engine : QxModelView module + makes easier integration between QML and databases.
    • +
    + qx::IxModel interface provides + a generic way for all models linked to persistents classes registered in QxOrm context. All methods of + this class prefixed by 'qx' call functions from 'qx::dao' namespace and then communicate + with database.
    +
    + The qxBlogModelView sample project in ./test/ directory of QxOrm package shows how to + create quickly a model and associate it to the Qt model/view engine (first with a Qt widget, + then with a QML view).
    +
    + 1- Here is an example to display/modify data from 'author' table (go to qxBlog tutorial for 'author' class + definition) in a QTableView :
    +
    + + + + + + + +
    +
    // Create a model and fetch all data from database
    +qx::IxModel * pModel = new qx::QxModel<author>();
    +pModel->qxFetchAll();
    +
    +// Associate the model to a QTableView and display it
    +QTableView tableView;
    +tableView.setModel(pModel);
    +tableView.show();
    +
    +
    + After executing this code, you will have the following window :
    +
    + qx_model_view_01
    +

    + 2- Here is another example in QML (with Qt5, QxModelView module works fine with Qt4 too) :
    +
    + + + + + + + +
    +
    // Create a model and fetch all data from database
    +qx::IxModel * pModel = new qx::QxModel<author>();
    +pModel->qxFetchAll();
    +
    +// Associate the model to a QML view and display it
    +QQuickView qmlView;
    +qmlView.rootContext()->setContextProperty("myModel", pModel);
    +qmlView.setSource(QUrl("qrc:/documents/main.qml"));
    +qmlView.show();
    +
    +
    + And here is the 'main.qml' file content :
    +
    + + + + + + + +
    +
    import QtQuick 2.1
    +import QtQuick.Controls 1.0
    +
    +Item {
    +   width: 400
    +   height: 300
    +   Row {
    +      height: 20
    +      spacing: 20
    +      Button {
    +         text: "Clear"
    +         onClicked: myModel.clear()
    +      }
    +      Button {
    +         text: "Fetch All"
    +         onClicked: myModel.qxFetchAll_()
    +      }
    +      Button {
    +         text: "Save"
    +         onClicked: myModel.qxSave_()
    +      }
    +   }
    +   ListView {
    +      y: 30
    +      height: 270
    +      model: myModel
    +      delegate: Row {
    +         height: 20
    +         spacing: 10
    +         Text { text: "id: " + author_id }
    +         TextField {
    +            text: name
    +            onTextChanged: name = text
    +         }
    +      }
    +   }
    +}
    +
    +
    + After executing this code, you will have the following window :
    +
    + qx_model_view_02
    +
    + As you can see in the 'main.qml' file, 'author_id' and 'name' properties of + 'author' model (myModel variable) can be automatically read and write (because they are + registered in QxOrm context).
    + Moreover, qx::IxModel + interface provides a list of methods for QML side (Q_INVOKABLE) to communicate with database : + for example, the 'Save' button will save the model in database without having to write a C++ + function.
    +
    + Note : a QxEntityEditor plugin generates automatically the code to manage models with + relationships. Then it is possible to work with nested C++ models.
    +
    +

    +
    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/doc/qxorm_en/home.html b/doc/qxorm_en/home.html new file mode 100644 index 0000000..78ff5be --- /dev/null +++ b/doc/qxorm_en/home.html @@ -0,0 +1,411 @@ + + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic editor + (data model designer and source code generator) + + + + + + + + + + + + + + + + + + + + + +
    QxOrm + + + + Windows + Linux + Macintosh + C++
    +
    + + + + + + + + + + + + + + + + + + +
    HomeDownloadQuick sample + Tutorial (4) + + + + + + +
    + +
    +
    + Manual (2) + + + + + + +
    + +
    +
    ForumOur customers
    +
    + + + + + + + + + + + + + + + + + +
    + QxOrm >> Home + + + + + + + + + + + + + + +
    + Current version :  + + QxOrm 1.5.0 - QxOrm library online class documentation - GitHub +
    + + + QxEntityEditor 1.2.8 +
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    + + + + + + + + + +
    +

    QxOrm (the engine) + QxEntityEditor (the graphic editor) = the best solution to + manage your persistent data layer in C++/Qt !

    +
    +
    +
    QxOrm library is an Object Relational Mapping (ORM) database library for C++/Qt + developers. + With a simple C++ setting function per class (like Hibernate XML mapping file in Java), you have access to the following + features :
    +
      +
    • + Persistence + : support most common databases like SQLite, MySQL, PostgreSQL, Oracle, MS SQL Server, MongoDB (with 1-1, 1-n, n-1 and + n-n relationships) ; +
    • +
    • + Serialization + : JSON, binary and XML format ;
    • +
    • + Reflection + (or + introspection + ) : access dynamically to classes definitions, retrieve properties and call classes methods ; +
    • +
    • + HTTP web server + + : standalone multi-threaded HTTP 1.1 web server (support SSL/TLS, persistent connections, + cookies, sessions, chunked responses, URL dispatcher/routing) ;
    • +
    • + JSON API + : interoperability with other technology than C++/Qt (REST web services, QML applications, + scripting language).
    • +
    +
    +
    qt_ambassador
    + + QxOrm library has been accepted into the Qt Ambassador + Program + +
    +
    + QxEntityEditor is a graphic editor for QxOrm library : QxEntityEditor provides a graphic + way to manage the data model.
    + QxEntityEditor is multi-platform (available for Windows, Linux and Mac OS X) and generates native code + for all environments : desktop (Windows, Linux, Mac OS X), embedded and mobile (Android, iOS, Windows Phone, + Raspberry Pi, etc.).
    + A user manual (documentation) for QxEntityEditor application is + available.
    +
    + QxEntityEditor is based on plugins and provides many ways to import/export your data model : + + QxEntityEditor +

    +
    +
    + QxOrm library is designed to make easier C++ development and provides many functionalities.
    + Here is a list of advantages of QxOrm library : +
      +
    • non intrusive : the C++ setting function per class doesn't modify class definition, QxOrm + can be used in existing projects ;
    • +
    • no XML mapping file ;
    • +
    • classes doesn't need to inherit from a 'super object' ;
    • +
    • template meta-programming : no macro hack ;
    • +
    • works with Visual C++ on Windows, GCC on Linux, Clang on Mac OS X, and MinGW + on Windows (other platforms will be tested soon : smartphones, etc...) ;
    • +
    • only one file <QxOrm.h> to include in precompiled-header (precompiled-header file is + recommended to reduce compilation times).
    • +
    +
    + QxOrm is based on Qt framework (support Qt4, + Qt5, Qt6) : + +

    + + + + + + + + + +
    QtQt : cross-platform application development framework : GUI (QtGui), + network (QtNetwork), XML (QtXml), database (QtSql)...
    + Qt provides excellent support and documentation. Using Qt, you can write simple and powerful C++ + code.
    + Qt is produced by Digia's Qt Development Frameworks division and is available under LGPL license.
    + QxOrm is compatible with many Qt's objects : QObject, QString, QDate, QTime, QDateTime, QList, + QHash, QSharedPointer, QScopedPointer...
    + It is recommended to install the latest version of Qt available at the following address : http://www.qt.io/
    +

    + +
    +

    QxOrm provides the following modules :

    +
      +
    • +

      QxDao : based on Qt QtSql engine, this module + provides communication with databases (select, update, delete, transaction...) by mapping + database table columns with C++ class properties. The relationships 1-1, 1-n, n-1 + and n-n are easy to setup in C++ code. This module supports object-oriented programming : + inheritance, polymorphism, modularity. It supports also many containers (stl, boost and + Qt : std::vector, std::list, std::unordered_map, QList, QHash...). +

      +
    • +
    • +

      QxRegister, QxDataMember, QxFactory and QxFunction : QxOrm + emulates reflection (or introspection) mechanism (like other languages : Java, C#...).

      +
    • +
    • +

      QxModelView : each + class registered in QxOrm context can be used by the Qt model/view engine (Qt widgets and/or QML views). qx::IxModel interface exposes + automatically to QML engine all properties defined in QxOrm context. This module makes easier + interaction between QML and databases.

      +
    • +
    • +

      QxService : based on QtNetwork engine of Qt + library, this module provides an easy and powerful way to create C++ application server + (services concept with request from client and reply from server). QxService + uses serialization and introspection from QxOrm library to transfer over network any class + or structure. For more details about QxService module, a tutorial is available here.

      +
    • +
    • +

      QxCollection<Key, + Value> : + + this thread-safe container has advantages of std::vector<T> (insertion order + quick access + by index) and std::unordered_map<Key, Value> or QHash<Key, Value> (quick + access by a key : hash-map). QxCollection<Key, Value> is adapted to contain the + items resulting from a database. +

      +
    • +
    • +

      QxSerialize : this + module is based on Qt QDataStream class. + + Any class defined by QxOrm can be serialized to binary, XML and JSON format. This module also allows to + clone all instances of objects. Most of Qt objects (QObject, QString, QDate, QTime, QDateTime, QList, + QHash...) are compatible with QxSerialize module. +

      +
    • +
    • +

      QxTraits : C++ standard + library provides several generic traits. QxOrm uses them but needs new functionalities that are not + present in std standard library.

      +
    • +
    • +

      QxCache : QxOrm's cache can + contain all types of objects. It's possible for example to store data resulting from a database to avoid + too frequent requests. This cache is generic and can be used in other contexts than QxOrm.

      +
    • +
    • +

      QxValidator : this + module provides a validation engine for classes registered in QxOrm context. To use this validation + engine, you have to define your constraints into the mapping function per class : void + qx::register_class. If for an instance of class, at least one constraint violation is detected, + then the instance is invalid : the object cannot be saved into database (INSERT or + UPDATE). For more information about QxValidator module, read the manual here. +

      +
    • +
    • +

      QxMemLeak : fast + detection of memory leaks in Debug mode (with indication of file and line => MFC style from + Microsoft). This module is developed by Wu + Yongwei. If another tool is already used in your projects (Valgrind for example), this + functionality should not be enabled. By default, QxMemLeak module is disabled.

      +
    • +
    +

    +
    +

    You can download the current version of QxOrm library and QxEntityEditor application here. +
    A manual (user guide) to learn how to work with QxOrm library is available here. +
    A quick sample showing basic functionalities of QxOrm is available here. +
    If you have any questions about QxOrm library or QxEntityEditor application, a forum is + available here.
    +

    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/doc/qxorm_en/link.html b/doc/qxorm_en/link.html new file mode 100644 index 0000000..35d7198 --- /dev/null +++ b/doc/qxorm_en/link.html @@ -0,0 +1,264 @@ + + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic editor + (data model designer and source code generator) + + + + + + + + + + + + + + + + + + + + + +
    QxOrm + + + + Windows + Linux + Macintosh + C++
    +
    + + + + + + + + + + + + + + + + + + +
    HomeDownloadQuick sample + Tutorial (4) + + + + + + +
    + +
    +
    + Manual (2) + + + + + + +
    + +
    +
    ForumOur customers
    +
    + + + + + + + + + + + + + + + + + +
    + QxOrm >> Forum + + + + + + + + + + + + + + +
    + Current version :  + + QxOrm 1.5.0 - QxOrm library online class documentation - GitHub +
    + + + QxEntityEditor 1.2.8 +
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    + + + + + + + + + +
    + If you find a bug or if you have a question about QxOrm library or QxEntityEditor + software : + + + + + + + + + + +
    + + + QxOrm forum + + QxEntityEditor forum +
    +
    + If you have a question about boost or Qt library : + + + + + + + + + + +
    + + + Qt forum + + boost forum +
    +
    qt_ambassador
    + + QxOrm library has been accepted into the Qt Ambassador + Program + +
    +
    + French web sites to help you to your development software with forums, tutorials, documentations... + +
    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/doc/qxorm_en/manual.html b/doc/qxorm_en/manual.html new file mode 100644 index 0000000..230eff2 --- /dev/null +++ b/doc/qxorm_en/manual.html @@ -0,0 +1,12788 @@ + + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic + editor (data model designer and source code generator) + + + + + + + + + + + + + + + + + + + + + +
    QxOrm + + + Windows + Linux + Macintosh + C++
    +
    + + + + + + + + + + + + + + + + + + +
    HomeDownloadQuick sample + Tutorial (4) + + + + + + +
    + +
    +
    + Manual (2) + + + + + + +
    + +
    +
    ForumOur customers
    +
    + + + + + + + + + + + + + + + + + +
    + QxOrm >> Manual - QxOrm library user guide + + + + + + + + + + + + + + +
    + Current version :  + + QxOrm 1.5.0 - QxOrm library online class documentation - GitHub +
    + + + QxEntityEditor 1.2.8 +
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    + + + + + + + + + + +
    + Select a manual : + + QxOrm user guide + QxEntityEditor user guide +
    +
    +
    + + + + + + + + + +
    +

    QxOrm library manual - Table of Contents

    +
    +
      +
    1. + Introduction +
        +
      1. + QxOrm library +
      2. +
      3. + Quick overview of QxEntityEditor application +
      4. +
      5. + C++ coding style of QxOrm library +
      6. +
      +
    2. +
    3. + Installation +
        +
      1. + Qt dependency +
      2. +
      3. + Boost dependency (optional) +
      4. +
      5. + QxOrm.pri (or QxOrm.cmake) configuration file +
      6. +
      7. + Build QxOrm library (with qmake or CMake) +
      8. +
      9. + Qt SQL drivers (supported databases) +
      10. +
      +
    4. +
    5. + Persistence - Object Relational Mapping (ORM) +
        +
      1. + Register a class in QxOrm context (mapping) +
          +
        1. + Primary key other than default type "long" +
        2. +
        3. + Composite primary key +
        4. +
        5. + public or protected/private data members +
        6. +
        7. + Namespace +
        8. +
        9. + Built-in C++ supported types +
        10. +
        11. + Register a transient data member +
        12. +
        +
      2. +
      3. + Connection to database +
      4. +
      5. + Persist a C++ instance in database (insert/update) +
      6. +
      7. + Delete an instance in database (delete) +
          +
        1. + Soft delete behaviour (logical delete) +
        2. +
        +
      8. +
      9. + Get a C++ instance from database (fetch) +
      10. +
      11. + SQL queries +
          +
        1. + Using qx::QxSqlQuery class (or qx_query + alias) +
        2. +
        3. + Using standard SQL or stored procedure +
        4. +
        +
      12. +
      13. + Transactions (commit, rollback, session) +
      14. +
      15. + Relationships +
          +
        1. + one-to-many (1-n) +
        2. +
        3. + many-to-one (n-1) +
        4. +
        5. + many-to-many (n-n) +
        6. +
        7. + one-to-one (1-1) +
        8. +
        9. + Fetch relationships +
        10. +
        11. + Select columns fetching relationships and define + custom SQL alias +
        12. +
        13. + Add SQL query inside LEFT OUTER JOIN / INNER + JOIN +
        14. +
        +
      16. +
      17. + Supported containers +
          +
        1. + Qt containers +
        2. +
        3. + Boost containers +
        4. +
        5. + std containers +
        6. +
        7. + qx::QxCollection +
        8. +
        +
      18. +
      19. + Supported smart pointers +
          +
        1. + Qt smart pointers +
        2. +
        3. + Boost smart pointers +
        4. +
        5. + std smart pointers +
        6. +
        7. + qx::dao::ptr +
        8. +
        +
      20. +
      21. + Triggers +
      22. +
      23. + Validators +
      24. +
      25. + Manage NULL database value +
          +
        1. + boost::optional +
        2. +
        3. + QVariant +
        4. +
        +
      26. +
      27. + Inheritance and polymorphism +
      28. +
      29. + qx::IxPersistable interface (abstract class) +
      30. +
      31. + Use PIMPL C++ pattern (Private Implementation idiom or + d-pointer) +
      32. +
      33. + Persist custom type +
      34. +
      35. + Generate database DDL SQL schema +
      36. +
      37. + Associate a SQL type to a C++ class +
      38. +
      39. + Async database queries +
      40. +
      41. + Cache to store C++ instances (QxCache module) +
      42. +
      43. + Working with several databases +
      44. +
      45. + Register an abstract class in QxOrm context +
      46. +
      47. + Register automatically Qt meta-properties + (Q_PROPERTY macro) +
      48. +
      +
    6. +
    7. + Serialization +
        +
      1. + Version number to manage ascendant compatibility +
      2. +
      3. + Qt QDataStream engine +
      4. +
      5. + Qt JSON engine +
      6. +
      7. + XML boost serialization +
      8. +
      9. + Binary boost serialization +
      10. +
      11. + Other boost serialization +
      12. +
      13. + Clone a C++ instance +
      14. +
      15. + Dump a C++ instance (XML or JSON format) +
      16. +
      +
    8. +
    9. + Introspection - Reflection +
        +
      1. + Get a data member value dynamically +
      2. +
      3. + Set a data member value dynamically +
      4. +
      5. + Call function dynamically +
      6. +
      7. + Create a C++ instance dynamically +
      8. +
      9. + Iterate over all classes/properties registered in QxOrm + context +
      10. +
      +
    10. +
    11. + Services : transfer persistent data layer over network + (QxService module) +
        +
      1. + Input/output service parameters (request/response) +
      2. +
      3. + Define service functions exposed to clients +
      4. +
      5. + List of options available on server side +
      6. +
      7. + Connection settings on client side +
      8. +
      9. + Service authentication +
      10. +
      11. + Async client/server queries +
      12. +
      +
    12. +
    13. + Model View engine (QxModelView module) +
        +
      1. + Simple model (without relationship) +
      2. +
      3. + Model with relationships (nested models) +
      4. +
      5. + Interaction with QML views +
      6. +
      7. + Interaction with QtWidget views +
      8. +
      9. + Connect model to QxService module +
      10. +
      +
    14. +
    15. + QxOrm and MongoDB database (C++ ODM Object Document Mapper) +
        +
      1. + Prerequisites : driver libmongoc and + libbson +
      2. +
      3. + QxOrm.pri (or QxOrm.cmake) configuration file +
      4. +
      5. + Connection to MongoDB database +
      6. +
      7. + Register a MongoDB persistent class (Collection) in + QxOrm context (mapping) +
          +
        1. + Manage ObjectId (primary key) +
        2. +
        +
      8. +
      9. + Insert a C++ instance (Document) in MongoDB database +
          +
        1. + Insert many C++ instances (list of Documents) in + MongoDB database +
        2. +
        +
      10. +
      11. + Update a C++ instance (Document) in MongoDB database +
          +
        1. + Update many C++ instances (list of Documents) in + MongoDB database +
        2. +
        +
      12. +
      13. + Delete a C++ instance (Document) from MongoDB + database +
          +
        1. + Delete many C++ instances (list of Documents) + from MongoDB database +
        2. +
        +
      14. +
      15. + Fetch a C++ instance (Document) from MongoDB + database +
          +
        1. + Fetch many C++ instances (list of Documents) from + MongoDB database +
        2. +
        +
      16. +
      17. + JSON queries +
          +
        1. + Using qx::QxSqlQuery class (or qx_query + alias) +
        2. +
        3. + Using MongoDB aggregation framework +
        4. +
        5. + Add 'sort', 'limit', 'skip', etc..., properties + to JSON query +
        6. +
        7. + Execute a custom query +
        8. +
        +
      18. +
      19. + Relationships engine (MongoDB version 3.6 or + is + required) +
          +
        1. + Embedded vs Referenced +
        2. +
        +
      20. +
      21. + Create automatically indexes +
      22. +
      +
    16. +
    17. + HTTP/HTTPS web server (QxHttpServer module) +
        +
      1. + Hello World ! +
      2. +
      3. + HTTP/HTTPS web server settings +
          +
        1. + Secured connections SSL/TLS +
        2. +
        +
      4. +
      5. + Routing URL (dispatcher / endpoints) +
          +
        1. + Dynamic URL routing +
        2. +
        +
      6. +
      7. + Get HTTP request parameters +
      8. +
      9. + Build HTTP response +
      10. +
      11. + Sessions (storage per client on server side) +
      12. +
      13. + Cookies +
      14. +
      15. + Static files +
      16. +
      17. + Chunked responses +
      18. +
      19. + Requests using JSON API (QxRestApi module) +
      20. +
      21. + WebSocket +
      22. +
      23. + Performance (tested with Apache Benchmark) +
          +
        1. + Improve performance with epoll dispatcher on + Linux +
        2. +
        +
      24. +
      +
    18. +
    19. + JSON REST API (QxRestApi module) +
        +
      1. + How it works +
          +
        1. + Use cases +
        2. +
        +
      2. +
      3. + qxBlogRestApi example project (QML and HTTP web + server) +
      4. +
      5. + Fetch +
          +
        1. + fetch_all +
        2. +
        3. + fetch_by_id +
        4. +
        5. + fetch_by_query +
        6. +
        7. + count +
        8. +
        9. + exist +
        10. +
        +
      6. +
      7. + Insert +
      8. +
      9. + Update +
      10. +
      11. + Save (insert or update) +
      12. +
      13. + Delete +
          +
        1. + delete_all / destroy_all +
        2. +
        3. + delete_by_query / destroy_by_query +
        4. +
        5. + delete_by_id / destroy_by_id +
        6. +
        +
      14. +
      15. + Validate +
      16. +
      17. + Custom SQL query or stored procedure +
      18. +
      19. + Call C++ natives functions +
      20. +
      21. + Meta-data (C++ classes registered into QxOrm + context) +
      22. +
      23. + Send a list of JSON requests +
      24. +
      +
    20. +
    +
    +
    qt_ambassador
    + + QxOrm library has been accepted into the Qt + Ambassador Program + +
    +
    +
    +
    + +

    Introduction

    +
    + The goal of this documentation is to provide a user guide to learn how to work with QxOrm + library features. + This manual is intended for developers and software architects who are looking for a solution to + manage a persistent data layer in C++/Qt. + Technical skills in C++ and databases are required to understand this document. +

    + Note : all features described in this manual/user guide can be defined quickly and easily + with QxEntityEditor application (the graphic editor for QxOrm library, data model + designer and source code generator). + Another documentation dedicated to QxEntityEditor application + is available on QxOrm website. +

    + Other note : this manual is based on the old FAQ of QxOrm website, you + can always access to this FAQ here. +

    +

    QxOrm library

    +
    + QxOrm is a C++ library designed to provide Object Relational Mapping (ORM) feature to + C++ users.
    + QxOrm is developed by XDL Team, a software development engineer since 2003.
    +

    + Based on a simple C++ setting function per class (like Hibernate XML mapping file in Java), QxOrm library provides + following features : +
      +
    • + Persistence + + : support most common databases like SQLite, MySQL, PostgreSQL, Oracle, MS SQL + Server, MongoDB (with 1-1, 1-n, + n-1 and n-n relationships) ; +
    • +
    • + Serialization + : JSON, binary and XML format ;
    • +
    • + Reflection + + (or + introspection + ) : access dynamically to classes definitions, retrieve properties and call classes + methods ;
    • +
    • + HTTP web + server + : standalone multi-threaded HTTP 1.1 web server (support SSL/TLS, persistent + connections, cookies, sessions, chunked responses, URL dispatcher/routing) ;
    • +
    • + JSON API + + : interoperability with other technology than C++/Qt (REST web services, QML + applications, scripting language).
    • +
    + QxOrm depends on Qt (from version 4.5.0) + and boost (from version 1.38, and by + default just header files *.hpp are necessary).
    + QxOrm library has been accepted into the Qt Ambassador + Program. +

    + If you find a bug or if you have a question about QxOrm library, you can send an e-mail + to : support@qxorm.com.
    + A forum dedicated to QxOrm library is available here.
    + If you speak/understand french, you can access to the french community on the famous developpez.com forum. +

    +
    +

    Quick overview of + QxEntityEditor application

    +
    + QxEntityEditor is a graphic editor for QxOrm library : QxEntityEditor + provides a graphic way to manage the data model.
    + QxEntityEditor is multi-platform (available for Windows, Linux and Mac OS X) and + generates native code for all environments : desktop (Windows, Linux, Mac OS X), embedded and + mobile (Android, iOS, Windows Phone, Raspberry Pi, etc.).
    + A presentation video of QxEntityEditor application is + available on YouTube.
    +
    + QxEntityEditor is based on plugins and provides many ways to import/export your data + model : +
      +
    • generate C++ persistent classes automatically (registered in QxOrm context) ;
    • +
    • generate DDL SQL script automatically (database schema) for SQLite, MySQL, PostgreSQL, + Oracle and MS SQL Server ;
    • +
    • manage schema evolution for each project version (ALTER TABLE, ADD COLUMN, + DROP INDEX, etc.) ; +
    • +
    • transfer your data model over network and create quickly client/server applications, + using QxService + module ;
    • +
    • import existing database structure (using ODBC connection) for SQLite, MySQL, PostgreSQL, + Oracle and MS SQL Server databases ;
    • +
    • because each project is different, QxEntityEditor provides several ways to customize + generated files (especially a javascript engine and an integrated debugger).
    • +
    + QxEntityEditor +

    + QxEntityEditor is developed by XDL Team, a software development engineer since 2003.
    + A manual dedicated to QxEntityEditor application is + available on QxOrm website. +

    +
    +

    C++ coding style of QxOrm + library

    +
    + QxOrm library uses following syntax and naming convention for C++ source code : +
      +
    • all classes, functions, properties, etc... are defined under namespace qx ; +
    • +
    • all macros of QxOrm library are prefixed by QX_... ; +
    • +
    • all abstracts classes (or interfaces) start with prefix Ix (for example + IxFactory is an interface to create an instance of object) ; +
    • +
    • other classes start with prefix Qx (for example QxDataMember) ; +
    • +
    • containers of objects end with suffix X (for example QxDataMemberX is a + list of QxDataMember) ; +
    • +
    • functions to interact with databases are under namespace qx::dao (for example + qx::dao::fetch_by_id()) ; +
    • +
    • functions to serialize are under namespace + qx::serialization (for example qx::serialization::xml::to_file()) ; +
    • +
    • the reflection (or introspection) engine can be used with + qx::QxClassX class (for example qx::QxClassX::invoke() to call a + class method) ; +
    • +
    • all traits classes are under namespace qx::trait (for + example qx::trait::is_smart_ptr<T>). +
    • +
    +
    +
    + +

    Installation

    +
    + QxOrm library is multi-platform and can be installed on all environments : Windows, Linux, + Mac OS X, Android, iOS, Windows Phone, Raspberry Pi, etc...
    + A full tutorial (with screenshots) to install a development environment + with QxOrm on Windows is available here. +

    + The goal of this chapter is to explain all steps required to install QxOrm library on all + environments : + +
    +

    Qt dependency

    +
    + + + + + + + + + +
    QtQt : cross-platform application development framework : GUI + (QtGui), network (QtNetwork), XML (QtXml), database + (QtSql)...
    + Qt provides excellent support and documentation. Using Qt, you can write simple and + powerful C++ code.
    + Qt is produced by Digia's Qt Development Frameworks division and is available under + LGPL license.
    + QxOrm is compatible with many Qt's objects : QObject, QString, QDate, QTime, + QDateTime, QList, QHash, QSharedPointer, QScopedPointer...
    + It is recommended to install the latest version of Qt available at the following + address : http://www.qt.io/
    +
    + Note : by default, QxOrm library depends only on QtCore and QtSql binaries. + It is possible to enable extra features in the QxOrm.pri (or + QxOrm.cmake) configuration file : some features can add dependencies to QxOrm + library. +

    +
    +

    Boost dependency + (optional)

    +
    + By default, QxOrm library depends only on Qt (QtCore and QtSql). + boost installation is optional and not required with default configuration. +
    + Note : QxOrm provides 2 dependency levels on boost : +
      +
    • dependency only on boost headers files (*.hpp) : _QX_ENABLE_BOOST + compilation option ;
    • +
    • dependency on boost serialization module : _QX_ENABLE_BOOST_SERIALIZATION + compilation option.
    • +
    +
    + + + + + + + + + +
    boostboost : many of boost's founders are on the C++ standard + committee and several boost libraries have been accepted for incorporation into + C++1x (new standard for the C++ programming language). + The boost's libraries are aimed at a wide range of C++ users and application + domains.
    + QxOrm uses the following boost's features (header files *.hpp only, boost + serialization dependency is optional) : smart_pointer, type_traits, + multi_index_container, unordered_container, any, tuple, foreach, + function.
    + It is recommended to get the latest version of boost available at the following + address : http://www.boost.org/ +
    +
    + Important note : with _QX_ENABLE_BOOST compilation option, QxOrm library + depends only on *.hpp boost header files (header only libraries). + So in this case, boost installation is very easy because you just have to unzip boost package + (to get *.hpp header files, there is nothing to build). +

    +
    +

    QxOrm.pri (or + QxOrm.cmake) configuration file

    +
    + QxOrm.pri (or QxOrm.cmake) configuration file is divided into several sections + (each section is commented) and provides all settings and compilation options available to + customize QxOrm library features. + So it is strongly recommended to read carefully QxOrm.pri configuration file before + compiling and building QxOrm library. + It is possible to keep default settings, only QX_BOOST_INCLUDE_PATH variable is required + if you work with boost : this variable is used to define where *.hpp boost header files + are located :
    +
    + + + + + + + +
    +
       isEmpty(QX_BOOST_INCLUDE_PATH) { QX_BOOST_INCLUDE_PATH = $$quote(D:/Dvlp/_Libs/Boost/1_57/include) }   
    +
    +
    + If you don't want to change QxOrm.pri configuration file, it is possible to define an + environment variable named BOOST_INCLUDE : this environment variable will be used + automatically to set QX_BOOST_INCLUDE_PATH value (read QxOrm.pri configuration + file for more details). +

    + Here is a list of compilation options available in QxOrm.pri configuration file, by + default they are all disabled : +
      +
    • _QX_ENABLE_BOOST : add a dependency to boost headers files (*.hpp), support + some classes like : boost::shared_ptr, boost::optional, + boost::container, etc... ; +
    • +
    • _QX_ENABLE_BOOST_SERIALIZATION : enable boost::serialization + engine. This option requires to build boost::serialization binary and adds a dependency to QxOrm library + ;
    • +
      +
    • _QX_ENABLE_QT_GUI : support serialization of QtGui types : + QBrush, QColor, QFont, QImage, QMatrix, QPicture, QPixmap, QRegion. This option + adds a dependency to QxOrm library (QtGui) ; +
    • +
    • _QX_ENABLE_QT_NETWORK : enable QxService module to + transfer persistent data layer over network (client/server application). This option adds + a dependency to QxOrm library (QtNetwork) ;
    • +
    • _QX_NO_PRECOMPILED_HEADER : disable precompiled header (used to reduce compilation + times of a project) : this option is required with recent + versions of MinGW (because of a known compiler bug), for all other compilers it is + recommended to work with precompiled header ;
    • +
    • _QX_NO_RTTI : build QxOrm library (and all projects which depend on QxOrm) without + RTTI C++ + type information ; +
    • +
    • _QX_STATIC_BUILD : build QxOrm library in static mode (but build QxOrm as a + shared library is recommended). +
    • +
    • _QX_UNITY_BUILD : reduce QxOrm library compilation times using unity build + concept : only one all.cpp source file to compile. It is recommended to enable this + option with CMake (because doesn't + support natively precompiled headers) ;
    • +
    • _QX_ENABLE_MONGODB : support MongoDB database, QxOrm + library becomes an ODM (Object Document Mapper).
    • +
    +
    + Note : QxOrm.pri (or QxOrm.cmake) configuration file must be included in + all projects which depend on QxOrm library, just adding following line in *.pro project + file :
    +
    + + + + + + + +
    +
       include(my_path_to_QxOrm_library/QxOrm.pri)   
    +
    +
    + Other note : instead of qmake, it is possible to use CMake compilation tools to configure and build QxOrm library. + CMake provides a GUI tool to display and configure all available parameters : +

    + QxOrm and CMake +

    +
    +

    Build QxOrm library (with + qmake or CMake)

    +
    + QxOrm library uses qmake process from Qt framework to create + makefile and build the project (it is also possible to use CMake compilation tools, a CMakeLists.txt file is provided with + QxOrm library).
    + qmake is portable and multi-platform, so it works perfectly on Windows, Linux (Unix) and + Mac OS X.
    + To build QxOrm library, just execute following commands :
    +
    + + + + + + + +
    +
       qmake
    +   make debug
    +   make release   
    +
    +
    + On Windows, *.vcproj and *.sln files are available for Microsoft Visual + C++.
    + *.pro files are readable by Qt Creator, and some plugins are available to + interface to other C++ source code editors.
    + mingw_build_all_debug.bat and mingw_build_all_release.bat scripts in the directory + ./tools/ can be used to quickly build QxOrm library and all tests with MinGW + compiler on Windows.
    + gcc_build_all_debug.sh and gcc_build_all_release.sh scripts in the directory + ./tools/ can be used to quickly build QxOrm library and all tests with GCC + compiler on Linux.
    + osx_build_all_debug.sh and osx_build_all_release.sh scripts in the directory + ./tools/ can be used to quickly build QxOrm library and all tests on Mac OS X + (thanks very much to Dominique Billet for the scripts). +

    +
    +

    Qt SQL drivers (supported + databases)

    +
    + QxOrm library uses QtSql engine of Qt framework based on a system of + plugins.
    + A list of supported databases is + available on Qt website.
    + The ODBC plugin (QODBC) ensures compatibility with many databases.
    + For optimal performances, it is recommended to work with a database specific plugin : +
      +
    • QMYSQL : MySQL ;
    • +
    • QPSQL : PostgreSQL (versions 7.3 and above) ;
    • +
    • QOCI : Oracle Call Interface Driver ;
    • +
    • QSQLITE : SQLite version 3 ;
    • +
    • QDB2 : IBM DB2 (version 7.1 and above) ;
    • +
    • QIBASE : Borland InterBase ;
    • +
    • QTDS : Sybase Adaptive Server.
    • +
    + Note : to connect to a Microsoft SQL Server database, it is necessary to use + ODBC (QODBC plugin). +

    + Other note : QxOrm library is able to connect to MongoDB database + (C++ ODM Object Document Mapper). +

    +
    +
    + +

    Persistence - Object + Relational Mapping (ORM)

    +
    + QxOrm library provides a data persistence engine based on QtSql module of Qt + framework. + This persistence engine uses programming pattern : Object Relational Mapping (ORM). +

    + From Wikipedia + website : Object-Relational Mapping (ORM) in computer science is a programming technique for + converting data between incompatible type systems in object-oriented programming languages. This + creates, in effect, a "virtual object database" that can be used from within the programming + language.
    + In object-oriented programming, data management tasks act on object-oriented (OO) objects that are + almost always non-scalar values. For example, consider an address book entry that represents a + single person along with zero or more phone numbers and zero or more addresses. This could be + modeled in an object-oriented implementation by a "Person object" with attributes/fields to hold + each data item that the entry comprises : the person's name, a list of phone numbers, and a list of + addresses. The list of phone numbers would itself contain "PhoneNumber objects" and so on. The + address book entry is treated as a single object by the programming language (it can be referenced + by a single variable containing a pointer to the object, for instance). Various methods can be + associated with the object, such as a method to return the preferred phone number, the home + address, and so on.
    + However, many popular database products such as SQL database management systems (DBMS) can only + store and manipulate scalar values such as integers and strings organized within tables. The + programmer must either convert the object values into groups of simpler values for storage in the + database (and convert them back upon retrieval), or only use simple scalar values within the + program. Object-relational mapping is used to implement the first approach.
    + The heart of the problem is translating the logical representation of the objects into an atomized + form that is capable of being stored in the database, while preserving the properties of the + objects and their relationships so that they can be reloaded as objects when needed. If this + storage and retrieval functionality is implemented, the objects are said to be persistent. +

    + To do this link between object world and relational world, and to provide all its features, + QxOrm library requires that C++ classes are registered in QxOrm context. + So we will start this chapter this way : how to register a C++ class in QxOrm context ? +

    + Note : QxOrm library is able to connect to MongoDB database (C++ ODM + Object Document Mapper). +

    +

    Register a class in QxOrm + context (mapping)

    +
    + All C++ classes can be registered in QxOrm context : there is no need to inherit from a super + object, and you can write your methods and accessors without any constraint. + Register a C++ class in QxOrm context means : +
      +
    • in *.h header file (containing class definition) : use + QX_REGISTER_HPP(class_name, base_class, class_version) macro ; +
    • +
    • in *.cpp source file (containing class implementation) : use + QX_REGISTER_CPP(class_name) macro ; +
    • +
    • in *.cpp source file (containing class implementation) : specialize template + function : void qx::register_class<T>(qx::QxClass<T> & t).
    • +
    + Here is an example to show how to define a class named person with 4 properties + registered in QxOrm context : id, firstName, lastName, birthDate + :
    +
    + * person.h file :
    + + + + + + + +
    +
    #ifndef _PERSON_H_
    +#define _PERSON_H_
    +
    +class person
    +{
    +public:
    +   long id;
    +   QString firstName;
    +   QString lastName;
    +   QDateTime birthDate;
    +
    +   person() : id(0) { ; }
    +   virtual ~person() { ; }
    +};
    +
    +QX_REGISTER_HPP_MY_TEST_EXE(person, qx::trait::no_base_class_defined, 0)
    +
    +/* This macro is necessary to register 'person' class in QxOrm context */
    +/* param 1 : the current class to register => 'person' */
    +/* param 2 : the base class, if no base class, use the qx trait => 'qx::trait::no_base_class_defined' */
    +/* param 3 : the class version used by serialization engine to provide 'ascendant compatibility' */
    +
    +#endif // _PERSON_H_
    +
    +
    + * person.cpp file :
    + + + + + + + +
    +
    #include "precompiled.h"   // Precompiled-header with '#include <QxOrm.h>' and '#include "export.h"'
    +#include "person.h"          // Class definition 'person'
    +#include <QxOrm_Impl.h>     // Automatic memory leak detection and boost serialization export macro
    +
    +QX_REGISTER_CPP_MY_TEST_EXE(person)   // This macro is necessary to register 'person' class in QxOrm context
    +
    +namespace qx {
    +template <> void register_class(QxClass<person> & t)
    +{
    +  t.setName("t_person");               // 'person' C++ class is mapped to 't_person' database table
    +
    +  t.id(& person::id, "id");               // Register 'person::id' <=> primary key in your database
    +  t.data(& person::firstName, "first_name");      // Register 'person::firstName' property mapped to 'first_name' database column name
    +  t.data(& person::lastName, "last_name");  // Register 'person::lastName' property mapped to 'last_name' database column name
    +  t.data(& person::birthDate, "birth_date");  // Register 'person::birthDate' property mapped to 'birth_date' database column name
    +}}
    +
    +

    + Note : class methods qx::QxClass<T>::id() and qx::QxClass<T>::data() return an instance of type : qx::IxDataMember + (which is the base class to register a data member). + With this instance, it is possible to customize default behaviour of qx::IxDataMember + class, like for example in the chapter : Register a transient data + member. +

    + Other note : it is also possible to register functions and class methods in QxOrm context + (support static and non static methods) with qx::QxClass<T>::fct_0(), qx::QxClass<T>::fct_1(), etc... + This feature is a part of introspection engine of QxOrm library, more + details in the chapter : Call function dynamically. +

    +

    Primary key other + than default type "long"

    +
    + By default, the unique id (primary key) of a C++ class registered in QxOrm context is defined + as long type (with auto-increment behaviour in database).
    +
    + It is possible to define a unique id (primary key) of another type (for example, + QString type) with QX_REGISTER_PRIMARY_KEY macro.
    + This macro specializes qx::trait::get_primary_key<T> template to associate a + primary key type to a C++ class.
    +
    + For example, to define a QString primary key for myClass C++ class (mapped to a + database table with a column primary key of type VARCHAR), you have to write : + QX_REGISTER_PRIMARY_KEY(myClass, QString)
    +
    + Warning : QX_REGISTER_PRIMARY_KEY macro must be used before + QX_REGISTER_HPP macro in your class definition, otherwise a compilation error + occurs.
    +
    + Here is an example with author class of qxBlog tutorial and a QString + primary key type :
    +
    + + + + + + + +
    +
    #ifndef _QX_BLOG_AUTHOR_H_
    +#define _QX_BLOG_AUTHOR_H_
    + 
    +class author
    +{
    +public:
    +// -- properties
    +   QString  m_id;
    +   QString  m_name;
    +// -- constructor, virtual destructor
    +   author() { ; }
    +   virtual ~author() { ; }
    +};
    +
    +QX_REGISTER_PRIMARY_KEY(author, QString)
    +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0)
    +
    +#endif // _QX_BLOG_AUTHOR_H_
    +
    +

    +
    +

    Composite primary + key

    +
    + QxOrm supports 'multi-columns primary key'.
    + The class id (primary key) must be defined with following type : +
      +
    • QPair or std::pair to define 2 columns ;
    • +
    • boost::tuple (or std::tuple) to define from 2 columns to 9 columns.
    • +
    + It is necessary to use QX_REGISTER_PRIMARY_KEY() macro to specialize template + and to map class id with multi-columns in database.
    + The list of multi-columns names must be defined with '|' character as separator : + 'column1|column2|column3|etc...'.
    +
    + Here is an example with 'author' class from 'qxBlogCompositeKey' sample + project, this class has an id mapped to 3 columns in database :
    +
    + + + + + + + +
    +
    #ifndef _QX_BLOG_AUTHOR_H_
    +#define _QX_BLOG_AUTHOR_H_
    +
    +class blog;
    +
    +class QX_BLOG_DLL_EXPORT author
    +{
    +
    +   QX_REGISTER_FRIEND_CLASS(author)
    +
    +public:
    +
    +// -- composite key (multi-column primary key in database)
    +   typedef boost::tuple<QString, long, QString> type_composite_key;
    +   static QString str_composite_key() { return "author_id_0|author_id_1|author_id_2"; }
    +
    +// -- typedef
    +   typedef boost::shared_ptr<blog> blog_ptr;
    +   typedef std::vector<blog_ptr> list_blog;
    +
    +// -- enum
    +   enum enum_sex { male, female, unknown };
    +
    +// -- properties
    +   type_composite_key   m_id;
    +   QString              m_name;
    +   QDate                m_birthdate;
    +   enum_sex             m_sex;
    +   list_blog            m_blogX;
    +
    +// -- contructor, virtual destructor
    +   author() : m_id("", 0, ""), m_sex(unknown) { ; }
    +   virtual ~author() { ; }
    +
    +// -- methods
    +   int age() const;
    +
    +// -- methods "get" to composite key
    +   type_composite_key getId() const    { return m_id; }
    +   QString getId_0() const             { return boost::tuples::get<0>(m_id); }
    +   long getId_1() const                { return boost::tuples::get<1>(m_id); }
    +   QString getId_2() const             { return boost::tuples::get<2>(m_id); }
    +
    +// -- methods "set" to composite key
    +   void setId_0(const QString & s)     { boost::tuples::get<0>(m_id) = s; }
    +   void setId_1(long l)                { boost::tuples::get<1>(m_id) = l; }
    +   void setId_2(const QString & s)     { boost::tuples::get<2>(m_id) = s; }
    +
    +};
    +
    +QX_REGISTER_PRIMARY_KEY(author, author::type_composite_key)
    +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0)
    +
    +typedef boost::shared_ptr<author> author_ptr;
    +typedef qx::QxCollection<author::type_composite_key, author_ptr> list_author;
    +
    +#endif // _QX_BLOG_AUTHOR_H_
    +
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +#include "../include/author.h"
    +#include "../include/blog.h"
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_BLOG(author)
    +
    +namespace qx {
    +template <> void register_class(QxClass<author> & t)
    +{
    +   t.id(& author::m_id, author::str_composite_key());
    +
    +   t.data(& author::m_name, "name");
    +   t.data(& author::m_birthdate, "birthdate");
    +   t.data(& author::m_sex, "sex");
    +
    +   t.relationOneToMany(& author::m_blogX, blog::str_composite_key(), author::str_composite_key());
    +
    +   t.fct_0<int>(& author::age, "age");
    +}}
    +
    +int author::age() const
    +{
    +   if (! m_birthdate.isValid()) { return -1; }
    +   return (QDate::currentDate().year() - m_birthdate.year());
    +}
    +
    +
    +

    +
    +

    public or + protected/private data members

    +
    + To register private or protected data members in QxOrm context + (qx::register_class<T> function), it's necessary to declare some friend + class.
    + To do that, QxOrm library provides QX_REGISTER_FRIEND_CLASS(myClass) macro.
    + An example can be found in ./test/qxDllSample/dll1/ directory of QxOrm package with + CPerson class :
    +
    + + + + + + + +
    +
    namespace qx {
    +namespace test {
    +
    +class QX_DLL1_EXPORT CPerson : public QObject
    +{
    +
    +   Q_OBJECT
    +   QX_REGISTER_FRIEND_CLASS(qx::test::CPerson)
    +
    +   // etc...
    +
    +};
    +
    +} // namespace test
    +} // namespace qx
    +
    +

    +
    +

    Namespace

    +
    + If a class is defined in a namespace, a compilation error occurs with QX_REGISTER_HPP + and QX_REGISTER_CPP macros.
    + To avoid this compilation error, it is necessary to use + QX_REGISTER_COMPLEX_CLASS_NAME_HPP and QX_REGISTER_COMPLEX_CLASS_NAME_CPP + macros.
    +
    + You can find a sample in ./test/qxDllSample/dll1/ directory of QxOrm package with + CPerson class defined in qx::test namespace :
    +
    + + + + + + + +
    +
       QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QX_DLL1(qx::test::CPerson, QObject, 0, qx_test_CPerson)   
    +
    +
    + QX_REGISTER_COMPLEX_CLASS_NAME... macros require an extra parameter (in above sample : + qx_test_CPerson) to be able to create a global variable. + This global variable is created before your application is started. + This global variable instance registers the class in QxFactory + module (design pattern factory). + A C++ class name cannot contain "::" character, so this is why extra parameter + replaces all "::" characters by "_". +

    +
    +

    Built-in C++ + supported types

    +
    + QxOrm library supports all primitive types of C++ standard and Qt framework (numeric, + boolean, string, date/time, container, pointer and smart-pointer, etc...). + Here is an example with a list (non exhaustive) of supported C++ types mapped to database + types (here as SQLite format) : +

    + + + + + + + +
    +
    "bool" <-> "SMALLINT"
    +"qx_bool" <-> "SMALLINT"
    +"short" <-> "SMALLINT"
    +"int" <-> "INTEGER"
    +"long" <-> "INTEGER"
    +"long long" <-> "INTEGER"
    +"float" <-> "FLOAT"
    +"double" <-> "FLOAT"
    +"long double" <-> "FLOAT"
    +"unsigned short" <-> "SMALLINT"
    +"unsigned int" <-> "INTEGER"
    +"unsigned long" <-> "INTEGER"
    +"unsigned long long" <-> "INTEGER"
    +"std::string" <-> "TEXT"
    +"std::wstring" <-> "TEXT"
    +"QString" <-> "TEXT"
    +"QVariant" <-> "TEXT"
    +"QUuid" <-> "TEXT"
    +"QDate" <-> "DATE"
    +"QTime" <-> "TIME"
    +"QDateTime" <-> "TIMESTAMP"
    +"QByteArray" <-> "BLOB"
    +"qx::QxDateNeutral" <-> "TEXT"
    +"qx::QxTimeNeutral" <-> "TEXT"
    +"qx::QxDateTimeNeutral" <-> "TEXT"
    +
    +
    + Note : it is possible to persist a type not managed by default by QxOrm library. Go to + the chapter Persist custom type for more details + about this feature. +

    + Other note : to map a C++ type to a database type, please go to the chapter Associate a SQL type to a C++ class for more details. +

    +
    +

    Register a transient + data member

    +
    + A transient data member is not associated to a column in a database table. + QxDao module doesn't use a + transient property for all requests to database. +

    + Why to register a transient property in QxOrm context ?
    + Register a transient data member in QxOrm context enables other features provided by + QxOrm library on this property : serialization, introspection, etc... +

    + qx::QxClass<T>::data() class method has an optional parameter + named : bool bDao (default value is true). + For example, we add a transient property named age to person class (this + property doesn't have to be stored in database because we already have a birthDate + property) : +

    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<person> & t)
    +{
    +  t.id(& person::id, "id");
    +  t.data(& person::firstName, "first_name";);
    +  t.data(& person::lastName, "last_name");
    +  t.data(& person::birthDate, "birth_date");
    +  t.data(& person::age, "age", 0, true, false);
    +}}
    +
    +
    + Here is another way to define a transient property with qx::IxDataMember instance : +

    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<person> & t)
    +{
    +  t.id(& person::id, "id");
    +  t.data(& person::firstName, "first_name";);
    +  t.data(& person::lastName, "last_name");
    +  t.data(& person::birthDate, "birth_date");
    +
    +  IxDataMember * pDataMember = t.data(& person::age, "age");
    +  pDataMember->setDao(false);
    +}}
    +
    +

    +
    +
    +

    Connection to + database

    +
    + To configure a connection to database, you can use the singleton class : qx::QxSqlDatabase.
    + Here is an example to connect to a SQLite database named test_qxorm.db : +

    + + + + + + + +
    +
       // Init parameters to connect to database
    +   qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE");
    +   qx::QxSqlDatabase::getSingleton()->setDatabaseName("./test_qxorm.db");
    +   qx::QxSqlDatabase::getSingleton()->setHostName("localhost");
    +   qx::QxSqlDatabase::getSingleton()->setUserName("root");
    +   qx::QxSqlDatabase::getSingleton()->setPassword("");
    +
    +
    +
    + By default, all requests sent from QxOrm library to database are executed using qx::QxSqlDatabase singleton class settings. + For more details about available settings, it is recommended to read QSqlDatabase Qt class + documentation. +

    + Note : qx::QxSqlDatabase singleton class can manage automatically + requests to database in several threads (support multi-threading). +

    + Other note : it is possible to manage your own connection pool to database, and also to + work with several databases : for more details about this feature, please go to the chapter Working with several databases. +

    + Other note : depending on Qt SQL plugin (setDriverName() method), QxOrm library associates automatically a + SQL generator. + This SQL generator is used to manage specific features provided by databases. + All SQL generators inherit from qx::dao::detail::IxSqlGenerator base class : + + + + + + + + +
    +
       qx::dao::detail::IxSqlGenerator_ptr pSqlGenerator;
    +   pSqlGenerator.reset(new qx::dao::detail::QxSqlGenerator_MSSQLServer());   
    +   qx::QxSqlDatabase::getSingleton()->setSqlGenerator(pSqlGenerator);   
    +
    +

    +
    +

    Persist a C++ instance in + database (insert/update)

    +
    + All functions to communicate with databases are located in qx::dao namespace.
    +
    + To save a C++ instance (or a list of C++ instances) to database, QxOrm library provides these + functions : + +
    + For example :
    + + + + + + + +
    +
       // Create 3 drugs instances
    +   // It is possible to use 'boost' and 'Qt' smart pointer : 'boost::shared_ptr', 'QSharedPointer', etc...
    +   typedef boost::shared_ptr<drug> drug_ptr;
    +   drug_ptr d1; d1.reset(new drug()); d1->name = "name1"; d1->description = "desc1";
    +   drug_ptr d2; d2.reset(new drug()); d2->name = "name2"; d2->description = "desc2";
    +   drug_ptr d3; d3.reset(new drug()); d3->name = "name3"; d3->description = "desc3";
    +
    +   // Insert some drugs into a container
    +   // It is possible to use many containers from 'std', 'boost', 'Qt' and 'qx::QxCollection<Key, Value>'
    +   typedef std::vector<drug_ptr> type_lst_drug;
    +   type_lst_drug lst_drug;
    +   lst_drug.push_back(d1);
    +   lst_drug.push_back(d2);
    +   lst_drug.push_back(d3);
    +
    +   // Insert drugs from container to database
    +   // 'id' property of 'd1', 'd2' and 'd3' are auto-updated
    +   QSqlError daoError = qx::dao::insert(lst_drug);
    +
    +   // Modify and update the second drug into database
    +   d2->name = "name2 modified";
    +   d2->description = "desc2 modified";
    +   daoError = qx::dao::update(d2);
    +
    +
    +

    + Note : all functions located in qx::dao namespace are flexible because they accept several types of + parameters : a simple instance, a list of instances, a pointer, a smart-pointer, a list of + pointers, a list of smart-pointers, etc... For example : +
      +
    • my_entity t;     /* ... */     + qx::dao::insert(t);
    • +
    • my_entity * t;     /* ... */     + qx::dao::insert(t);
    • +
    • std::shared_ptr<my_entity> t;     /* ... + */     qx::dao::insert(t);
    • +
    • QList<my_entity> lst;     /* ... */     + qx::dao::insert(lst);
    • +
    • QList<std::shared_ptr<my_entity> > lst;     /* ... + */     qx::dao::insert(lst);
    • +
    + About supported collection types by QxOrm library, please go to the chapter : Supported containers.
    + About supported smart-pointer types by QxOrm library, please go to the chapter : Supported smart pointers.
    +

    +
    +

    Delete an instance in + database (delete)

    +
    + All functions to communicate with databases are located in qx::dao namespace.
    +
    + To delete a C++ instance (or a list of C++ instances) from database, QxOrm library provides + these functions : + +
    + For example :
    + + + + + + + +
    +
       // Create a drug instance with id '18'
    +   drug d; d.setId(18);
    +
    +   // Delete the drug with id '18' from database
    +   QSqlError daoError = qx::dao::delete_by_id(d);
    +
    +   // Delete all drugs from database
    +   daoError = qx::dao::delete_all<drug>();
    +
    +

    +

    Soft delete behaviour + (logical delete)

    +
    + A soft delete doesn't remove rows from a database table (this is not a physical delete) : a + new column is added to table definition to flag a row as deleted or not. + This extra column can contain a boolean (1 means row deleted, 0 or NULL means row not + deleted), or can contain deletion date-time (if empty or NULL, row is not deleted). + So it is always possible to enable a row which has been previously deleted : you just have to + put a NULL or empty value in this extra column.
    +
    + To define a soft delete behaviour with QxOrm library, you have to use qx::QxSoftDelete class in mapping function + qx::register_class<T>.
    + Here is an example with Bar class containing 2 properties m_id and + m_desc :
    +
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<Bar> & t)
    +{
    +   t.setSoftDelete(qx::QxSoftDelete("deleted_at"));
    +
    +   t.id(& Bar::m_id, "id");
    +   t.data(& Bar::m_desc, "desc");
    +}}
    +
    +
    + SQL queries generated by QxOrm library will take into account this soft delete parameter to + add conditions (don't fetch deleted items, don't delete physically a row, etc...).
    + For example, if you execute this code with Bar class :
    +
    + + + + + + + +
    +
    Bar_ptr pBar; pBar.reset(new Bar());
    +pBar->setId(5);
    +QSqlError daoError = qx::dao::delete_by_id(pBar);     qAssert(! daoError.isValid());
    +qx_bool bDaoExist = qx::dao::exist(pBar);             qAssert(! bDaoExist);
    +daoError = qx::dao::delete_all<Bar>();                qAssert(! daoError.isValid());
    +long lBarCount = qx::dao::count<Bar>();               qAssert(lBarCount == 0);
    +daoError = qx::dao::destroy_all<Bar>();               qAssert(! daoError.isValid());
    +
    +
    + Then output logs are :
    +
    + + + + + + + +
    +
    [QxOrm] sql query (93 ms) : UPDATE Bar SET deleted_at = '20110617115148615' WHERE id = :id
    +[QxOrm] sql query (0 ms) : SELECT Bar.id AS Bar_id_0, Bar.deleted_at FROM Bar WHERE Bar.id = :id 
    +                                         AND (Bar.deleted_at IS NULL OR Bar.deleted_at = '')
    +[QxOrm] sql query (78 ms) : UPDATE Bar SET deleted_at = '20110617115148724'
    +[QxOrm] sql query (0 ms) : SELECT COUNT(*) FROM Bar WHERE (Bar.deleted_at IS NULL OR Bar.deleted_at = '')
    +[QxOrm] sql query (110 ms) : DELETE FROM Bar
    +
    +
    + Note : to delete physically a row from database, you have to use these functions : + qx::dao::destroy_by_id() and qx::dao::destroy_all().
    +
    + Other note : it is recommended to define an index on deleted_at extra column to + optimize SQL queries execution (better performance). +

    +
    +
    +

    Get a C++ instance from + database (fetch)

    +
    + All functions to communicate with databases are located in qx::dao namespace.
    +
    + To fetch automatically all properties of a C++ instance (or a list of C++ instances) mapped to + database table columns (and several tables if relationships are defined), QxOrm library provides + these functions : + +
    + For example :
    + + + + + + + +
    +
       // Fetch drug with id '3' into a new variable
    +   drug_ptr d; d.reset(new drug());
    +   d->id = 3;
    +   QSqlError daoError = qx::dao::fetch_by_id(d);
    +
    +

    +
    +

    SQL queries

    +
    + QxOrm library provides several tools to execute SQL queries to database : + + Note : QxOrm library is based on QtSql module of Qt framework, so it is always possible to call database + using QSqlQuery Qt class if + QxOrm features are not adapted to solve an issue. +

    +

    Using qx::QxSqlQuery + class (or qx_query alias)

    +
    + qx::QxSqlQuery class (or its qx_query typedef) is used + to communicate with database (to filter, to sort, etc.) in 2 different ways : +
      +
    • writing manually a SQL query ;
    • +
    • using C++ methods with a syntax similar to SQL (same concept than the great library SubSonic + for .Net).
    • +
    + With the first method (writing manually SQL query), you can use some optimizations specific + for each database.
    + The second method (using C++ code to build SQL query) binds automatically SQL parameters + without having to deal with qx::QxSqlQuery::bind() function.
    +
    + Here is an example with qx::QxSqlQuery class writing manually a SQL query :
    +
    + + + + + + + +
    +
    // Build a SQL query to fetch only 'author' of type 'female'
    +qx::QxSqlQuery query("WHERE author.sex = :sex");
    +query.bind(":sex", author::female);
    +
    +QList<author> list_of_female;
    +QSqlError daoError = qx::dao::fetch_by_query(query, list_of_female);
    +for (long l = 0; l < list_of_female.count(); l++)
    +{ /* here we can work with the collection provided by database */ }
    +
    +
    + QxOrm library provides 3 styles to write SQL parameters.
    + This style can be modified for a project using + qx::QxSqlDatabase::getSingleton()->setSqlPlaceHolderStyle() method :
    +
      +
    • ph_style_2_point_name : "WHERE author.sex = :sex" (default style) ;
    • +
    • ph_style_at_name : "WHERE author.sex = @sex" ;
    • +
    • ph_style_question_mark : "WHERE author.sex = ?".
    • +
    + Here is the same example using qx::QxSqlQuery class methods to generate SQL query + automatically :
    +
    + + + + + + + +
    +
    // Build a SQL query to fetch only 'author' of type 'female'
    +qx_query query;
    +query.where("author.sex").isEqualTo(author::female);
    +
    +QList<author> list_of_female;
    +QSqlError daoError = qx::dao::fetch_by_query(query, list_of_female);
    +for (long l = 0; l < list_of_female.count(); l++)
    +{ /* here we can work with the collection provided by database */ }
    +
    +
    + With C++ methods of qx::QxSqlQuery class, you don't have to bind any SQL parameter, + and the syntax is similar to real SQL.
    + All SQL parameters will be provided to database automatically with the following style : + qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle().
    +
    + Here is another example using several qx::QxSqlQuery class methods :
    +
    + + + + + + + +
    +
    qx_query query;
    +query.where("sex").isEqualTo(author::female)
    +     .and_("age").isGreaterThan(38)
    +     .or_("last_name").isNotEqualTo("Dupont")
    +     .or_("first_name").like("Alfred")
    +     .and_OpenParenthesis("id").isLessThanOrEqualTo(999)
    +     .and_("birth_date").isBetween(date1, date2)
    +     .closeParenthesis()
    +     .or_("id").in(50, 999, 11, 23, 78945)
    +     .and_("is_deleted").isNotNull()
    +     .orderAsc("last_name", "first_name", "sex")
    +     .limit(50, 150);
    +
    +
    + This code will generate following SQL query for MySQL, PostgreSQL and + SQLite databases (for Oracle and Microsoft SQL Server, there is a + specific process for limit() method) :
    +
    + + + + + + + +
    +
    WHERE sex = :sex_1_0 
    +AND age > :age_3_0 
    +OR last_name <> :last_name_5_0 
    +OR first_name LIKE :first_name_7_0 
    +AND ( id <= :id_10_0 AND birth_date BETWEEN :birth_date_12_0_1 AND :birth_date_12_0_2 ) 
    +OR id IN (:id_15_0_0, :id_15_0_1, :id_15_0_2, :id_15_0_3, :id_15_0_4) 
    +AND is_deleted IS NOT NULL 
    +ORDER BY last_name ASC, first_name ASC, sex ASC 
    +LIMIT :limit_rows_count_19_0 OFFSET :offset_start_row_19_0
    +
    +
    + Here is the list of all functions available to use qx::QxSqlQuery class (or its + qx_query typedef) :
    +
    + + + + + + + +
    +
    // with functions into namespace qx::dao
    +qx::dao::count<T>()
    +qx::dao::fetch_by_query<T>()
    +qx::dao::update_by_query<T>()
    +qx::dao::delete_by_query<T>()
    +qx::dao::destroy_by_query<T>()
    +qx::dao::fetch_by_query_with_relation<T>()
    +qx::dao::fetch_by_query_with_all_relation<T>()
    +qx::dao::update_by_query_with_relation<T>()
    +qx::dao::update_by_query_with_all_relation<T>()
    +qx::dao::update_optimized_by_query<T>()
    +
    +// with qx::QxSession class
    +qx::QxSession::count<T>()
    +qx::QxSession::fetchByQuery<T>()
    +qx::QxSession::update<T>()
    +qx::QxSession::deleteByQuery<T>()
    +qx::QxSession::destroyByQuery<T>()
    +
    +// with qx::QxRepository<T> class
    +qx::QxRepository<T>::count()
    +qx::QxRepository<T>::fetchByQuery()
    +qx::QxRepository<T>::update()
    +qx::QxRepository<T>::deleteByQuery()
    +qx::QxRepository<T>::destroyByQuery()
    +
    +
    + Note : these functions have 2 other optionals parameters : +
      +
    • const QStringList & columns : to select columns to fetch (by default, all + columns are fetched) ;
    • +
    • const QStringList & relation : to select relationships to fetch + (one-to-one, one-to-many, many-to-one and many-to-many + defined in void qx::register_class<T>() mapping function per class), by + default there is no relation fetched.
    • +
    +
    +
    +

    Using standard SQL or + stored procedure

    +
    + QxOrm library provides 2 functions to execute a stored procedure or a custom SQL query : + + The first parameter of these functions, of qx::QxSqlQuery type (or qx_query), contains the stored + procedure or the custom SQL query to execute.
    + For more information about qx::QxSqlQuery class, please read this chapter : Using qx::QxSqlQuery class (or qx_query alias)
    +
    + qx::dao::execute_query<T>() function is a template + function : T type must be registered in QxOrm context (qx::register_class<T> + function).
    + All datas returned by the stored procedure or the custom SQL query which could be associated + with members of the C++ class (of T type) will be fetched automatically.
    + An automatic search is done on the name of each fields returned by the query.
    + Here is an example from qxBlog project of QxOrm package :
    +
    + + + + + + + +
    +
    // Call a custom SQL query or a stored procedure and fetch automatically properties (with a collection of items)
    +qx_query testStoredProcBis("SELECT * FROM author");
    +daoError = qx::dao::execute_query(testStoredProcBis, authorX);
    +qAssert(! daoError.isValid()); qAssert(authorX.count() > 0);
    +qx::dump(authorX);
    +
    +

    + qx::dao::call_query() + function is not a template function : you have to iterate over each result using qx::QxSqlQuery class (or qx_query).
    + To get an output value parameter (must be passed as QSql::Out or QSql::InOut) + returned by a stored procedure, just call the following method : QVariant + qx::QxSqlQuery::boundValue(const QString & sKey) const;.
    +
    + To iterate over all resultset, just use the following methods : +
      +
    • long qx::QxSqlQuery::getSqlResultRowCount() const;
    • +
    • long qx::QxSqlQuery::getSqlResultColumnCount() const;
    • +
    • QVariant qx::QxSqlQuery::getSqlResultAt(long row, long column) const;
    • +
    • QVariant qx::QxSqlQuery::getSqlResultAt(long row, const QString & column) + const;
    • +
    • QVector qx::QxSqlQuery::getSqlResultAllColumns() const;
    • +
    • void qx::QxSqlQuery::dumpSqlResult();
    • +
    + Here is an example using qx::dao::call_query() function :
    +
    + + + + + + + +
    +
    qx_query query("CALL MyStoredProc(:param1, :param2)");
    +query.bind(":param1", "myValue1");
    +query.bind(":param2", 5024, QSql::InOut);
    +QSqlError daoError = qx::dao::call_query(query);
    +QVariant vNewValue = query.boundValue(":param2");
    +query.dumpSqlResult();
    +
    +

    +
    +
    +

    Transactions (commit, + rollback, session)

    +
    + A database transaction is a sequence of operations performed as a single + logical unit of work :
    +
      +
    • if no error occurred during the execution of the transaction, then the system + commits the transaction. +
    • +
    • if an error occurs during the transaction, or if the user specifies a rollback + operation, then the data manipulations within the transaction are not persisted to the + database.
    • +
    + qx::QxSession + class of QxOrm library is designed to manage automatically database transactions (using C++ RAII) :
    +
    + + + + + + + +
    +
    { // Start a scope where a new session is instantiated
    +
    +  // Create a session : a valid database connection by thread is automatically assigned to the session and a transaction is opened
    +  qx::QxSession session;
    +
    +  // Execute some operations with database (using += operator of qx::QxSession class and session database connection)
    +  session += qx::dao::insert(my_object, session.database());
    +  session += qx::dao::update(my_object, session.database());
    +  session += qx::dao::fetch_by_id(my_object, session.database());
    +  session += qx::dao::delete_by_id(my_object, session.database());
    +
    +  // If the session is not valid (so an error occured) => display first error
    +  if (! session.isValid()) { qDebug("[QxOrm] session error : '%s'", qPrintable(session.firstError().text())); }
    +
    +} // End of scope : session is destroyed (transaction => automatically commit or rollback if there is an error)
    +
    +
    + Note : a session can throw a qx::dao::sql_error exception when a SQL error occured (by default, + there is no exception). You can setup this feature using : +
      +
    • qx::QxSession constructor (for a specific session) ;
    • +
    • qx::QxSqlDatabase::getSingleton()->setSessionThrowable(bool b) parameter (for all + sessions).
    • +
    + Other note : don't forget to pass the session database connection to each + qx::dao::xxx functions (using session.database() method).
    + Moreover, you can manage your own database connection (from a connection pool for example) using + constructor of qx::QxSession class.
    +
    + qx::QxSession class provides persistent methods to perform CRUD operations.
    + Here is the same example using qx::QxSession class methods instead of qx::dao + functions :
    +
    + + + + + + + +
    +
    { // Start a scope where a new session is instantiated
    +
    +  // Create a session : a valid database connection by thread is automatically assigned to the session and a transaction is opened
    +  qx::QxSession session;
    +
    +  // Execute some operations with database
    +  session.insert(my_object);
    +  session.update(my_object);
    +  session.fetchById(my_object);
    +  session.deleteById(my_object);
    +
    +  // If the session is not valid (so an error occured) => display first error
    +  if (! session.isValid()) { qDebug("[QxOrm] session error : '%s'", qPrintable(session.firstError().text())); }
    +
    +} // End of scope : session is destroyed (transaction => automatically commit or rollback if there is an error)
    +
    +

    +
    +

    Relationships

    +
    + QxOrm library provides a powerful relationship engine to define easily : + + Note : a full tutorial about relationships based on + qxBlog sample project (source code in QxOrm package) is available. +

    +

    one-to-many (1-n) +

    +
    + A one-to-many (1-n) relationship is defined with method : qx::QxClass<T>::relationOneToMany(). + This method returns an instance of qx::IxSqlRelation type (which is the base class to register a + relationship) and take 3 parameters : +
      +
    • V U::* pData : reference to the class data member ;
    • +
    • const QString & sKey : unique key associated to the relationship ;
    • +
    • const QString & sForeignKey : foreign key defined in the linked class/table. +
    • +
    +
    + For example : imagine an author (a person) who can write several blog : + we will show how to define a one-to-many relationship.
    + Here are the 2 tables in database :
    +
    + qxBlog.table.author
    +
    + author.h file :
    + + + + + + + +
    +
    #ifndef _QX_BLOG_AUTHOR_H_
    +#define _QX_BLOG_AUTHOR_H_
    +
    +class blog;
    +
    +class QX_BLOG_DLL_EXPORT author
    +{
    +public:
    +// -- typedef
    +   typedef boost::shared_ptr<blog> blog_ptr;
    +   typedef std::vector<blog_ptr> list_blog;
    +// -- enum
    +   enum enum_sex { male, female, unknown };
    +// -- properties
    +   QString     m_id;
    +   QString     m_name;
    +   QDate       m_birthdate;
    +   enum_sex    m_sex;
    +   list_blog   m_blogX;
    +// -- constructor, virtual destructor
    +   author() : m_id(0), m_sex(unknown) { ; }
    +   virtual ~author() { ; }
    +// -- methods
    +   int age() const;
    +};
    +
    +QX_REGISTER_PRIMARY_KEY(author, QString)
    +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0)
    +
    +typedef boost::shared_ptr<author> author_ptr;
    +typedef qx::QxCollection<QString, author_ptr> list_author;
    +
    +#endif // _QX_BLOG_AUTHOR_H_
    +
    +
    +
    + author.cpp file :
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/author.h"
    +#include "../include/blog.h"
    +
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_BLOG(author)
    +
    +namespace qx {
    +template <> void register_class(QxClass<author> & t)
    +{
    +   t.id(& author::m_id, "author_id");
    +
    +   t.data(& author::m_name, "name");
    +   t.data(& author::m_birthdate, "birthdate");
    +   t.data(& author::m_sex, "sex");
    +
    +   t.relationOneToMany(& author::m_blogX, "list_blog", "author_id");
    +
    +   t.fct_0<int>(& author::age, "age");
    +}}
    +
    +int author::age() const
    +{
    +   if (! m_birthdate.isValid()) { return -1; }
    +   return (QDate::currentDate().year() - m_birthdate.year());
    +}
    +
    +
    +

    +
    +

    many-to-one (n-1) +

    +
    + A many-to-one (n-1) relationship is defined with method : qx::QxClass<T>::relationManyToOne(). + This method returns an instance of qx::IxSqlRelation type (which is the base class to register a + relationship) and take 2 parameters : +
      +
    • V U::* pData : reference to the class data member ;
    • +
    • const QString & sKey : unique key associated to the relationship (mapped to a + table column in database) ;
    • +
    +
    + For example : a comment is associated to a blog and a blog can + contain several comment : we will show how to define a many-to-one + relationship.
    + Here are the 2 tables in database :
    +
    + qxBlog.table.comment
    +
    + comment.h file :
    + + + + + + + +
    +
    #ifndef _QX_BLOG_COMMENT_H_
    +#define _QX_BLOG_COMMENT_H_
    +
    +class blog;
    +
    +class QX_BLOG_DLL_EXPORT comment
    +{
    +public:
    +// -- typedef
    +   typedef boost::shared_ptr<blog> blog_ptr;
    +// -- properties
    +   long        m_id;
    +   QString     m_text;
    +   QDateTime   m_dt_create;
    +   blog_ptr    m_blog;
    +// -- constructor, virtual destructor
    +   comment() : m_id(0) { ; }
    +   virtual ~comment() { ; }
    +};
    +
    +QX_REGISTER_HPP_QX_BLOG(comment, qx::trait::no_base_class_defined, 0)
    +
    +typedef boost::shared_ptr<comment> comment_ptr;
    +typedef QList<comment_ptr> list_comment;
    +
    +#endif // _QX_BLOG_COMMENT_H_
    +
    +
    +
    + comment.cpp file :
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/comment.h"
    +#include "../include/blog.h"
    +
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_BLOG(comment)
    +
    +namespace qx {
    +template <> void register_class(QxClass<comment> & t)
    +{
    +   t.id(& comment::m_id, "comment_id");
    +
    +   t.data(& comment::m_text, "comment_text");
    +   t.data(& comment::m_dt_create, "date_creation");
    +
    +   t.relationManyToOne(& comment::m_blog, "blog_id");
    +}}
    +
    +
    +

    +
    +

    many-to-many + (n-n)

    +
    + A many-to-many (n-n) relationship is defined with method : qx::QxClass<T>::relationManyToMany(). + This method returns an instance of qx::IxSqlRelation type (which is the base class to register a + relationship) and take 5 parameters : +
      +
    • V U::* pData : reference to the class data member ;
    • +
    • const QString & sKey : unique key associated to the relationship ;
    • +
    • const QString & sExtraTable : extra table name to store id of each side of + relationship ;
    • +
    • const QString & sForeignKeyOwner : foreign key in extra table linked to current + class ;
    • +
    • const QString & sForeignKeyDataType : foreign key in extra table linked to + other class.
    • +
    +
    + For example : a category embed several blog and a blog can belong + to several category : we will show how to define a many-to-many + relationship. + A n-n relationship requires an extra table to store id of each side of + relationship.
    + Here are the 3 tables in database :
    +
    + qxBlog.table.category
    +
    + category.h file :
    + + + + + + + +
    +
    #ifndef _QX_BLOG_CATEGORY_H_
    +#define _QX_BLOG_CATEGORY_H_
    +
    +class blog;
    +
    +class QX_BLOG_DLL_EXPORT category
    +{
    +public:
    +// -- typedef
    +   typedef boost::shared_ptr<blog> blog_ptr;
    +   typedef qx::QxCollection<long, blog_ptr> list_blog;
    +// -- properties
    +   long        m_id;
    +   QString     m_name;
    +   QString     m_desc;
    +   list_blog   m_blogX;
    +// -- constructor, virtual destructor
    +   category() : m_id(0) { ; }
    +   virtual ~category() { ; }
    +};
    +
    +QX_REGISTER_HPP_QX_BLOG(category, qx::trait::no_base_class_defined, 0)
    +
    +typedef QSharedPointer<category> category_ptr;
    +typedef qx::QxCollection<long, category_ptr> list_category;
    +
    +#endif // _QX_BLOG_CATEGORY_H_
    +
    +
    +
    + category.cpp file :
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/category.h"
    +#include "../include/blog.h"
    +
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_BLOG(category)
    +
    +namespace qx {
    +template <> void register_class(QxClass<category> & t)
    +{
    +   t.id(& category::m_id, "category_id");
    +
    +   t.data(& category::m_name, "name");
    +   t.data(& category::m_desc, "description");
    +
    +   t.relationManyToMany(& category::m_blogX, "list_blog", "category_blog", "category_id", "blog_id");
    +}}
    +
    +
    +

    +
    +

    one-to-one (1-1) +

    +
    + A one-to-one (1-1) relationship links 2 entities which share the same database id. + A one-to-one (1-1) relationship is defined with method : qx::QxClass<T>::relationOneToOne(). + This method returns an instance of qx::IxSqlRelation type (which is the base class to register a + relationship) and take 2 parameters : +
      +
    • V U::* pData : reference to the class data member ;
    • +
    • const QString & sKey : unique key associated to the relationship ;
    • +
    +
    + For example : imagine a person table and an author table : an + author is also a person, so these 2 tables could share the same id in database. + Here are the 2 tables in database (with person_id == author_id) :
    +
    + qxBlog.table.person +


    +
    +

    Fetch + relationships

    +
    + QxOrm library supports 4 kind of relationships to link C++ classes registered in QxOrm + context : one-to-one, one-to-many, many-to-one and + many-to-many.
    + For more details to define relationships, you can take a look at qxBlog tutorial.
    + We will explain here how to fetch datas from many tables (QxDao module, functions of + qx::dao namespace) : + + The first parameter of fetch_by_id_with_relation, fetch_all_with_relation + and fetch_by_query_with_relation functions is the list of relationships to + fetch.
    + This list of relationships can contain these items : +
      +
    • relation key : each relation is associated to a key defined in + qx::register_class<T> mapping function ; +
    • +
    • "*" keyword means "fetch all relationships defined in + qx::register_class<T> function (1 level of relationships)" ;
    • +
    • "->" keyword means "LEFT OUTER JOIN" join type between 2 tables ;
    • +
    • ">>" keyword means "INNER JOIN" join type between 2 tables.
    • +
    + Note : using "*" keyword to indicate "fetch all relationships defined into + qx::register_class<T> function", following calls are similar : +
      +
    • qx::dao::fetch_by_id_with_relation("*", ...) == + qx::dao::fetch_by_id_with_all_relation(...) ; +
    • +
    • qx::dao::fetch_by_query_with_relation("*", ...) == + qx::dao::fetch_by_query_with_all_relation(...) ; +
    • +
    • qx::dao::fetch_all_with_relation("*", ...) == + qx::dao::fetch_all_with_all_relation(...). +
    • +
    +
    + Example : from qxBlog tutorial, it's possible to fetch all these datas with only 1 + query :
    +
    + 1- fetch a blog and its author ;
    + 2- for the author fetched, fetch all blog he wrote ;
    + 3- for each blog written by author fetched, fetch all comment + associated.
    +
    + + + + + + + +
    +
    blog_ptr my_blog = blog_ptr(new blog(10));
    +QSqlError daoError = qx::dao::fetch_by_id_with_relation("author_id->list_blog->list_comment", my_blog);
    +
    +
    + This code generates following SQL query : +
    +
    SELECT blog.blog_id AS blog_blog_id_0, blog.blog_text AS blog_blog_text_0, blog.date_creation AS blog_date_creation_0, blog.author_id AS blog_author_id_0, 
    +       author_1.author_id AS author_1_author_id_0, author_1.name AS author_1_name_0, author_1.birthdate AS author_1_birthdate_0, author_1.sex AS author_1_sex_0, 
    +       blog_2.blog_id AS blog_2_blog_id_0, blog_2.author_id AS blog_2_author_id_0, blog_2.blog_text AS blog_2_blog_text_0, blog_2.date_creation AS blog_2_date_creation_0, 
    +       comment_4.comment_id AS comment_4_comment_id_0, comment_4.blog_id AS comment_4_blog_id_0, comment_4.comment_text AS comment_4_comment_text_0, comment_4.date_creation AS comment_4_date_creation_0 
    +FROM blog 
    +LEFT OUTER JOIN author author_1 ON author_1.author_id = blog.author_id 
    +LEFT OUTER JOIN blog blog_2 ON blog_2.author_id = author_1.author_id 
    +LEFT OUTER JOIN comment comment_4 ON comment_4.blog_id = blog_2.blog_id 
    +WHERE blog.blog_id = :blog_id
    +
    +

    + Another example : it's also possible to create a list of relationships to fetch, like + this :
    +
    + + + + + + + +
    +
    blog_ptr my_blog = blog_ptr(new blog(10));
    +QStringList relation;
    +relation << "author_id->list_blog->list_comment";
    +relation << "author_id->list_blog->list_category";
    +relation << "list_comment";
    +relation << "list_category";
    +QSqlError daoError = qx::dao::fetch_by_id_with_relation(relation, my_blog);
    +
    +
    + This code generates following SQL query : +
    +
    SELECT blog.blog_id AS blog_blog_id_0, blog.blog_text AS blog_blog_text_0, blog.date_creation AS blog_date_creation_0, blog.author_id AS blog_author_id_0, 
    +       author_1.author_id AS author_1_author_id_0, author_1.name AS author_1_name_0, author_1.birthdate AS author_1_birthdate_0, author_1.sex AS author_1_sex_0, 
    +       blog_2.blog_id AS blog_2_blog_id_0, blog_2.author_id AS blog_2_author_id_0, blog_2.blog_text AS blog_2_blog_text_0, blog_2.date_creation AS blog_2_date_creation_0, 
    +       category_5.category_id AS category_5_category_id_0, category_5.name AS category_5_name_0, category_5.description AS category_5_description_0, 
    +       comment_6.comment_id AS comment_6_comment_id_0, comment_6.blog_id AS comment_6_blog_id_0, comment_6.comment_text AS comment_6_comment_text_0, comment_6.date_creation AS comment_6_date_creation_0, 
    +       category_7.category_id AS category_7_category_id_0, category_7.name AS category_7_name_0, category_7.description AS category_7_description_0 
    +FROM blog 
    +LEFT OUTER JOIN author author_1 ON author_1.author_id = blog.author_id 
    +LEFT OUTER JOIN blog blog_2 ON blog_2.author_id = author_1.author_id 
    +LEFT OUTER JOIN category_blog category_blog_5 ON blog_2.blog_id = category_blog_5.blog_id 
    +LEFT OUTER JOIN category category_5 ON category_blog_5.category_id = category_5.category_id 
    +LEFT OUTER JOIN comment comment_6 ON comment_6.blog_id = blog.blog_id 
    +LEFT OUTER JOIN category_blog category_blog_7 ON blog.blog_id = category_blog_7.blog_id 
    +LEFT OUTER JOIN category category_7 ON category_blog_7.category_id = category_7.category_id 
    +WHERE blog.blog_id = :blog_id
    +
    +

    + Another example : to fetch all relationships per level, "*" keyword must be used.
    + So to fetch all relationships on 3 levels, we can write :
    +
    + + + + + + + +
    +
    blog_ptr my_blog = blog_ptr(new blog(10));
    +QSqlError daoError = qx::dao::fetch_by_id_with_relation("*->*->*", my_blog);
    +
    +
    + This code generates following SQL query : +
    +
    SELECT blog.blog_id AS blog_blog_id_0, blog.blog_text AS blog_blog_text_0, blog.date_creation AS blog_date_creation_0, blog.author_id AS blog_author_id_0, 
    +       author_1.author_id AS author_1_author_id_0, author_1.name AS author_1_name_0, author_1.birthdate AS author_1_birthdate_0, author_1.sex AS author_1_sex_0, 
    +       blog_2.blog_id AS blog_2_blog_id_0, blog_2.author_id AS blog_2_author_id_0, blog_2.blog_text AS blog_2_blog_text_0, blog_2.date_creation AS blog_2_date_creation_0, blog_2.author_id AS blog_2_author_id_0_2, 
    +       author_3.author_id AS author_3_author_id_0, author_3.name AS author_3_name_0, author_3.birthdate AS author_3_birthdate_0, author_3.sex AS author_3_sex_0, 
    +       comment_4.comment_id AS comment_4_comment_id_0, comment_4.blog_id AS comment_4_blog_id_0, comment_4.comment_text AS comment_4_comment_text_0, comment_4.date_creation AS comment_4_date_creation_0, 
    +       category_5.category_id AS category_5_category_id_0, category_5.name AS category_5_name_0, category_5.description AS category_5_description_0, 
    +       comment_6.comment_id AS comment_6_comment_id_0, comment_6.blog_id AS comment_6_blog_id_0, comment_6.comment_text AS comment_6_comment_text_0, comment_6.date_creation AS comment_6_date_creation_0, comment_6.blog_id AS comment_6_blog_id_0_6, 
    +       blog_7.blog_id AS blog_7_blog_id_0, blog_7.blog_text AS blog_7_blog_text_0, blog_7.date_creation AS blog_7_date_creation_0, blog_7.author_id AS blog_7_author_id_0_7, 
    +       author_8.author_id AS author_8_author_id_0, author_8.name AS author_8_name_0, author_8.birthdate AS author_8_birthdate_0, author_8.sex AS author_8_sex_0, 
    +       comment_9.comment_id AS comment_9_comment_id_0, comment_9.blog_id AS comment_9_blog_id_0, comment_9.comment_text AS comment_9_comment_text_0, comment_9.date_creation AS comment_9_date_creation_0, 
    +       category_10.category_id AS category_10_category_id_0, category_10.name AS category_10_name_0, category_10.description AS category_10_description_0, 
    +       category_11.category_id AS category_11_category_id_0, category_11.name AS category_11_name_0, category_11.description AS category_11_description_0, 
    +       blog_12.blog_id AS blog_12_blog_id_0, blog_12.blog_text AS blog_12_blog_text_0, blog_12.date_creation AS blog_12_date_creation_0, blog_12.author_id AS blog_12_author_id_0_12, 
    +       author_13.author_id AS author_13_author_id_0, author_13.name AS author_13_name_0, author_13.birthdate AS author_13_birthdate_0, author_13.sex AS author_13_sex_0, 
    +       comment_14.comment_id AS comment_14_comment_id_0, comment_14.blog_id AS comment_14_blog_id_0, comment_14.comment_text AS comment_14_comment_text_0, comment_14.date_creation AS comment_14_date_creation_0, 
    +       category_15.category_id AS category_15_category_id_0, category_15.name AS category_15_name_0, category_15.description AS category_15_description_0 
    +FROM blog 
    +LEFT OUTER JOIN author author_1 ON author_1.author_id = blog.author_id 
    +LEFT OUTER JOIN blog blog_2 ON blog_2.author_id = author_1.author_id 
    +LEFT OUTER JOIN author author_3 ON author_3.author_id = blog_2.author_id 
    +LEFT OUTER JOIN comment comment_4 ON comment_4.blog_id = blog_2.blog_id 
    +LEFT OUTER JOIN category_blog category_blog_5 ON blog_2.blog_id = category_blog_5.blog_id 
    +LEFT OUTER JOIN category category_5 ON category_blog_5.category_id = category_5.category_id 
    +LEFT OUTER JOIN comment comment_6 ON comment_6.blog_id = blog.blog_id 
    +LEFT OUTER JOIN blog blog_7 ON blog_7.blog_id = comment_6.blog_id 
    +LEFT OUTER JOIN author author_8 ON author_8.author_id = blog_7.author_id 
    +LEFT OUTER JOIN comment comment_9 ON comment_9.blog_id = blog_7.blog_id 
    +LEFT OUTER JOIN category_blog category_blog_10 ON blog_7.blog_id = category_blog_10.blog_id 
    +LEFT OUTER JOIN category category_10 ON category_blog_10.category_id = category_10.category_id 
    +LEFT OUTER JOIN category_blog category_blog_11 ON blog.blog_id = category_blog_11.blog_id 
    +LEFT OUTER JOIN category category_11 ON category_blog_11.category_id = category_11.category_id 
    +LEFT OUTER JOIN category_blog category_blog_12 ON category_11.category_id = category_blog_12.category_id 
    +LEFT OUTER JOIN blog blog_12 ON category_blog_12.blog_id = blog_12.blog_id 
    +LEFT OUTER JOIN author author_13 ON author_13.author_id = blog_12.author_id 
    +LEFT OUTER JOIN comment comment_14 ON comment_14.blog_id = blog_12.blog_id 
    +LEFT OUTER JOIN category_blog category_blog_15 ON blog_12.blog_id = category_blog_15.blog_id 
    +LEFT OUTER JOIN category category_15 ON category_blog_15.category_id = category_15.category_id 
    +WHERE blog.blog_id = :blog_id
    +
    +

    +
    +

    Select columns + fetching relationships and define custom SQL alias

    +
    + It is sometimes necessary to not request all table columns to optimize : indeed, selecting + columns really used by a process limits network traffic between database and C++ application, + so performance are improved.
    +
    + About relationships, QxOrm library provides a syntax to select columns to fetch, using format + : my_relation { col_1, col_2, etc... }. + By default, if this syntax is not used, then QxOrm library fetches all columns.
    +
    + For example : imagine a query to fetch : +
      +
    • only blog_text column of blog table ;
    • +
    • only name and birthdate columns of author table ;
    • +
    • only comment_text column of comment table.
    • +
    + + + + + + + +
    +
       // Fetch relations defining columns to fetch with syntax { col_1, col_2, etc... }
    +   list_blog lstBlogComplexRelation;
    +   QStringList relations = QStringList() << "{ blog_text }" << "author_id { name, birthdate }" << "list_comment { comment_text }";
    +   QSqlError daoError = qx::dao::fetch_all_with_relation(relations, lstBlogComplexRelation);
    +
    +   qx::dump(lstBlogComplexRelation);
    +   qAssert(lstBlogComplexRelation.size() > 0);
    +   qAssert(lstBlogComplexRelation[0]->m_text != ""); // Fetched
    +   qAssert(lstBlogComplexRelation[0]->m_dt_creation.isNull()); // Not fetched
    +   qAssert(lstBlogComplexRelation[0]->m_author->m_sex == author::unknown); // Not fetched
    +   qAssert(lstBlogComplexRelation[0]->m_author->m_name != ""); // Fetched
    +   qAssert(lstBlogComplexRelation[0]->m_commentX.size() > 0);
    +   qAssert(lstBlogComplexRelation[0]->m_commentX[0]->m_dt_create.isNull()); // Not fetched
    +   qAssert(lstBlogComplexRelation[0]->m_commentX[0]->m_text != ""); // Fetched
    +
    +
    + Note : another syntax is available to select columns to not fetch : my_relation -{ + col_1, col_2, etc... }. +

    + Other note : you can also define a custom SQL alias per relation. + This is useful to write your WHERE conditions in the SQL query. + A SQL alias can be defined between characters < >. +

    + Example : here is a fetch with relationships example defining some SQL aliases per + relation : +

    + + + + + + + +
    +
    list_blog lstBlogComplexRelation3;
    +QStringList relations;
    +relations << "<blog_alias> { blog_text }";
    +relations << "author_id <author_alias> { name, birthdate }";
    +relations << "list_comment <list_comment_alias> { comment_text } -> blog_id <blog_alias_2> -> * <..._my_alias_suffix>";
    +QSqlError daoError = qx::dao::fetch_all_with_relation(relations, lstBlogComplexRelation3);
    +qx::dump(lstBlogComplexRelation3);
    +
    +

    + This code generates following SQL query : +
    +
    +
    SELECT blog_alias.blog_id AS blog_alias_blog_id_0, blog_alias.blog_text AS blog_alias_blog_text_0, blog_alias.author_id AS blog_alias_author_id_0, author_alias.author_id AS author_alias_author_id_0, author_alias.name AS author_alias_name_0, author_alias.birthdate AS author_alias_birthdate_0, list_comment_alias.comment_id AS list_comment_alias_comment_id_0, list_comment_alias.blog_id AS list_comment_alias_blog_id_0, list_comment_alias.comment_text AS list_comment_alias_comment_text_0, list_comment_alias.blog_id AS list_comment_alias_blog_id_0_2, blog_alias_2.blog_id AS blog_alias_2_blog_id_0, blog_alias_2.blog_text AS blog_alias_2_blog_text_0, blog_alias_2.date_creation AS blog_alias_2_date_creation_0, blog_alias_2.author_id AS blog_alias_2_author_id_0_3, author_my_alias_suffix.author_id AS author_my_alias_suffix_author_id_0, author_my_alias_suffix.name AS author_my_alias_suffix_name_0, author_my_alias_suffix.birthdate AS author_my_alias_suffix_birthdate_0, author_my_alias_suffix.sex AS author_my_alias_suffix_sex_0, comment_my_alias_suffix.comment_id AS comment_my_alias_suffix_comment_id_0, comment_my_alias_suffix.blog_id AS comment_my_alias_suffix_blog_id_0, comment_my_alias_suffix.comment_text AS comment_my_alias_suffix_comment_text_0, comment_my_alias_suffix.date_creation AS comment_my_alias_suffix_date_creation_0, comment_my_alias_suffix.blog_id AS comment_my_alias_suffix_blog_id_0_5, category_my_alias_suffix.category_id AS category_my_alias_suffix_category_id_0, category_my_alias_suffix.name AS category_my_alias_suffix_name_0, category_my_alias_suffix.description AS category_my_alias_suffix_description_0
    +  FROM blog AS blog_alias
    +  LEFT OUTER JOIN author author_alias ON author_alias.author_id = blog_alias.author_id
    +  LEFT OUTER JOIN comment list_comment_alias ON list_comment_alias.blog_id = blog_alias.blog_id
    +  LEFT OUTER JOIN blog blog_alias_2 ON blog_alias_2.blog_id = list_comment_alias.blog_id
    +  LEFT OUTER JOIN author author_my_alias_suffix ON author_my_alias_suffix.author_id = blog_alias_2.author_id
    +  LEFT OUTER JOIN comment comment_my_alias_suffix ON comment_my_alias_suffix.blog_id = blog_alias_2.blog_id
    +  LEFT OUTER JOIN category_blog category_blog_6 ON blog_alias_2.blog_id = category_blog_6.blog_id
    +  LEFT OUTER JOIN category category_my_alias_suffix ON category_blog_6.category_id = category_my_alias_suffix.category_id
    +
    +

    +
    +

    Add SQL query inside + LEFT OUTER JOIN / INNER JOIN

    +
    + qx::QxSqlQuery + class (or its qx_query alias) provides the following method : +

    + + + + + + + +
    +
    QxSqlQuery & QxSqlQuery::addJoinQuery(const QString & relationKeyOrAlias, const QxSqlQuery & joinQuery);
    +
    +

    + The qx::QxSqlQuery::addJoinQuery() method inserts SQL sub-queries inside LEFT OUTER + JOIN / INNER JOIN.
    + For example : +

    + + + + + + + +
    +
    // Test to add join SQL sub-queries (inside LEFT OUTER JOIN or INNER JOIN)
    +list_blog lstBlogWithJoinQueries;
    +qx_query query = qx_query().where("blog_alias.blog_text").isEqualTo("update blog_text_1");
    +query.addJoinQuery("list_comment_alias", "AND list_comment_alias.comment_text IS NOT NULL");
    +query.addJoinQuery("author_alias", qx_query().freeText("AND author_alias.sex = :sex", QVariantList() << author::female));
    +daoError = qx::dao::fetch_by_query_with_relation(QStringList() << "<blog_alias> { blog_text }" << "author_id <author_alias> { name, birthdate, sex }" 
    +                                                               << "list_comment <list_comment_alias> { comment_text }", query, lstBlogWithJoinQueries);
    +qx::dump(lstBlogWithJoinQueries);
    +qAssert(lstBlogWithJoinQueries.size() > 0);
    +qAssert(lstBlogWithJoinQueries[0]->m_text == "update blog_text_1");
    +qAssert(lstBlogWithJoinQueries[0]->m_author->m_sex == author::female);
    +
    +

    + Above C++ code will build following SQL query : +

    +
    +
    SELECT blog_alias.blog_id AS blog_alias_blog_id_0, blog_alias.blog_text AS blog_alias_blog_text_0, blog_alias.author_id AS blog_alias_author_id_0, author_alias.author_id AS author_alias_author_id_0, author_alias.name AS author_alias_name_0, author_alias.birthdate AS author_alias_birthdate_0, author_alias.sex AS author_alias_sex_0, list_comment_alias.comment_id AS list_comment_alias_comment_id_0, list_comment_alias.blog_id AS list_comment_alias_blog_id_0, list_comment_alias.comment_text AS list_comment_alias_comment_text_0
    +  FROM blog AS blog_alias
    +  LEFT OUTER JOIN author author_alias ON (author_alias.author_id = blog_alias.author_id
    +      AND author_alias.sex = :sex)
    +  LEFT OUTER JOIN comment list_comment_alias ON (list_comment_alias.blog_id = blog_alias.blog_id
    +      AND list_comment_alias.comment_text IS NOT NULL)
    +  WHERE blog_alias.blog_text = :blog_alias_blog_text_1_0
    +
    +

    +
    +
    +

    Supported containers +

    +
    + QxOrm library supports several containers provided by Qt, boost and std standard library. + QxOrm library provides also its own container, named qx::QxCollection, especially designed to store data fetched from + database. + So the developer is not restricted : QxOrm library offers a large choice of containers. +

    +

    Qt containers

    +
    + + + + + + + +
    +
    +  QList<T>  
    +  QVector<T>  
    +  QSet<T>  
    +  QLinkedList<T>  
    +  QHash<Key, Value>  
    +  QMap<Key, Value>  
    +  QMultiHash<Key, Value>  
    +  QMultiMap<Key, Value>  
    +
    +
    +
    +
    +

    Boost containers +

    +
    + + + + + + + +
    +
    +  boost::unordered_map<Key, Value>  
    +  boost::unordered_set<T>  
    +  boost::unordered_multimap<Key, Value>  
    +  boost::unordered_multiset<T>  
    +
    +
    +
    +
    +

    std containers +

    +
    + + + + + + + +
    +
    +  std::list<T>  
    +  std::vector<T>  
    +  std::set<T>  
    +  std::map<Key, Value>  
    +
    +  std::unordered_map<Key, Value>  
    +  std::unordered_set<T>  
    +  std::unordered_multimap<Key, Value>  
    +  std::unordered_multiset<T>  
    +
    +
    +
    +
    +

    qx::QxCollection +

    +
    + There are several containers provided by stl, boost and Qt + libraries.
    + So, it is legitimate to ask this question : what is qx::QxCollection<Key, Value> + for ?
    + qx::QxCollection<Key, Value> is a new container + (based on the excellent boost::multi_index_container library) which has following + features : +
      +
    • keeps insertion order of items in the list ; +
    • +
    • quick access to an item by its index : is equivalent to std::vector<T> or + QList<T> for example ; +
    • +
    • quick access to an item by a key (hash-map) : is equivalent to QHash<Key, + Value> or boost::unordered_map<Key, Value> for example ; +
    • +
    • sort by Key type and by Value type ; +
    • +
    • thread-safe. +
    • +
    + Note : qx::QxCollection<Key, Value> is compatible with the foreach + macro provided by Qt library and the BOOST_FOREACH macro provided by boost library.
    + However, each element returned by these 2 macros matches to an object of std::pair<Key, + Value> type.
    + To get a more natural and more readable result, it is advised to use the _foreach + macro : this macro uses BOOST_FOREACH for all containers except for + qx::QxCollection<Key, Value>.
    + In this case, the returned element matches to the Value type (cf. sample).
    + _foreach macro is compatible with all containers (stl, Qt, + boost...) since it uses BOOST_FOREACH macro.

    + + Additional note : qx::QxCollection<Key, Value> is especially designed to + receive data resulting from a database.
    + Indeed, these data can be sorted (using ORDER BY in a SQL query for example), it is + thus important to keep insertion order of items in the list.
    + Furthermore, each data resulting from a database has a unique id : it is thus important to be + able to access quickly to an item based on this single identifier (hash-map).

    + + Sample :
    + + + + + + + +
    +
    /* definition of drug class with 3 properties : code, name, description */
    +class drug { public: QString code; QString name; QString desc; };
    +
    +/* smart pointer of drug */
    +typedef boost::shared_ptr<drug> drug_ptr;
    +
    +/* collection of drugs by code */
    +qx::QxCollection<QString, drug_ptr> lstDrugs;
    +
    +/* create 3 new drugs */
    +drug_ptr d1; d1.reset(new drug()); d1->code = "code1"; d1->name = "name1"; d1->desc = "desc1";
    +drug_ptr d2; d2.reset(new drug()); d2->code = "code2"; d2->name = "name2"; d2->desc = "desc2";
    +drug_ptr d3; d3.reset(new drug()); d3->code = "code3"; d3->name = "name3"; d3->desc = "desc3";
    +
    +/* insert drugs into the collection */
    +lstDrugs.insert(d1->code, d1);
    +lstDrugs.insert(d2->code, d2);
    +lstDrugs.insert(d3->code, d3);
    +
    +/* iterate with '_foreach' keyword */
    +_foreach(drug_ptr p, lstDrugs)
    +{ qDebug() << qPrintable(p->name) << " " << qPrintable(p->desc); }
    +
    +/* iterate with 'for' keyword */
    +for (long l = 0; l < lstDrugs.count(); ++l)
    +{
    +   drug_ptr p = lstDrugs.getByIndex(l);
    +   QString code = lstDrugs.getKeyByIndex(l);
    +   qDebug() << qPrintable(p->name) << " " << qPrintable(p->desc);
    +}
    +
    +/* iterate with 'QxCollectionIterator' java style */
    +qx::QxCollectionIterator<QString, drug_ptr> itr(lstDrugs);
    +while (itr.next())
    +{
    +   QString code = itr.key();
    +   qDebug() << qPrintable(itr.value()->name) << " " << qPrintable(itr.value()->desc);
    +}
    +
    +/* sort ascending by key and sort descending by value */
    +lstDrugs.sortByKey(true);
    +lstDrugs.sortByValue(false);
    +
    +/* access drug by code */
    +drug_ptr p = lstDrugs.getByKey("code2");
    +
    +/* access drug by index */
    +drug_ptr p = lstDrugs.getByIndex(2);
    +
    +/* test if drug exists and if collection is empty */
    +bool bExist = lstDrugs.exist("code3");
    +bool bEmpty = lstDrugs.empty();
    +
    +/* remove the second drug from collection */
    +lstDrugs.removeByIndex(2);
    +
    +/* remove the drug with "code3" */
    +lstDrugs.removeByKey("code3");
    +
    +/* clear the collection */
    +lstDrugs.clear();
    +
    +

    +
    +
    +

    Supported smart + pointers

    +
    + QxOrm library supports several smart pointers provided by Qt, boost and std standard library. + QxOrm library provides also its own smart pointer, named qx::dao::ptr, which provides new features when used with qx::dao functions. + So the developer is not restricted : QxOrm library offers a large choice of smart pointers. +

    +

    Qt smart pointers +

    +
    + + + + + + + +
    +
    +  QSharedPointer<T>  
    +  QScopedPointer<T>  
    +  QWeakPointer<T>  
    +  QSharedDataPointer<T>  
    +
    +
    +
    +
    +

    Boost smart + pointers

    +
    + + + + + + + +
    +
    +  boost::shared_ptr<T>  
    +  boost::intrusive_ptr<T>  
    +  boost::scoped_ptr<T>  
    +  boost::weak_ptr<T>  
    +
    +
    +
    +
    +

    std smart + pointers

    +
    + + + + + + + +
    +
    +  std::shared_ptr<T>  
    +  std::unique_ptr<T>  
    +  std::weak_ptr<T>  
    +
    +
    +
    +
    +

    qx::dao::ptr

    +
    + QxOrm library can be used with smart-pointers provided by std, boost and + Qt libraries.
    + QxOrm smart-pointer is based on QSharedPointer and provides new features with + 'qx::dao::...' functions.
    + qx::dao::ptr<T> keeps automatically values from + database.
    + So it's possible to detect if an instance has been modified using the method + 'isDirty()' : this method can return list of properties changed.
    + qx::dao::ptr<T> can also be used with the function + 'qx::dao::update_optimized()' to update in database only properties changed.
    + qx::dao::ptr<T> can be used with a simple object and with many containers : + stl, boost, Qt and qx::QxCollection<Key, Value>.
    +
    + Sample :
    + + + + + + + +
    +
       // Test 'isDirty()' method
    +   qx::dao::ptr<blog> blog_isdirty = qx::dao::ptr<blog>(new blog());
    +   blog_isdirty->m_id = blog_1->m_id;
    +   daoError = qx::dao::fetch_by_id(blog_isdirty);
    +   qAssert(! daoError.isValid() && ! blog_isdirty.isDirty());
    +
    +   blog_isdirty->m_text = "blog property 'text' modified => blog is dirty !!!";
    +   QStringList lstDiff; bool bDirty = blog_isdirty.isDirty(lstDiff);
    +   qAssert(bDirty && (lstDiff.count() == 1) && (lstDiff.at(0) == "blog_text"));
    +   if (bDirty) { qDebug("[QxOrm] test dirty 1 : blog is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
    +
    +   // Update only property 'm_text' of 'blog_isdirty'
    +   daoError = qx::dao::update_optimized(blog_isdirty);
    +   qAssert(! daoError.isValid() && ! blog_isdirty.isDirty());
    +   qx::dump(blog_isdirty);
    +
    +   // Test 'isDirty()' method with a container
    +   typedef qx::dao::ptr< QList<author_ptr> > type_lst_author_test_is_dirty;
    +
    +   type_lst_author_test_is_dirty container_isdirty = type_lst_author_test_is_dirty(new QList<author_ptr>());
    +   daoError = qx::dao::fetch_all(container_isdirty);
    +   qAssert(! daoError.isValid() && ! container_isdirty.isDirty() && (container_isdirty->count() == 3));
    +
    +   author_ptr author_ptr_dirty = container_isdirty->at(1);
    +   author_ptr_dirty->m_name = "author name modified at index 1 => container is dirty !!!";
    +   bDirty = container_isdirty.isDirty(lstDiff);
    +   qAssert(bDirty && (lstDiff.count() == 1));
    +   if (bDirty) { qDebug("[QxOrm] test dirty 2 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
    +
    +   author_ptr_dirty = container_isdirty->at(2);
    +   author_ptr_dirty->m_birthdate = QDate(1998, 03, 06);
    +   bDirty = container_isdirty.isDirty(lstDiff);
    +   qAssert(bDirty && (lstDiff.count() == 2));
    +   if (bDirty) { qDebug("[QxOrm] test dirty 3 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
    +
    +   // Update only property 'm_name' at position 1, only property 'm_birthdate' at position 2 and nothing at position 0
    +   daoError = qx::dao::update_optimized(container_isdirty);
    +   qAssert(! daoError.isValid() && ! container_isdirty.isDirty());
    +   qx::dump(container_isdirty);
    +
    +   // Fetch only property 'm_dt_creation' of blog
    +   QStringList lstColumns = QStringList() << "date_creation";
    +   list_blog lst_blog_with_only_date_creation;
    +   daoError = qx::dao::fetch_all(lst_blog_with_only_date_creation, NULL, lstColumns);
    +   qAssert(! daoError.isValid() && (lst_blog_with_only_date_creation.size() > 0));
    +
    +   if ((lst_blog_with_only_date_creation.size() > 0) && (lst_blog_with_only_date_creation[0] != NULL))
    +   { qAssert(lst_blog_with_only_date_creation[0]->m_text.isEmpty()); }
    +
    +   qx::dump(lst_blog_with_only_date_creation);
    +
    +

    +
    +
    +

    Triggers

    +
    + With QxOrm Triggers, it is possible to execute custom process before and/or after + an insert, update or delete query in database.
    + You can find a sample in ./test/qxDllSample/dll2/ directory of QxOrm package with + BaseClassTrigger class.
    + BaseClassTrigger sample class contains 5 properties : m_id, m_dateCreation, + m_dateModification, m_userCreation and m_userModification.
    + In the following example, each property will be automatically auto-updated for all derived + classes from BaseClassTrigger (see Foo class and Bar class in the same + project).
    + It is necessary to specialize 'qx::dao::detail::QxDao_Trigger<T>' template to work + with this feature.
    +
    + + + + + + + +
    +
    #ifndef _QX_BASE_CLASS_TRIGGER_H_
    +#define _QX_BASE_CLASS_TRIGGER_H_
    +
    +class QX_DLL2_EXPORT BaseClassTrigger
    +{
    +
    +   QX_REGISTER_FRIEND_CLASS(BaseClassTrigger)
    +
    +protected:
    +
    +   long        m_id;
    +   QDateTime   m_dateCreation;
    +   QDateTime   m_dateModification;
    +   QString     m_userCreation;
    +   QString     m_userModification;
    +
    +public:
    +
    +   BaseClassTrigger() : m_id(0)  { ; }
    +   virtual ~BaseClassTrigger()   { ; }
    +
    +   long getId() const                     { return m_id; }
    +   QDateTime getDateCreation() const      { return m_dateCreation; }
    +   QDateTime getDateModification() const  { return m_dateModification; }
    +   QString getUserCreation() const        { return m_userCreation; }
    +   QString getUserModification() const    { return m_userModification; }
    +
    +   void setId(long l)                              { m_id = l; }
    +   void setDateCreation(const QDateTime & dt)      { m_dateCreation = dt; }
    +   void setDateModification(const QDateTime & dt)  { m_dateModification = dt; }
    +   void setUserCreation(const QString & s)         { m_userCreation = s; }
    +   void setUserModification(const QString & s)     { m_userModification = s; }
    +
    +   void onBeforeInsert(qx::dao::detail::IxDao_Helper * dao);
    +   void onBeforeUpdate(qx::dao::detail::IxDao_Helper * dao);
    +
    +};
    +
    +QX_REGISTER_HPP_QX_DLL2(BaseClassTrigger, qx::trait::no_base_class_defined, 0)
    +
    +namespace qx {
    +namespace dao {
    +namespace detail {
    +
    +template <>
    +struct QxDao_Trigger<BaseClassTrigger>
    +{
    +
    +   static inline void onBeforeInsert(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { if (t) { t->onBeforeInsert(dao); } }
    +   static inline void onBeforeUpdate(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { if (t) { t->onBeforeUpdate(dao); } }
    +   static inline void onBeforeDelete(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onBeforeFetch(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onAfterInsert(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onAfterUpdate(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onAfterDelete(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onAfterFetch(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +
    +};
    +
    +} // namespace detail
    +} // namespace dao
    +} // namespace qx
    +
    +#endif // _QX_BASE_CLASS_TRIGGER_H_
    +
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +#include "../include/BaseClassTrigger.h"
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_DLL2(BaseClassTrigger)
    +
    +namespace qx {
    +template <> void register_class(QxClass<BaseClassTrigger> & t)
    +{
    +   IxDataMember * pData = NULL;
    +
    +   pData = t.id(& BaseClassTrigger::m_id, "id");
    +
    +   pData = t.data(& BaseClassTrigger::m_dateCreation, "date_creation");
    +   pData = t.data(& BaseClassTrigger::m_dateModification, "date_modification");
    +   pData = t.data(& BaseClassTrigger::m_userCreation, "user_creation");
    +   pData = t.data(& BaseClassTrigger::m_userModification, "user_modification");
    +}}
    +
    +void BaseClassTrigger::onBeforeInsert(qx::dao::detail::IxDao_Helper * dao)
    +{
    +   Q_UNUSED(dao);
    +   m_dateCreation = QDateTime::currentDateTime();
    +   m_dateModification = QDateTime::currentDateTime();
    +   m_userCreation = "current_user_1";
    +   m_userModification = "current_user_1";
    +}
    +
    +void BaseClassTrigger::onBeforeUpdate(qx::dao::detail::IxDao_Helper * dao)
    +{
    +   Q_UNUSED(dao);
    +   m_dateModification = QDateTime::currentDateTime();
    +   m_userModification = "current_user_2";
    +}
    +
    +
    +

    +
    +

    Validators

    +
    + QxValidator + module of QxOrm library provides a validation engine for classes registered in QxOrm + context.
    + To use this validation engine, you have to define your constraints into the mapping function per + class : void qx::register_class<T>.
    + If for an instance of class, at least one constraint violation is detected, then the instance is + invalid : the object cannot be saved into database (INSERT or UPDATE).
    +
    + It's also possible to use QxValidator module to validate an instance on the presentation + layer : if some datas from a user are invalids, an error message can be displayed, and it's not + necessary to try to send the instance to the data access layer.
    + The validation process can be executed in several layers of your application without having to + duplicate any of these rules (presentation layer, data access layer).
    +
    + Here is a description of some classes defined in QxValidator module : +
      +
    • qx::IxValidator : each constraint defined in void + qx::register_class<T> function is associated with an interface of + qx::IxValidator type ; +
    • +
    • qx::IxValidatorX : the list of constraints is associated with an + interface of qx::IxValidatorX type. You can iterate over this collection during + program execution : it could be interesting for example to generate DDL SQL schema and to + take into account some validation rules into database (read the chapter : Generate database DDL SQL schema) ;
    • +
    • qx::QxInvalidValueX : when an instance is invalid, list of + constraints violation are inserted into a qx::QxInvalidValueX collection ;
    • +
    • qx::QxInvalidValue : each item into this collection is a + qx::QxInvalidValue type and contains an error message (description to explain why + the instance is not valid). +
    • +
    + QxValidator module manages automatically class inheritance : each constraint defined into + a base class is checked during validation process of a derived class.
    +
    + Here is an example using QxValidator module with a 'person' class :
    +
    + * 'person.h' file :
    + + + + + + + +
    +
    #ifndef _CLASS_PERSON_H_
    +#define _CLASS_PERSON_H_
    + 
    +class person
    +{
    +
    +public:
    +
    +   enum sex { male, female, unknown };
    +
    +   long        _id;
    +   QString     _firstName;
    +   QString     _lastName;
    +   QDateTime   _birthDate;
    +   sex         _sex;
    +
    +   person() : _id(0), _sex(unknown) { ; }
    +   person(long id) : _id(id), _sex(unknown) { ; }
    +   virtual ~person() { ; }
    +
    +private:
    +
    +   void isValid(qx::QxInvalidValueX & invalidValues);
    +
    +};
    +
    +QX_REGISTER_HPP_MY_EXE(person, qx::trait::no_base_class_defined, 0)
    +
    +#endif // _CLASS_PERSON_H_
    +
    +
    + * 'person.cpp' file :
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/person.h"
    +#include "../include/global_validator.h"
    +
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_MY_EXE(person)
    +
    +namespace qx {
    +template <> void register_class(QxClass<person> & t)
    +{
    +   t.id(& person::_id, "id");
    +
    +   t.data(& person::_firstName, "firstName");
    +   t.data(& person::_lastName, "lastName");
    +   t.data(& person::_birthDate, "birthDate");
    +   t.data(& person::_sex, "sex");
    +
    +   QxValidatorX<person> * pAllValidator = t.getAllValidator();
    +   pAllValidator->add_NotEmpty("firstName");
    +   pAllValidator->add_NotEmpty("lastName", "a person must have a lastname");
    +   pAllValidator->add_CustomValidator(& person::isValid);
    +   pAllValidator->add_CustomValidator_QVariant(& validateFirstName, "firstName");
    +   pAllValidator->add_CustomValidator_DataType<QDateTime>(& validateDateTime, "birthDate");
    +}}
    +
    +void person::isValid(qx::QxInvalidValueX & invalidValues)
    +{
    +   // This method is called automatically by 'QxValidator' module (validator engine of QxOrm library) :
    +   // - when you try to insert or update using 'qx::dao::xxx' functions
    +   // - when you call 'qx::validate()' function
    +
    +   // For registration, see 'pAllValidator->add_CustomValidator(& person::isValid);' into 'qx::register_class' function
    +
    +   // Here, you can verify some values of your instance
    +   // If a value is not valid, you must add an invalid value into the collection 'invalidValues'
    +
    +   // For example, if we want to check property '_sex' of a person :
    +   if ((_sex != male) && (_sex != female))
    +   { invalidValues.insert("person sex must be defined : male or female"); }
    +}
    +
    +
    + * 'global_validator.h' file :
    + + + + + + + +
    +
    // Example of global functions 'validateFirstName' and 'validateDateTime' used by 'QxValidator' module
    +// Those functions will be called automatically by validator engine of QxOrm library :
    +// - when you try to insert or update using 'qx::dao::xxx' functions
    +// - when you call 'qx::validate()' function
    + 
    +void validateFirstName(const QVariant & value, const qx::IxValidator * validator, qx::QxInvalidValueX & invalidValues)
    +{
    +   // Here you can test the value (converted to QVariant type)
    +   // If an invalid value is detected, just add a message into 'invalidValues' collection
    +
    +   // For example, if the value must be never equal to "admin" :
    +   if (value.toString() == "admin")
    +   { invalidValues.insert("value must not be equal to 'admin'"); }
    +}
    +
    +void validateDateTime(const QDateTime & value, const qx::IxValidator * validator, qx::QxInvalidValueX & invalidValues)
    +{
    +   // Here you can test the value (with its real type, in this example, the data-member is a 'QDateTime' type)
    +   // If an invalid value is detected, just add a message into 'invalidValues' collection
    +
    +   // For example, if the date-time must be valid :
    +   if (! value.isValid())
    +   { invalidValues.insert("date-time value must not be empty and must be valid"); }
    +}
    +
    +
    + * 'main.cpp' file :
    + + + + + + + +
    +
    person personValidate;
    +personValidate._lastName = "admin";
    +qx::QxInvalidValueX invalidValues = qx::validate(personValidate);
    +QString sInvalidValues = invalidValues.text();
    +qDebug("[QxOrm] test 'QxValidator' module :\n%s", qPrintable(sInvalidValues));
    +
    +
    + During program execution of above source code, 'personValidate' instance is not valid : + 'invalidValues' collection contains 4 items :
    + - "property 'firstName' must not be empty" ;
    + - "person sex must be defined : male or female" ;
    + - "value must not be equal to 'admin'" ;
    + - "date-time value must not be empty and must be valid".
    +
    + QxValidator module provides some built-in constraints, which cover most of the basic data + checks.
    + As we'll see later, you're not limited to them, you can literally in a minute write your own + constraints : +
      +
    • add_NotNull() : checks if the value is not null ;
    • +
    • add_NotEmpty() : checks if the string is not empty ;
    • +
    • add_MinValue() : checks if the value is more than or equals to min ;
    • +
    • add_MaxValue() : checks if the value is less than or equals to max ;
    • +
    • add_Range() : checks if the value is between Min and Max (included) ;
    • +
    • add_MinDecimal() : checks if the decimal value is more than or equals to min ; +
    • +
    • add_MaxDecimal() : checks if the decimal value is less than or equals to max ; +
    • +
    • add_RangeDecimal() : checks if the decimal value is between Min and Max (included) + ;
    • +
    • add_MinLength() : checks if the string length is more than or equals to min ;
    • +
    • add_MaxLength() : checks if the string length is less than or equals to max ;
    • +
    • add_Size() : checks if the string length is between the min-max range ;
    • +
    • add_DatePast() : checks if the date is in the past ;
    • +
    • add_DateFuture() : checks if the date is in the future ;
    • +
    • add_RegExp() : checks if the property matches the regular expression given a match + flag ;
    • +
    • add_EMail() : checks whether the string conforms to the email address + specification.
    • +
    + Like 'person' class example, it's possible to define a custom validator : it's a function + or a class method called automatically by QxValidator module to validate a property or an + instance of class.
    + There are 3 kinds of custom validator : +
      +
    • add_CustomValidator() : class method, method signature must be "void + my_class::my_method(qx::QxInvalidValueX &)" ;
    • +
    • add_CustomValidator_QVariant() : global function with QVariant type (the + property is converted into QVariant type), function signature must be "void + my_validator(const QVariant &, const qx::IxValidator *, qx::QxInvalidValueX &)" ; +
    • +
    • add_CustomValidator_DataType() : global function with real type, function + signature must be "void my_validator(const T &, const qx::IxValidator *, + qx::QxInvalidValueX &)" ;
    • +
    + Note : each validator can be associated with a group (optional parameter for each + function add_XXX() of qx::IxValidatorX class).
    + So it's possible to create a context validation during program execution : for example, a person + from IHM A can have different validation rules than a person from IHM B.
    + To execute a validation process by group (for example "myGroup"), you have to call the + following function : "qx::QxInvalidValueX invalidValues = qx::validate(personValidate, + "myGroup");".
    +
    + Other note : QxValidator module provides default messages when a constraint + violation is detected.
    + It's possible to modify these default messages (for example, a translation) using the following + collection : "QHash * lstMessage = + QxClassX::getAllValidatorMessage();".
    + For example : "lstMessage->insert("min_value", "la valeur '%NAME%' doit �tre inf�rieure ou + �gale � '%CONSTRAINT%'");".
    + %NAME% and %CONSTRAINT% fields will be automatically replaced by the good + value.
    + To modify a message for a specific validator (and not globally), you have to use the optional + parameter provided by each function add_XXX() of qx::IxValidatorX class. +

    +
    +

    Manage NULL database + value

    +
    + All databases manages NULL value : for more details about NULL value, please read the Wikipedia + web page.
    + QxOrm library provides several ways to manage NULL value : +
      +
    • using boost::optional class provided by boost library ;
    • +
    • using QVariant class provided by Qt framework ;
    • +
    • using pointers or smart-pointers : a C++ NULL pointer is associated to a NULL value in + database.
    • +
    +
    +

    boost::optional +

    +
    + boost::optional<T> class provided by boost is the best solution + to manage NULL value in C++.
    + To use boost::optional<T> with QxOrm library, you must define + _QX_ENABLE_BOOST compilation option, or include + <QxExtras/QxBoostOptionalOnly.h> header file.
    + Here is an example where all properties (except primary key) can be NULL using boost::optional class :
    +
    + + + + + + + +
    +
    #ifndef _PERSON_H_
    +#define _PERSON_H_
    +
    +class person
    +{
    +public:
    +   long id;
    +   boost::optional<QString> firstName;
    +   boost::optional<QString> lastName;
    +   boost::optional<QDateTime> birthDate;  
    +
    +   person() : id(0) { ; }
    +   virtual ~person() { ; }
    +};
    +
    +#endif // _PERSON_H_
    +
    +
    + boost::optional<T> class is very easy to use : please read documentation on boost website for more details. +

    +
    +

    QVariant

    +
    + QVariant class provided by + Qt is another way to manage NULL value in C++.
    + Here is a class example where all values (except primary key) can be NULL using QVariant class :
    +
    + + + + + + + +
    +
    #ifndef _PERSON_H_
    +#define _PERSON_H_
    +
    +class person
    +{
    +public:
    +   long id;
    +   QVariant firstName;
    +   QVariant lastName;
    +   QVariant birthDate;  
    +
    +   person() : id(0) { ; }
    +   virtual ~person() { ; }  
    +};
    +
    +#endif // _PERSON_H_
    +
    +
    + This solution is not perfect compared to boost::optional<T> because you loose the property type.
    + So it is recommended to work with boost::optional<T> class to manage NULL value with QxOrm + library. +

    +
    +
    +

    Inheritance and + polymorphism

    +
    + With ORM tools, there are usually 3 strategies to manage inheritance and database : + + QxOrm works by default with Concrete Table Inheritance strategy (others are not + supported yet).
    + Many tutorials and forums are available on internet for more details about ORM + inheritance and database.
    + You can find a sample in ./test/qxDllSample/dll2/ directory of QxOrm package with + BaseClassTrigger class. +

    +
    +

    qx::IxPersistable + interface (abstract class)

    +
    + qx::IxPersistable interface (or abstract class) provides only pure + virtual methods.
    + Using qx::IxPersistable, you will have a common base class to call all persistents + functions without knowing the real type of current instance (polymorphism concept).
    + QxOrm library doesn't force developers to work with a base class to register a persistent type + in QxOrm context, however it's sometimes useful to have an interface to write some generic + algorithms.
    +
    + qx::IxPersistable class provides following virtual methods (for more details about these + methods, goto QxOrm library online class + documentation) :
    +
    +
    +
    virtual long qxCount(const qx::QxSqlQuery & query = qx::QxSqlQuery(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxFetchById(const QVariant & id = QVariant(), const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxFetchAll(qx::IxCollection & list, const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxFetchByQuery(const qx::QxSqlQuery & query, qx::IxCollection & list, const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxInsert(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxUpdate(const qx::QxSqlQuery & query = qx::QxSqlQuery(), const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxSave(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDeleteById(const QVariant & id = QVariant(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDeleteAll(QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDeleteByQuery(const qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDestroyById(const QVariant & id = QVariant(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDestroyAll(QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDestroyByQuery(const qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL);
    +virtual qx_bool qxExist(const QVariant & id = QVariant(), QSqlDatabase * pDatabase = NULL);
    +virtual qx::QxInvalidValueX qxValidate(const QStringList & groups = QStringList());
    +virtual qx::IxPersistableCollection_ptr qxNewPersistableCollection() const;
    +virtual qx::IxClass * qxClass() const;
    +
    +
    + For example, working with a list of qx::IxPersistable pointers, it's possible to save all + items to several database tables, like this :
    +
    + + + + + + + +
    +
    QList<qx::IxPersistable *> lst = ...;
    +foreach(qx::IxPersistable * p, lst)
    +{
    +   QSqlError daoError = p->qxSave();
    +   if (daoError.isValid()) { /* an error occured */ }
    +   // etc...
    +}
    +
    +
    + To implement qx::IxPersistable interface, it's necessary to : +
      +
    • inherit persistent class from qx::IxPersistable ;
    • +
    • into class definition (myClass.h for example), add + QX_PERSISTABLE_HPP(myClass) macro ; +
    • +
    • into class implementation (myClass.cpp for example), add + QX_PERSISTABLE_CPP(myClass) macro. +
    • +
    + For example, to implement qx::IxPersistable interface for author class from qxBlog tutorial, you have + to write :
    +
    + + + + + + + +
    +
    #ifndef _QX_BLOG_AUTHOR_H_
    +#define _QX_BLOG_AUTHOR_H_
    +
    +class blog;
    +
    +class QX_BLOG_DLL_EXPORT author : public qx::IxPersistable
    +{
    +   QX_PERSISTABLE_HPP(author)
    +public:
    +// -- typedef
    +   typedef boost::shared_ptr<blog> blog_ptr;
    +   typedef std::vector<blog_ptr> list_blog;
    +// -- enum
    +   enum enum_sex { male, female, unknown };
    +// -- properties
    +   QString     m_id;
    +   QString     m_name;
    +   QDate       m_birthdate;
    +   enum_sex    m_sex;
    +   list_blog   m_blogX;
    +// -- contructor, virtual destructor
    +   author() : m_id(0), m_sex(unknown) { ; }
    +   virtual ~author() { ; }
    +// -- methods
    +   int age() const;
    +};
    +
    +QX_REGISTER_PRIMARY_KEY(author, QString)
    +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0)
    +
    +typedef boost::shared_ptr<author> author_ptr;
    +typedef qx::QxCollection<QString, author_ptr> list_author;
    +
    +#endif // _QX_BLOG_AUTHOR_H_
    +
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/author.h"
    +#include "../include/blog.h"
    +
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_BLOG(author)
    +QX_PERSISTABLE_CPP(author)
    +
    +namespace qx {
    +template <> void register_class(QxClass<author> & t)
    +{
    +   t.id(& author::m_id, "author_id");
    +
    +   t.data(& author::m_name, "name");
    +   t.data(& author::m_birthdate, "birthdate");
    +   t.data(& author::m_sex, "sex");
    +
    +   t.relationOneToMany(& author::m_blogX, "list_blog", "author_id");
    +
    +   t.fct_0<int>(& author::age, "age");
    +}}
    +
    +int author::age() const
    +{
    +   if (! m_birthdate.isValid()) { return -1; }
    +   return (QDate::currentDate().year() - m_birthdate.year());
    +}
    +
    +
    +
    + Note : project test from ./test/qxDllSample/dll1/ directory provides a kind of + 'super base class' : qx::QxPersistable class implements qx::IxPersistable interface and inherits from QObject.
    + So SIGNAL-SLOT engine of Qt library can be used with this class and could be an + interesting way to use QxOrm triggers.
    + qx::QxPersistable class provides also some virtual methods to override to manage for + example data validation process from QxValidator module.
    + For information, qx::QxPersistable class is not a part of QxOrm library, but you can + copy-past it into your own project to use all its features : + +
    +
    +

    Use PIMPL C++ pattern + (Private Implementation idiom or d-pointer)

    +
    + From cppreference + website : "Pointer to implementation" or "pImpl" is a C++ programming technique that + removes implementation details of a class from its object representation by placing them in a + separate class, accessed through an opaque pointer. + This technique is used to construct C++ library interfaces with stable ABI and to reduce + compile-time dependencies. +

    + Advantages of PIMPL pattern for C++ persistent classes registered into + QxOrm context : +
      +
    • Compilation Firewall : if the private implementation changes, the client code + doesn't have to be recompiled ;
    • +
    • Reduce compilation times : headers files (*.h, *.hpp) are smaller ;
    • +
    • Binary Compatibility : as long as the binary interface stays the same, you can + link your app to a different version of a library ;
    • +
    • Reduce the size of generated binaries (*.dll, *.so, *.exe, etc...).
    • +
    + Disadvantages of PIMPL : +
      +
    • Performance : one level of indirection is added ;
    • +
    • Memory Management : a memory chunk has to be allocated (or preallocated) for the private + implementation (can issue memory fragmentation).
    • +
    +
    + QxOrm library provides a sample project where all persistent classes are developed using the + PIMPL pattern : qxBlogPImpl (with relationships).
    + It is also possible (and recommended) to use QxEntityEditor + application to export automatically all C++ persistent classes of a project + with the PIMPL option.
    +
    + Here is an example of a C++ class registered into QxOrm context developed with the PIMPL idiom + (with 1-n, n-1 and n-n relationships) :
    +
    + + + + + + + +
    +
    #ifndef _QX_BLOG_BLOG_H_
    +#define _QX_BLOG_BLOG_H_
    +
    +class author;
    +class comment;
    +class category;
    +
    +class QX_BLOG_DLL_EXPORT blog
    +{
    +
    +   QX_REGISTER_FRIEND_CLASS(blog)
    +
    +private:
    +
    +   struct blog_impl;
    +   std::unique_ptr<blog_impl> m_pImpl; //!< Private implementation idiom
    +
    +public:
    +
    +   blog();
    +   virtual ~blog();
    +
    +   blog(const blog & other);
    +   blog & operator=(const blog & other);
    +
    +#ifdef Q_COMPILER_RVALUE_REFS
    +   blog(blog && other) Q_DECL_NOEXCEPT;
    +   blog & operator=(blog && other) Q_DECL_NOEXCEPT;
    +#endif // Q_COMPILER_RVALUE_REFS
    +
    +   long id() const;
    +   QString text() const;
    +   QDateTime dateCreation() const;
    +
    +   void setId(long l);
    +   void setText(const QString & s);
    +   void setDateCreation(const QDateTime & d);
    +
    +   std::shared_ptr<author> & getAuthor();
    +   QList< std::shared_ptr<comment> > & listOfComments();
    +   qx::QxCollection<long, QSharedPointer<category> > & listOfCategories();
    +
    +};
    +
    +QX_REGISTER_HPP_QX_BLOG(blog, qx::trait::no_base_class_defined, 0)
    +
    +typedef std::shared_ptr<blog> blog_ptr;
    +typedef std::vector<blog_ptr> list_blog;
    +
    +#endif // _QX_BLOG_BLOG_H_
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/blog.h"
    +#include "../include/author.h"
    +#include "../include/comment.h"
    +#include "../include/category.h"
    +
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_BLOG(blog)
    +
    +struct Q_DECL_HIDDEN blog::blog_impl
    +{
    +   long           m_id;
    +   QString        m_text;
    +   QDateTime      m_dt_creation;
    +   author_ptr     m_author;
    +   list_comment   m_commentX;
    +   list_category  m_categoryX;
    +
    +   blog_impl() : m_id(0) { ; }
    +   ~blog_impl() { ; }
    +};
    +
    +namespace qx {
    +template <> void register_class(QxClass<blog> & t)
    +{
    +   IxDataMember * pImpl = t.pimpl(& blog::m_pImpl);
    +
    +   t.id(& blog::blog_impl::m_id, "blog_id", 0, pImpl);
    +
    +   t.data(& blog::blog_impl::m_text, "blog_text", 0, true, true, pImpl);
    +   t.data(& blog::blog_impl::m_dt_creation, "date_creation", 0, true, true, pImpl);
    +
    +   t.relationManyToOne(& blog::blog_impl::m_author, "author_id", 0, pImpl);
    +   t.relationOneToMany(& blog::blog_impl::m_commentX, "list_comment", "blog_id", 0, pImpl);
    +   t.relationManyToMany(& blog::blog_impl::m_categoryX, "list_category", "category_blog", "blog_id", "category_id", 0, pImpl);
    +}}
    +
    +blog::blog() : m_pImpl(new blog_impl()) { ; }
    +
    +blog::~blog() { ; }
    +
    +blog::blog(const blog & other) : m_pImpl(new blog_impl(* other.m_pImpl)) { ; }
    +
    +blog & blog::operator=(const blog & other)
    +{
    +   if (this != (& other)) { (* m_pImpl) = (* other.m_pImpl); }
    +   return (* this);
    +}
    +
    +#ifdef Q_COMPILER_RVALUE_REFS
    +blog::blog(blog && other) Q_DECL_NOEXCEPT : m_pImpl(std::move(other.m_pImpl)) { ; }
    +blog & blog::operator=(blog && other) Q_DECL_NOEXCEPT { if (this != (& other)) { m_pImpl = std::move(other.m_pImpl); }; return (* this); }
    +#endif // Q_COMPILER_RVALUE_REFS
    +
    +long blog::id() const { return m_pImpl->m_id; }
    +
    +QString blog::text() const { return m_pImpl->m_text; }
    +
    +QDateTime blog::dateCreation() const { return m_pImpl->m_dt_creation; }
    +
    +void blog::setId(long l) { m_pImpl->m_id = l; }
    +
    +void blog::setText(const QString & s) { m_pImpl->m_text = s; }
    +
    +void blog::setDateCreation(const QDateTime & d) { m_pImpl->m_dt_creation = d; }
    +
    +std::shared_ptr<author> & blog::getAuthor() { return m_pImpl->m_author; }
    +
    +QList< std::shared_ptr<comment> > & blog::listOfComments() { return m_pImpl->m_commentX; }
    +
    +qx::QxCollection<long, QSharedPointer<category> > & blog::listOfCategories() { return m_pImpl->m_categoryX; }
    +
    +

    +
    +

    Persist custom type +

    +
    + QxOrm library can persist every types, not only classes registered in QxOrm context using + qx::register_class<T>().
    +
    + It's necessary to write serialization functions from boost framework, using the non + intrusive method (because source code is not available or is read-only). + For more details on boost serialization module, goto official website.
    +
    + For example, imagine that you have the class 'ExtObject3D' from an external library and + the source code is not available or is read-only. + Here is the code to can persist an instance of 'ExtObject3D' type into database :
    +
    + + + + + + + +
    +
    #ifndef _PERSIST_EXTOBJECT3D_H_
    +#define _PERSIST_EXTOBJECT3D_H_
    +
    +#include "ExtObject3D.h"
    +
    +#include <boost/serialization/serialization.hpp>
    +#include <boost/serialization/split_free.hpp>
    +#include <boost/serialization/nvp.hpp>
    + 
    +namespace boost {
    +namespace serialization {
    +
    +template <class Archive>
    +void save(Archive & ar, const ExtObject3D & t, unsigned int version)
    +{
    +   Q_UNUSED(version);
    +   double x(t.getX()), y(t.getY()), z(t.getZ()), angle(t.getAngle());
    +
    +   ar << boost::serialization::make_nvp("x", x);
    +   ar << boost::serialization::make_nvp("y", y);
    +   ar << boost::serialization::make_nvp("z", z);
    +   ar << boost::serialization::make_nvp("angle", angle);
    +}
    +
    +template <class Archive>
    +void load(Archive & ar, ExtObject3D & t, unsigned int version)
    +{
    +   Q_UNUSED(version);
    +   double x(0.0), y(0.0), z(0.0), angle(0.0);
    +
    +   ar >> boost::serialization::make_nvp("x", x);
    +   ar >> boost::serialization::make_nvp("y", y);
    +   ar >> boost::serialization::make_nvp("z", z);
    +   ar >> boost::serialization::make_nvp("angle", angle);
    +
    +   t.setX(x);
    +   t.setY(y);
    +   t.setZ(z);
    +   t.setAngle(angle);
    +}
    +
    +} // namespace serialization
    +} // namespace boost
    + 
    +BOOST_SERIALIZATION_SPLIT_FREE(ExtObject3D)
    +
    +#endif // _PERSIST_EXTOBJECT3D_H_
    +
    +
    + Now you can persist an instance of 'ExtObject3D' type into database : so you can have a + 'ExtObject3D' property in a persistent class registered in QxOrm context. + This property can be mapped with a column of type TEXT or VARCHAR into + database.
    +
    + The default behaviour of QxOrm library is : the instance is serialized to XML format before + being inserted or updated into database. + This default behaviour can be useful, for example if you want to save a collection of items + without to make relation (so you don't have to manage another table into database). + For example, with a property of type std::vector<mon_objet> in a persistent class + without relation, the list of items will be saved into database under XML format.
    +
    + Note : the default behaviour can be easily modified for a specific type. + QtSql engine uses QVariant type to link C++ code and database. + QVariant type can contain text, numeric, binary, etc. + So it can be interesting to specialize the default behaviour (XML serialization) if you want to + save datas under binary format or to optimize your application (XML serialization is not very + fast). + You just have to write (with boost serialization functions) a conversion into/from + QVariant type, for example with 'ExtObject3D' class :
    +
    + + + + + + + +
    +
    namespace qx {
    +namespace cvt {
    +namespace detail {
    +
    +template <> struct QxConvert_ToVariant< ExtObject3D > {
    +static inline QVariant toVariant(const ExtObject3D & t, const QString & format, int index, qx::cvt::context::ctx_type ctx)
    +{ /* Here I convert from ExtObject3D to QVariant */ } };
    +
    +template <> struct QxConvert_FromVariant< ExtObject3D > {
    +static inline qx_bool fromVariant(const QVariant & v, ExtObject3D & t, const QString & format, int index, qx::cvt::context::ctx_type ctx)
    +{ /* Here I convert from QVariant to ExtObject3D */; return qx_bool(true); } };
    +
    +} // namespace detail
    +} // namespace cvt
    +} // namespace qx
    +
    +

    + Note : here is a template to create a custom persistable type : +

    + + + + + + + +
    +
    #ifndef _MY_CUSTOM_PERSISTABLE_TYPE_H_
    +#define _MY_CUSTOM_PERSISTABLE_TYPE_H_
    +
    +#ifdef _MSC_VER
    +#pragma once
    +#endif
    +
    +#include <QxOrm.h>
    +
    +class MyPersistableType
    +{
    +   /* What you want here */
    +};
    +
    +QX_REGISTER_CLASS_NAME(MyPersistableType)
    +QX_CLASS_VERSION(MyPersistableType, 0)
    +
    +QDataStream & operator<< (QDataStream & stream, const MyPersistableType & t)
    +{
    +   /* Your implementation here */
    +}
    +
    +QDataStream & operator>> (QDataStream & stream, MyPersistableType & t)
    +{
    +   /* Your implementation here */
    +}
    +
    +namespace qx {
    +namespace cvt {
    +namespace detail {
    +
    +template <> struct QxConvert_ToVariant< MyPersistableType > {
    +static inline QVariant toVariant(const MyPersistableType & t, const QString & format, int index, qx::cvt::context::ctx_type ctx)
    +{
    +   /* Here I convert from MyPersistableType to QVariant */
    +} };
    +
    +template <> struct QxConvert_FromVariant< MyPersistableType > {
    +static inline qx_bool fromVariant(const QVariant & v, MyPersistableType & t, const QString & format, int index, qx::cvt::context::ctx_type ctx)
    +{
    +   /* Here I convert from QVariant to MyPersistableType */
    +   return qx_bool(true);
    +} };
    +
    +} // namespace detail
    +} // namespace cvt
    +} // namespace qx
    +
    +#ifndef _QX_NO_JSON
    +
    +namespace qx {
    +namespace cvt {
    +namespace detail {
    +
    +template <>
    +struct QxConvert_ToJson< MyPersistableType >
    +{
    +   static inline QJsonValue toJson(const MyPersistableType & t, const QString & format)
    +   {
    +      /* Your implementation here */
    +   }
    +};
    +
    +template <>
    +struct QxConvert_FromJson< MyPersistableType >
    +{
    +   static inline qx_bool fromJson(const QJsonValue & j, MyPersistableType & t, const QString & format)
    +   {
    +      /* Your implementation here */
    +   }
    +};
    +
    +} // namespace detail
    +} // namespace cvt
    +} // namespace qx
    +
    +#endif // _QX_NO_JSON
    +
    +// ------------------------------------
    +// If you are using boost serialization, you have also to implement save/load functions like above 'ExtObject3D' example
    +// ------------------------------------
    +
    +#endif // _MY_CUSTOM_PERSISTABLE_TYPE_H_
    +
    +

    +
    +

    Generate database DDL SQL + schema

    +
    + + !!! It's strongly recommended to use + QxEntityEditor application to manage DDL SQL schema generation !!! + +
    +
    + QxOrm library doesn't provide a generator to create and to update automatically tables into + database.
    + Indeed, qx::dao::create_table<T> function must be used only to create prototypes or + samples.
    + It's strongly recommended to work with a tool provided by each SGBD to design and to manage + tables into database (for example Navicat with MySql, pgAdmin with + PostgreSQL, SQLite Manager with SQLite, etc.).
    + Moreover, each tool provided by each SGBD can add some optimizations to the database (add some + indexes for example).
    +
    + But sometimes, it can be useful to not have to manage manually tables into database.
    + In this case, it's possible to create a C++ function to iterate over all persistents classes + registered in QxOrm context (using introspection engine of QxOrm library) : so you can build a + SQL script to create and to update tables into database.
    +
    + QxOrm library provides an example of a C++ function : based on this function, you can create + your own function to build SQL schema.
    + This QxOrm function is written in the file ./src/QxRegister/QxClassX.cpp and is called QString + qx::QxClassX::dumpSqlSchema().
    + This QxOrm function builds a SQL script and returns a QString value : it's also possible + to modify the function to generate a file with SQL script or to execute each SQL process + directly to the SGBD.
    +
    + Here is a sample implementation provided by dodobibi to + manage a PostgreSQL database : this sample works with a version number to add columns to + existing tables, to add some indexes to existing columns, etc.
    + When you start your application, a version number is provided and incremented when a new version + of your application is released :
    +
    + + + + + + + +
    +
    QApplication app(argc, argv);
    +app.setProperty("DomainVersion", 1);
    +
    +
    + A table into the database must be created to store this version number.
    + A C++ persistent class is mapped to this table :
    +
    + + + + + + + +
    +
    #ifndef _DATABASE_VERSION_H_
    +#define _DATABASE_VERSION_H_
    + 
    +class MY_DLL_EXPORT DatabaseVersion
    +{
    +public:
    +  QString name;
    +  long version;
    +};
    +
    +QX_REGISTER_HPP_MY_APP(DatabaseVersion, qx::trait::no_base_class_defined, 0)
    +
    +#endif // _DATABASE_VERSION_H_
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +#include "../include/DatabaseVersion.h"
    +#include <QxOrm_Impl.h>
    + 
    +QX_REGISTER_CPP_MY_APP(DatabaseVersion)
    +
    +namespace qx {
    +template <> void register_class(QxClass<DatabaseVersion> & t)
    +{
    +  t.id(& DatabaseVersion::name, "name");
    +  t.data(& DatabaseVersion::version, "version");
    +}}
    +
    +
    + With DatabaseVersion class, it's possible to verify that the database must be updated or + not.
    + This is the goal of isDatabaseVersionOld() function :
    +
    + + + + + + + +
    +
    bool isDatabaseVersionOld()
    +{
    +  DatabaseVersion dbVersion;
    +  dbVersion.name = "MyAppName";
    +  QSqlError err = qx::dao::fetch_by_id(dbVersion);
    +  if (err.isValid()) { qAssert(false); return false; }
    +  return (dbVersion.version < qApp->property("DomainVersion").toInt());
    +}
    +
    +
    + If isDatabaseVersionOld() function returns true when you start your application, + then you must update your SQL schema :
    +
    + + + + + + + +
    +
    void updateDatabaseVersion()
    +{
    +  try
    +  {
    +    int domainVersion = qApp->property("DomainVersion").toInt();
    +
    +    // Connect to the database with a user with modifications rights on SQL schema
    +    QSqlDatabase db = qx::QxSqlDatabase::getSingleton()->getDatabaseCloned();
    +    db.setUserName("MyAdminLogin");
    +    db.setPassword("MyAdminPassword");
    +
    +    // Create a session, open automatically a transaction and throw an exception when an error occured
    +    qx::QxSession session(db, true, true);
    +
    +    // Fetch the database version with a lock to protect the database
    +    DatabaseVersion dbVersion;
    +    session.fetchByQuery(qx_query("WHERE name='MyAppName' FOR UPDATE"), dbVersion);
    +
    +    // When unlocked for other users, verify that the database must be updated or not
    +    if (dbVersion.version >= domainVersion) { return; }
    +
    +    // Execute each SQL process with "query" variable
    +    QSqlQuery query(db);
    +
    +    // Fetch all C++ persistents classes registered in QxOrm context
    +    qx::QxCollection<QString, qx::IxClass *> * pAllClasses = qx::QxClassX::getAllClasses();
    +    if (! pAllClasses) { qAssert(false); return; }
    +
    +    // Fetch all tables into database (this is a Qt function)
    +    QStringList tables = db.tables();
    +
    +    for (long k = 0; k < pAllClasses->count(); k++)
    +    {
    +      qx::IxClass * pClass = pAllClasses->getByIndex(k);
    +      if (! pClass) { continue; }
    +
    +      // Filter non persitents classes
    +      if (pClass->isKindOf("qx::service::IxParameter") || pClass->isKindOf("qx::service::IxService")) { continue; }
    +
    +      // Filter already updated classes
    +      if (pClass->getVersion() <= dbVersion.version) { continue; }
    +
    +      // If table doesn't exist, create it and set the owner
    +      if (! tables.contains(pClass->getName()))
    +      {
    +        query.exec("CREATE TABLE " + pClass->getName() + " ( ) WITH (OIDS = FALSE);"
    +                   "ALTER TABLE " + pClass->getName() + " OWNER TO \"MyAdminLogin\";");
    +        session += query.lastError();
    +      }
    +
    +      // If a column doesn't exist, add it to the table
    +      qx::IxDataMemberX * pDataMemberX = pClass->getDataMemberX();
    +      for (long l = 0; (pDataMemberX && (l < pDataMemberX->count_WithDaoStrategy())); l++)
    +      {
    +        qx::IxDataMember * p = pDataMemberX->get_WithDaoStrategy(l);
    +        if (! p || (p->getVersion() <= dbVersion.version)) { continue; }
    +
    +        query.exec("ALTER TABLE " + pClass->getName() + " ADD COLUMN " + p->getName() + " " + p->getSqlType() + ";");
    +        session += query.lastError();
    +
    +        if (p->getIsPrimaryKey()) // PRIMARY KEY
    +        {
    +          query.exec("ALTER TABLE " + pClass->getName() + " ADD PRIMARY KEY (" + p->getName() + ");");
    +          session += query.lastError();
    +        }
    +
    +        if (p->getAllPropertyBagKeys().contains("INDEX")) // INDEX
    +        {
    +          query.exec("CREATE INDEX " + pClass->getName() + "_" + p->getName() + "_idx" + 
    +                     " ON " + pClass->getName() + " USING " + p->getPropertyBag("INDEX").toString() + " (" + p->getName() + ");");
    +          session += query.lastError();
    +        }
    +
    +        if (p->getNotNull()) // NOT NULL
    +        {
    +          query.exec("ALTER TABLE " + pClass->getName() + " ALTER COLUMN " + p->getName() + " SET NOT NULL;");
    +          session += query.lastError();
    +        }
    +
    +        if (p->getAutoIncrement()) // AUTO INCREMENT
    +        {
    +          query.exec("CREATE SEQUENCE " + pClass->getName() + "_" + p->getName() + "_seq" + "; "
    +                     "ALTER TABLE " + pClass->getName() + "_" + p->getName() + "_seq" + " OWNER TO \"MyAdminLogin\"; "
    +                     "ALTER TABLE " + pClass->getName() + " ALTER COLUMN " + p->getName() + " " +
    +                     "SET DEFAULT nextval('" + pClass->getName() + "_" + p->getName() + "_seq" + "'::regclass);");
    +          session += query.lastError();
    +        }
    +
    +        if (p->getDescription() != "") // DESCRIPTION
    +        {          query.exec("COMMENT ON COLUMN " + pClass->getName() + "." + p->getName() + " IS $$" + p->getDescription() + "$$ ;");
    +          session += query.lastError();
    +        }
    +      }
    +    }
    +
    +    // Save current version of the database
    +    dbVersion.version = domainVersion;
    +    session.save(dbVersion);
    +
    +    // End of "try" scope : session is destroyed => commit or rollback automatically
    +    // Moreover, a commit or a rollback unlock the process for all users
    +  }
    +  catch (const qx::dao::sql_error & err)
    +  {
    +    QSqlError sqlError = err.get();
    +    qDebug() << sqlError.databaseText();
    +    qDebug() << sqlError.driverText();
    +    qDebug() << sqlError.number();
    +    qDebug() << sqlError.type();
    +  }
    +}
    +
    +
    + Note : this code (like qx::QxClassX::dumpSqlSchema() function) can be modified to provide + more features.
    + For example, it could be interesting to create by default another table (like + DatabaseVersion table) to store the list of all persistents classes registered in QxOrm + context : instead of using "db.tables()" Qt function, it could be possible to fetch all + tables with more information (version number for each table, columns count registered in QxOrm + context, table description, etc.). +

    +
    +

    Associate a SQL type to a + C++ class

    +
    + Each database provides its own SQL types to store datas.
    + QxOrm library associates by default some C++ classes frequently used in a program :
    +
    + + + + + + + +
    +
    "bool" <-> "SMALLINT"
    +"qx_bool" <-> "SMALLINT"
    +"short" <-> "SMALLINT"
    +"int" <-> "INTEGER"
    +"long" <-> "INTEGER"
    +"long long" <-> "INTEGER"
    +"float" <-> "FLOAT"
    +"double" <-> "FLOAT"
    +"long double" <-> "FLOAT"
    +"unsigned short" <-> "SMALLINT"
    +"unsigned int" <-> "INTEGER"
    +"unsigned long" <-> "INTEGER"
    +"unsigned long long" <-> "INTEGER"
    +"std::string" <-> "TEXT"
    +"std::wstring" <-> "TEXT"
    +"QString" <-> "TEXT"
    +"QVariant" <-> "TEXT"
    +"QUuid" <-> "TEXT"
    +"QDate" <-> "DATE"
    +"QTime" <-> "TIME"
    +"QDateTime" <-> "TIMESTAMP"
    +"QByteArray" <-> "BLOB"
    +"qx::QxDateNeutral" <-> "TEXT"
    +"qx::QxTimeNeutral" <-> "TEXT"
    +"qx::QxDateTimeNeutral" <-> "TEXT"
    +
    +
    + If a SQL type provided by default by QxOrm library is not supported by the database, it can be + easily modified (globally for all the application) using the following collection :
    +
    + + + + + + + +
    +
    QHash<QString, QString> * lstSqlType = qx::QxClassX::getAllSqlTypeByClassName();
    +lstSqlType->insert("QString", "VARCHAR(255)");
    +lstSqlType->insert("std::string", "VARCHAR(255)");
    +// etc.
    +
    +
    + To modify a SQL type for a specific column of a table, you have to define the new SQL type in + the mapping function of QxOrm library :
    +
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<MyClass> & t)
    +{
    +  //...
    +  IxDataMember * p =  t.data(& MyClass::m_MyProperty, "my_property");
    +  p->setSqlType("VARCHAR(255)");
    +  //...
    +}}
    +
    +
    + For all classes not supported by default by QxOrm library (read chapter : Persist custom type), it's possible to associate a default SQL + type using the following macro (outside all namespace) :
    +
    + + + + + + + +
    +
    QX_REGISTER_TRAIT_GET_SQL_TYPE(MyClass, "my_sql_type")
    +
    +

    +
    +

    Async database + queries

    +
    + Sometimes, it's necessary to execute some queries to database in asynchronous way + (multi-thread), for example to avoid to freeze a GUI if a query is too long to execute.
    + To make easier to work with asynchronous queries, QxOrm library provides qx::QxDaoAsync class.
    + This class executes a query in another thread and returns the queryFinished() + SIGNAL when query is terminated.
    + To use qx::QxDaoAsync class, you just have to : +
      +
    • be careful to work only with classes implementing qx::IxPersistable interface ;
    • +
    • create an instance of qx::QxDaoAsync type (for example, a property of a + QWidget derived class) ; +
    • +
    • connect a SLOT to the qx::QxDaoAsync::queryFinished() SIGNAL (for + example, a SLOT of a QWidget derived class) ;
    • +
    • run a query using one of qx::QxDaoAsync::asyncXXXX() methods.
    • +
    + Here is an example with a class called MyWidget :
    +
    + + + + + + + +
    +
    class MyWidget : public QWidget
    +{ Q_OBJECT
    +
    +   //...
    +   qx::QxDaoAsync m_daoAsync;
    +   //...
    +Q_SLOTS:
    +   void onQueryFinished(const QSqlError & daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams);
    +   //...
    +
    +};
    +
    +
    + And here is the implementation of MyWidget class :
    +
    + + + + + + + +
    +
    MyWidget::MyWidget() : QObject()
    +{
    +   //...
    +   QObject::connect((& m_daoAsync), SIGNAL(queryFinished(const QSqlError &, qx::dao::detail::QxDaoAsyncParams_ptr)), 
    +                    this, SLOT(onQueryFinished(const QSqlError &, qx::dao::detail::QxDaoAsyncParams_ptr)));
    +   //...
    +}
    +
    +void MyWidget::onQueryFinished(const QSqlError & daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams)
    +{
    +   if (! pDaoParams) { return; }
    +   qx::QxSqlQuery query = pDaoParams->query;
    +   if (! daoError.isValid()) { ; }
    +   // If the async query is associated to a simple object, just use 'pDaoParams->pInstance' method
    +   qx::IxPersistable_ptr ptr = pDaoParams->pInstance;
    +   // If the async query is associated to a list of objects, just use 'pDaoParams->pListOfInstances' method
    +   qx::IxPersistableCollection_ptr lst = pDaoParams->pListOfInstances;
    +   //...
    +}
    +
    +

    +
    +

    Cache to store C++ + instances (QxCache module)

    +
    + Cache engine provided by QxOrm library (QxCache module) is thread-safe and can store easily any + kind of objects.
    + Functions to access to the cache engine are inside namespace + qx::cache.
    + qx::cache engine can provide a program optimization : you can for example store items + fetched by a query to database.
    +
    + Each item into the cache is associated with a key of type QString : this key provides a + quick access to an item stored into the cache.
    + If a new item is inserted with a key already in the cache, then the old item associated with + this key is removed automatically from the cache.
    +
    + Cache engine of QxOrm library doesn't manage memory : there is no delete called by the + cache engine.
    + This is why it's strongly recommended (but not an obligation) to store smart-pointers into the + cache : for example, boost::shared_ptr<T> of boost library or QSharedPointer<T> of Qt library.
    +
    + Cache engine can have a max cost to avoid too much memory usage : each item inserted to the + cache can be associated with a cost (for example, element's count of a collection).
    + When the limit (max cost) of the cache engine is reached, first items inserted to the cache are + automatically removed (insertion order) until limit of the cache is ok.
    +
    + It's also possible to associate a date-time insertion when an item is added to the cache.
    + If there is no date-time, then the current date-time is taken into account.
    + This feature provides a way to verify that an item stored into the cache must be updated or + not.
    +
    + Here is an example using cache engine of QxOrm library (functions into namespace + qx::cache) :
    +
    + + + + + + + +
    +
    // Define max cost of cache engine to 500
    +qx::cache::max_cost(500);
    +
    +// Fetch a list of 'author' from database
    +boost::shared_ptr< QList<author> > list_author;
    +QSqlError daoError = qx::dao::fetch_all(list_author);
    +
    +// Insert the list of 'author' to the cache
    +qx::cache::set("list_author", list_author);
    +
    +// Fetch a list of 'blog' from database
    +QSharedPointer< std::vector<blog> > list_blog;
    +daoError = qx::dao::fetch_all(list_blog);
    +
    +// Insert the list of 'blog' to the cache (cost = 'blog' count)
    +qx::cache::set("list_blog", list_blog, list_blog.count());
    +
    +// Pointer to an object of type 'comment'
    +comment_ptr my_comment;
    +my_comment.reset(new comment(50));
    +daoError = qx::dao::fetch_by_id(my_comment);
    +
    +// Insert 'comment' to the cache with a date-time insertion
    +qx::cache::set("comment", my_comment, 1, my_comment->dateModif());
    +
    +// Get the list of 'blog' stored into the cache
    +list_blog = qx::cache::get< QSharedPointer< std::vector<blog> > >("list_blog");
    +
    +// Get the list of 'blog' without providing the type
    +qx_bool bGetOk = qx::cache::get("list_blog", list_blog);
    +
    +// Remove list of 'author' from cache
    +bool bRemoveOk = qx::cache::remove("list_author");
    +
    +// Get items count stored into the cache
    +long lCount = qx::cache::count();
    +
    +// Get current cost of items stored into the cache
    +long lCurrentCost = qx::cache::current_cost();
    +
    +// Verify that an element with the key "comment" exists into the cache
    +bool bExist = qx::cache::exist("comment");
    +
    +// Get 'comment' stored into the cache with its date-time insertion
    +QDateTime dt;
    +bGetOk = qx::cache::get("comment", my_comment, dt);
    +
    +// Clear the cache
    +qx::cache::clear();
    +
    +

    +
    +

    Working with several + databases

    +
    + In the Connection to database chapter, we have seen how to configure + default connection to database using singleton class : qx::QxSqlDatabase. + QxOrm library is based on Qt QtSql engine, so QxOrm uses internally the Qt QSqlDatabase class. + All functions to communicate with databases (qx::dao namespace, qx::QxSession class, etc...) have an optional parameter named : + QSqlDatabase * pDatabase = NULL : +
      +
    • if value of this parameter is NULL (which is the default value) : then QxOrm library uses + qx::QxSqlDatabase settings to connect to database (support + multi-threading) ; +
    • +
    • if value is not NULL : then QxOrm library uses connection provided by this QSqlDatabase * + pDatabase pointer.
    • +
    + So this optional parameter can be used to manage your own connection pool and/or to connect to + several databases. +

    +
    +

    Register an abstract + class in QxOrm context

    +
    + A C++ abstract class (with at least one pure virtual method) cannot be mapped to a database + table (because it cannot be instantiated).
    + However, in some cases, it can be interesting to define properties into abstract class used by a + persistent object (by inheritance).
    + A sample of abstract class registered in QxOrm context is available in + ./test/qxDllSample/dll2/ directory of QxOrm package with BaseClassTrigger + class.
    + To register an abstract class in QxOrm context, you have to : +
      +
    • register the class with 'void register_class' like any other class ; +
    • +
    • use QX_REGISTER_ABSTRACT_CLASS(className) macro just after the class definition. +
    • +
    +
    +
    +

    Register automatically Qt + meta-properties (Q_PROPERTY macro)

    +
    + All classes inherited from QObject type can use Q_PROPERTY + macro : these properties become meta-properties. + This is how Qt framework provides an introspection engine using the moc process. + Meta-properties can be used for example by QML engine, QtScript, etc.
    +
    + QxOrm library requires to register all properties per class in the void + qx::register_class<T>() mapping function to provide all features (persistence, XML, + JSON and binary serialization, etc.). + It's possible to register automatically all Qt meta-properties in QxOrm context without having + to manage any mapping function per class void qx::register_class<T>() : + QX_REGISTER_ALL_QT_PROPERTIES() macro works with Qt introspection engine to iterate over + all meta-properties.
    +
    + Here is an example with TestQtProperty class in ./test/qxDllSample/dll1/include/ + directory of QxOrm package :
    +
    + + + + + + + +
    +
    #ifndef _QX_TEST_QT_META_PROPERTY_H_
    +#define _QX_TEST_QT_META_PROPERTY_H_
    + 
    +class QX_DLL1_EXPORT TestQtProperty : public QObject
    +{
    +
    +   Q_OBJECT
    +   Q_PROPERTY(int id READ id WRITE setId)
    +   Q_PROPERTY(long number READ number WRITE setNumber)
    +   Q_PROPERTY(QString desc READ desc WRITE setDesc)
    +   Q_PROPERTY(QDateTime birthDate READ birthDate WRITE setBirthDate)
    +   Q_PROPERTY(QVariant photo READ photo WRITE setPhoto)
    +
    +protected:
    +
    +   int         m_id;
    +   long        m_number;
    +   QString     m_desc;
    +   QDateTime   m_birthDate;
    +   QVariant    m_photo;
    +
    +public:
    +
    +   TestQtProperty() : QObject(), m_id(0), m_number(0) { ; }
    +   virtual ~TestQtProperty() { ; }
    +
    +   int id() const                { return m_id; }
    +   long number() const           { return m_number; }
    +   QString desc() const          { return m_desc; }
    +   QDateTime birthDate() const   { return m_birthDate; }
    +   QVariant photo() const        { return m_photo; }
    +
    +   void setId(int i)                         { m_id = i; }
    +   void setNumber(long l)                    { m_number = l; }
    +   void setDesc(const QString & s)           { m_desc = s; }
    +   void setBirthDate(const QDateTime & dt)   { m_birthDate = dt; }
    +   void setPhoto(const QVariant & v)         { m_photo = v; }
    + 
    +};
    +
    +QX_REGISTER_HPP_QX_DLL1(TestQtProperty, QObject, 0)
    +
    +#endif // _QX_TEST_QT_META_PROPERTY_H_
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/TestQtProperty.h"
    +
    +#include <QxOrm_Impl.h>
    + 
    +QX_REGISTER_CPP_QX_DLL1(TestQtProperty)
    +QX_REGISTER_ALL_QT_PROPERTIES(TestQtProperty, "id")
    +
    +
    + If you don't want to use QX_REGISTER_ALL_QT_PROPERTIES macro, you can write 4 lines of + code :
    +
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<TestQtProperty> & t)
    +{ qx::register_all_qt_properties<TestQtProperty>(t, "id"); }
    +} // namespace qx
    +
    +
    + Note : the second parameter of QX_REGISTER_ALL_QT_PROPERTIES macro is the name of + the property mapped to the primary key into database. + If this parameter is empty, then the class doesn't have any primary key or the primary key has + been registered in a base class.
    +
    + All properties defined with Q_PROPERTY macro can be registered in QxOrm context in two + different ways :
    + 1- with the classic method : t.data(& MyQObject::my_property, "my_property", + 0);
    + 2- or without writing the data-member pointer : t.data("my_property", 0);
    +
    + You can use the first or the second method to register your properties in QxOrm context and + access to the same functionalities using the common interface qx::IxDataMember. + You can also mix Qt meta-properties and classic registration data-member into the same mapping + function void qx::register_class<T>(). + Each registration method has some advantages and disadvantages.
    +
    + Here is the list of advantages using the second registration method in QxOrm context : +
      +
    • much more faster to compile ;
    • +
    • reduce exec size ;
    • +
    • strong integration with Qt introspection/moc engine ;
    • +
    • no need to manage any mapping function per class using + QX_REGISTER_ALL_QT_PROPERTIES macro. +
    • +
    + Here is the list of disadvantages compared to the classic registration method : +
      +
    • need to inherit from QObject class to use Q_PROPERTY macro ;
    • +
    • program execution more slower (QVariant type versus C++ template) ;
    • +
    • doesn't support relationship between tables into database (one-to-one, + one-to-many, many-to-one and many-to-many) ; +
    • +
    • cannot access to the data-member pointer of a class (need to convert to QVariant + type before to access or to modify a value).
    • +
    +
    +
    + +

    Serialization

    +
    + From Wikipedia web page : + serialization is the process of translating data structures or object state into a format + that can be stored (for example, in a file or memory buffer, or transmitted across a network + connection link) and reconstructed later in the same or another computer environment. + When the resulting series of bits is reread according to the serialization format, it can be used + to create a semantically identical clone of the original object. + The opposite operation, extracting a data structure from a series of bytes, is named + deserialization. +

    + Each C++ class registered in QxOrm context can be serialized in several ways : + + Note : serialization engine of QxOrm library provides extra features like : clone entity, dump entity (XML or JSON format) + and QxService module. +

    + Other note : by default, all properties registered in QxOrm context are serializable. To + remove a property from the serialization engine, you can write :
    +
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<person> & t)
    +{
    +  IxDataMember * pDataMember = t.data(& person::age, "age");
    +  pDataMember->setSerialize(false);
    +}}
    +
    +

    +

    Version number to manage + ascendant compatibility

    +
    + Ascendant compatibility allows deserialization process (so restore a data structure) from a + stream generated by a previous version of an application. + QxOrm library requires a version number per class and a version number for each property + registered in QxOrm context to provide ascendant compatibility. +

    + For example, imagine a person class created in a version A of your application : + we put in QX_REGISTER_HPP macro a class version equals to 0 (means first version of our + person class), and each property class have also a version equals to 0 (0 is the default + value, optional parameter). + So our person class looks like : +

    + * person.h file :
    + + + + + + + +
    +
    #ifndef _PERSON_H_
    +#define _PERSON_H_
    +
    +class person
    +{
    +public:
    +   long id;
    +   QString firstName;
    +   QString lastName;
    +   QDateTime birthDate;
    +
    +   person() : id(0) { ; }
    +   virtual ~person() { ; }
    +};
    +
    +QX_REGISTER_HPP_MY_TEST_EXE(person, qx::trait::no_base_class_defined, 0)
    +
    +#endif // _PERSON_H_
    +
    +
    + * person.cpp file :
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<person> & t)
    +{
    +  t.id(& person::id, "id");
    +  t.data(& person::firstName, "first_name", 0);
    +  t.data(& person::lastName, "last_name", 0);
    +  t.data(& person::birthDate, "birth_date", 0);
    +}}
    +
    +

    + In version B of the application, we modify our person class and we add 2 + properties : sex and address. + Our class has changed, so we have to increment its class version number, and new properties must + have a version equals to 1. + Now, our person class looks like : +

    + * person.h file :
    + + + + + + + +
    +
    #ifndef _PERSON_H_
    +#define _PERSON_H_
    +
    +class person
    +{
    +public:
    +   long id;
    +   QString firstName;
    +   QString lastName;
    +   QDateTime birthDate;
    +   QString sex;
    +   QString address;
    +
    +   person() : id(0) { ; }
    +   virtual ~person() { ; }
    +};
    +
    +QX_REGISTER_HPP_MY_TEST_EXE(person, qx::trait::no_base_class_defined, 1)
    +
    +#endif // _PERSON_H_
    +
    +
    + * person.cpp file :
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<person> & t)
    +{
    +  t.id(& person::id, "id");
    +  t.data(& person::firstName, "first_name", 0);
    +  t.data(& person::lastName, "last_name", 0);
    +  t.data(& person::birthDate, "birth_date", 0);
    +  t.data(& person::sex, "sex", 1);
    +  t.data(& person::address, "address", 1);
    +}}
    +
    +

    + Note : QxOrm library can serialize this person class from application in version + A, then deserialize this version A stream to create a cloned instance of person class in + version B of our application. +

    + Other note : remove a property breaks ascendant compatibility. + So it is recommended to never remove a property to work with QxOrm serialization engine : it is + possible for example to put a private visibility and to delete get/set accessors, + so property is hidden and can be considered as obsolete. +

    +
    +

    Qt QDataStream engine +

    +
    + Each C++ class registered in QxOrm context can be serialized using Qt QDataStream engine. + Functions to use Qt QDataStream serialization are available in namespace : qx::serialization::qt. + + Note : QDataStream + serialization is portable (support serialization/deserialization on all environments : Windows, + Linux, Mac OS X, etc...). + The output serialized stream is in binary format : so stream size is smaller than XML or JSON + for example. + QDataStream serialization + is based on introspection engine of QxOrm library, so it is slower than boost::serialization engine (based on C++ template). +

    + For example :
    + + + + + + + +
    +
       // Fetch a drug with id '3' in a new variable
    +   // drug is a C++ class registered in QxOrm context
    +   drug d;
    +   d.id = 3;
    +   QSqlError daoError = qx::dao::fetch_by_id(d);
    +
    +   // Serialize the drug to a file
    +   qx::serialization::qt::to_file(d, "export_drug.txt");
    +
    +   // Import drug from file in a new instance
    +   drug d2;
    +   qx::serialization::qt::from_file(d2, "export_drug.txt");
    +
    +   // Check if d == d2
    +   qAssert(d == d2);
    +
    +
    + Note : in above example, we serialize a C++ instance. + All functions in qx::serialization namespace can serialize list of objects. + For more details about supported containers, please read this chapter : Supported containers. +

    +
    +

    Qt JSON engine

    +
    + Each C++ class registered in QxOrm context can be serialized to JSON using Qt QJson engine (requires Qt5). + Functions to use Qt JSON serialization are available in namespace : qx::serialization::json. + + Note : JSON serialization + engine is the most permissive (compared to XML engine for example) : + indeed, properties can be defined in any order, and properties can be removed or added. + JSON deserialization doesn't generate errors or throw exceptions : the engine ignores invalid or + removed properties (but JSON stream must be valid) : so JSON engine is much more flexible than + XML engine. +

    + Other note : JSON + serialization is based on introspection engine of QxOrm library, so it is slower than boost::serialization engine (based on C++ template). +

    + For example :
    + + + + + + + +
    +
       // Fetch a list of authors from database and serialize them to a JSON file
    +   list_author list_of_author;
    +   qx::dao::fetch_all(list_of_author);
    +   qx::serialization::json::to_file(list_of_author, "list_of_author.json");
    +
    +
    + Above example generates following JSON stream :
    +
    +
    {
    +    "author_id_2": {
    +        "author_id": "author_id_2",
    +        "birthdate": "2016-03-24",
    +        "list_blog": [
    +        ],
    +        "name": "author_2",
    +        "sex": 1
    +    },
    +    "author_id_3": {
    +        "author_id": "author_id_3",
    +        "birthdate": "2016-03-24",
    +        "list_blog": [
    +        ],
    +        "name": "author_3",
    +        "sex": 1
    +    }
    +}
    +
    +

    + Note : QxRestApi module provided by QxOrm library is based on + JSON serialization engine. +

    + Other note : you can customize output JSON format (to filter some properties generated by + JSON serialization process). + All JSON serialization functions provide an optional parameter of type QString named + format. + Prerequisites to use this format parameter are : +
      +
    • format parameter must start with prefix : filter: ;
    • +
    • output properties can be defined inside { } ;
    • +
    • relationships are splitted by character | ;
    • +
    • character * means : all relationships for 1 level ;
    • +
    • character - before { } means : all properties except. +
    • +
    +
    + Example : here is a JSON serialization example defining an output format to filter some + properties on several levels of relationships : +

    + + + + + + + +
    +
    // Serialize a C++ instance to a JSON string
    +QString jsonFormat = "filter: { blog_text } | author_id { name, birthdate } | list_comment { comment_text } -> blog_id -> *";
    +QString outputJsonFiltered = qx::serialization::json::to_string(blog, 1, jsonFormat);
    +qDebug("[QxOrm] custom JSON serialization process (filtered) : \n%s", qPrintable(outputJsonFiltered));
    +
    +// Fill a C++ instance based on a JSON string
    +blog_ptr blogFromJsonFiltered; blogFromJsonFiltered.reset(new blog());
    +qx::serialization::json::from_string(blogFromJsonFiltered, outputJsonFiltered, 1, jsonFormat);
    +qx::dump(blogFromJsonFiltered);
    +qAssert(blogFromJsonFiltered->m_text != ""); // Fetched
    +qAssert(blogFromJsonFiltered->m_dt_creation.isNull()); // Not fetched
    +qAssert(blogFromJsonFiltered->m_author->m_sex == author::unknown); // Not fetched
    +qAssert(blogFromJsonFiltered->m_author->m_name != ""); // Fetched
    +qAssert(blogFromJsonFiltered->m_commentX.size() > 0);
    +qAssert(blogFromJsonFiltered->m_commentX[0]->m_dt_create.isNull()); // Not fetched
    +qAssert(blogFromJsonFiltered->m_commentX[0]->m_text != ""); // Fetched
    +qAssert(blogFromJsonFiltered->m_commentX[0]->m_blog);
    +
    +

    +
    +

    XML boost + serialization

    +
    + XML boost::serialization engine is disabled by default : to enable this + feature, it is necessary to define _QX_ENABLE_BOOST_SERIALIZATION and + _QX_ENABLE_BOOST_SERIALIZATION_XML compilation options in QxOrm.pri + (or QxOrm.cmake) configuration file. + It is also required to build boost::serialization binary (because this module is not header + only), and to set the path to this boost serialization binary to + QX_BOOST_LIB_PATH, QX_BOOST_LIB_SERIALIZATION_DEBUG and + QX_BOOST_LIB_SERIALIZATION_RELEASE variables of QxOrm.pri (or + QxOrm.cmake) configuration file. +

    + Each C++ class registered in QxOrm context can be serialized using XML boost::serialization engine. + Functions to work with XML boost serialization are available in namespace : qx::serialization::xml (same functions as qx::serialization::qt namespace). +

    + XML boost serialization engine is : +
      +
    • portable : support serialization/deserialization on all environments : Windows, + Linux, Mac OS X, etc... ;
    • +
    • slowest : slower than binary and text serialization ;
    • +
    • largest : generated stream are bigger than binary and text + serialization ;
    • +
    • human-readable : a XML stream can easily be parsed by a text editor and can be + read by a human.
    • +
    +
    +
    +

    Binary boost + serialization

    +
    + Binary boost::serialization engine is disabled by default : to enable this + feature, it is necessary to define _QX_ENABLE_BOOST_SERIALIZATION and + _QX_ENABLE_BOOST_SERIALIZATION_BINARY compilation options in QxOrm.pri (or QxOrm.cmake) configuration file. + It is also required to build boost::serialization binary (because this module is not header + only), and to set the path to this boost serialization binary to + QX_BOOST_LIB_PATH, QX_BOOST_LIB_SERIALIZATION_DEBUG and + QX_BOOST_LIB_SERIALIZATION_RELEASE variables of QxOrm.pri (or + QxOrm.cmake) configuration file. +

    + Each C++ class registered in QxOrm context can be serialized using binary boost::serialization engine. + Functions to work with binary boost serialization are available in namespace : qx::serialization::binary (same functions as qx::serialization::qt namespace). +

    + Binary boost serialization engine is : +
      +
    • non-portable : an instance serialized on Windows cannot be deserialized on Linux + for example : so you have to stay on the same environment ;
    • +
    • fastest : faster than XML and text serialization ;
    • +
    • smallest : generated stream are smaller than XML and text + serialization ;
    • +
    • non-human-readable : a binary stream cannot be read (not useful to log for + example).
    • +
    +
    +
    +

    Other boost + serialization

    +
    + boost::serialization engine provides several formats to serialize C++ + classes. + All boost serialization process are disabled by default, so to use them (same functions as qx::serialization::qt namespace), it is necessary to define + compilation options in QxOrm.pri (or QxOrm.cmake) configuration file : + +
    +
    +

    Clone a C++ instance +

    +
    + Each C++ class registered in QxOrm context can be cloned using : + + For example : +

    + + + + + + + +
    +
       drug_ptr d1;
    +   d1.reset(new drug());
    +   d1->name = "name1";
    +   d1->description = "desc1";
    +
    +   // Clone a drug
    +   drug_ptr d_clone = qx::clone(* d1);
    +
    +   // Check if (d1 == d_clone)
    +   qAssert((* d1) == (* d_clone));
    +
    +
    + Important note : be careful when you clone a smart-pointer (boost::shared_ptr or + QSharedPointer for example) where the root item can be referenced several times in its + hierarchy (tree structure for example). + In this case, to protect the root pointer of a double deletion (2 smart-pointers which take + ownership of the same raw pointer), it is recommended to clone this way : +

    + + + + + + + +
    +
    // 'pOther' type is boost::shared_ptr<myClass> (smart-pointer)
    +boost::shared_ptr<myClass> * pCloneTemp = qx::clone_to_nude_ptr(pOther);
    +boost::shared_ptr<myClass> pClone = (pCloneTemp ? (* pCloneTemp) : boost::shared_ptr<myClass>());
    +if (pCloneTemp) { delete pCloneTemp; pCloneTemp = NULL; }
    +// Now use 'pClone' ...
    +
    +

    +
    +

    Dump a C++ instance (XML + or JSON format)

    +
    + Each C++ class registered in QxOrm context can be displayed to JSON + format. + If XML boost::serialization engine is enabled, then it is also + possible to display a XML dump of a C++ instance (second input parameter of qx::dump + function). + QxOrm dump feature can be useful to debug or to log for example. +

    + + + + + + + +
    +
       blog_ptr b;
    +   b.reset(new blog());
    +   b->id = 36;
    +   qx::dao::fetch_by_id_with_all_relation(b);
    +
    +   // Dump 'b' instance result from database (XML or JSON serialization)
    +   // Second parameter is optional : 'true' = JSON format, 'false' = XML format
    +   qx::dump(b, false);
    +
    +
    + Above source code generates output XML : +

    +
    +
    +[QxOrm] start dump 'boost::shared_ptr<blog>'
    +<boost.shared_ptr-blog- class_id="0" tracking_level="0" version="1">
    +	<px class_id="1" tracking_level="1" version="0" object_id="_0">
    +		<blog_id>113</blog_id>
    +		<blog_text class_id="2" tracking_level="0" version="0">update blog_text_1</blog_text>
    +		<date_creation class_id="3" tracking_level="0" version="0">20100409162612000</date_creation>
    +		<author_id class_id="4" tracking_level="0" version="1">
    +			<px class_id="5" tracking_level="1" version="0" object_id="_1">
    +				<author_id>author_id_2</author_id>
    +				<name>author_2</name>
    +				<birthdate class_id="6" tracking_level="0" version="0">20100409</birthdate>
    +				<sex>1</sex>
    +				<list_blog class_id="7" tracking_level="0" version="0">
    +					<count>0</count>
    +					<item_version>1</item_version>
    +				</list_blog>
    +			</px>
    +		</author_id>
    +		<list_comment class_id="8" tracking_level="0" version="0">
    +			<count>2</count>
    +			<item class_id="9" tracking_level="0" version="1">
    +				<px class_id="10" tracking_level="1" version="0" object_id="_2">
    +					<comment_id>209</comment_id>
    +					<comment_text>comment_1 text</comment_text>
    +					<date_creation>20100409162612000</date_creation>
    +					<blog_id>
    +						<px class_id_reference="1" object_id="_3">
    +							<blog_id>113</blog_id>
    +							<blog_text></blog_text>
    +							<date_creation></date_creation>
    +							<author_id>
    +								<px class_id="-1"></px>
    +							</author_id>
    +							<list_comment>
    +								<count>0</count>
    +							</list_comment>
    +							<list_category class_id="11" tracking_level="0" version="0">
    +								<count>0</count>
    +							</list_category>
    +						</px>
    +					</blog_id>
    +				</px>
    +			</item>
    +			<item>
    +				<px class_id_reference="10" object_id="_4">
    +					<comment_id>210</comment_id>
    +					<comment_text>comment_2 text</comment_text>
    +					<date_creation>20100409162612000</date_creation>
    +					<blog_id>
    +						<px class_id_reference="1" object_id="_5">
    +							<blog_id>113</blog_id>
    +							<blog_text></blog_text>
    +							<date_creation></date_creation>
    +							<author_id>
    +								<px class_id="-1"></px>
    +							</author_id>
    +							<list_comment>
    +								<count>0</count>
    +							</list_comment>
    +							<list_category>
    +								<count>0</count>
    +							</list_category>
    +						</px>
    +					</blog_id>
    +				</px>
    +			</item>
    +		</list_comment>
    +		<list_category>
    +			<count>2</count>
    +			<item class_id="12" tracking_level="0" version="0">
    +				<first>355</first>
    +				<second class_id="13" tracking_level="0" version="0">
    +					<qt_shared_ptr class_id="14" tracking_level="1" version="0" object_id="_6">
    +						<category_id>355</category_id>
    +						<name>category_1</name>
    +						<description>desc_1</description>
    +						<list_blog class_id="15" tracking_level="0" version="0">
    +							<count>0</count>
    +						</list_blog>
    +					</qt_shared_ptr>
    +				</second>
    +			</item>
    +			<item>
    +				<first>357</first>
    +				<second>
    +					<qt_shared_ptr class_id_reference="14" object_id="_7">
    +						<category_id>357</category_id>
    +						<name>category_3</name>
    +						<description>desc_3</description>
    +						<list_blog>
    +							<count>0</count>
    +						</list_blog>
    +					</qt_shared_ptr>
    +				</second>
    +			</item>
    +		</list_category>
    +	</px>
    +</boost.shared_ptr-blog->
    +[QxOrm] end dump 'boost::shared_ptr<blog>'
    +
    +
    +
    +
    +
    + +

    Introspection - + Reflection

    +
    + All C++ classes registered in QxOrm context (with qx::register_class<T>() function) + can be used by introspection engine (or reflection engine) of QxOrm library. + Introspection engine provides dynamically (so during program execution) some information about + types. + These information are called meta-datas and list some classes characteristics (properties, + methods, etc.). + Many programming languages (for example Java or C#) have natively this mechanism, but not C++, + that's why QxOrm library emulates an introspection engine. + For more details about introspection (or reflection), please read the + Wikipedia web page. +

    + Here is a list of QxOrm library classes to register/access to meta-datas : +
      +
    • qx::QxClassX : + singleton class to iterate over all classes registered in QxOrm context (with + qx::register_class<T>() function) ; +
    • +
    • qx::IxClass : + interface for a class registered in QxOrm context ;
    • +
    • qx::IxDataMemberX : list of properties associated to a class ;
    • +
    • qx::IxDataMember : interface for a class property ;
    • +
    • qx::IxFunctionX : + list of methods associated to a class ;
    • +
    • qx::IxFunction : + interface for a class method.
    • +
    + A qx::IxClass + instance contains the list of class properties (qx::IxDataMemberX) and the list of class methods (qx::IxFunctionX).
    +
    + Introspection engine of QxOrm library provides : + +
    + Note : QxService + module of QxOrm library (click here to go to the + tutorial) is based on introspection engine to provide an easy and powerful way to create C++ + application server calling dynamically services methods (client request) on server side, and + creating automatically input/ouput services parameters instances.
    +
    + Other note : you can add extra information to introspection engine using property bag + pattern. + Indeed, qx::IxClass, qx::IxDataMember and qx::IxFunction classes contain a + list of QVariant items associated to a QString key (read qx::QxPropertyBag + class documentation for more details). +

    + Other note : to initialize QxOrm introspection engine, it is recommanded to call following + function once in your main for example : +

    + + + + + + + +
    +
    // Following command is recommanded to initialize QxOrm introspection engine
    +qx::QxClassX::registerAllClasses(true);
    +
    +

    +

    Get a data member value + dynamically

    +
    + To get dynamically a data member value using introspection engine of QxOrm library, you have to + work with qx::IxDataMember base class (interface). + qx::IxDataMember + class provides several methods to get a data member value (each method has a generic pointer + void * as parameter which is the address of the current instance) : + + For example : we have a generic pointer void * to a person class. + We can get the QString value of firstName property writing : +

    + + + + + + + +
    +
    // Generic pointer of type void * : we know that p is of type 'person'
    +void * p = ...;
    +
    +// Get a pointer to the registered data member 'firstName' of class 'person'
    +qx::IxDataMember * pDataMember = qx::QxClassX::getDataMember("person", "firstName");
    +
    +// First method to get the data member value with the real type
    +QString sFirstName = pDataMember->getValue<QString>(p);
    +
    +// Second method to get the data member value converted in QVariant
    +QVariant vFirstName = pDataMember->toVariant(p);
    +
    +// Third method to get the value encapsulated in qx::any type
    +boost::any aFirstName = pDataMember->getValueAnyPtr(p);
    +
    +// Check if all values are equals
    +qAssert((sFirstName == vFirstName.toString()) && (sFirstName == (* boost::any_cast<QString *>(aFirstName))));
    +
    +

    +
    +

    Set a data member value + dynamically

    +
    + qx::IxDataMember + base class (interface) is able to set dynamically a new value to a property class (modify its + value). + qx::IxDataMember + class provides 2 methods (each method has a generic pointer void * as parameter which is + the address of the current instance, and the new value to change) : +
      +
    • fromVariant() : set a new data member value based on a QVariant parameter ; +
    • +
    • setValue<T>() : set a new data member value based on a real + type parameter (template T).
    • +
    + For example : we have a generic pointer void * to a person class. + We can modify firstName property of QString type writing : +

    + + + + + + + +
    +
    // Generic pointer of type void * : we know that p is of type 'person'
    +void * p = ...;
    +
    +// Get a pointer to the registered data member 'firstName' of class 'person'
    +qx::IxDataMember * pDataMember = qx::QxClassX::getDataMember("person", "firstName");
    +
    +// First method to change the data member value
    +QVariant vFirstName = QVariant("my new firstname 1");
    +pDataMember->fromVariant(p, vFirstName);
    +
    +// Other method to change the data member value (using real type)
    +QString sFirstName = "other firstname 2";
    +pDataMember->setValue<QString>(p, sFirstName);
    +
    +

    +
    +

    Call function + dynamically

    +
    + Like data members (class properties), it is possible to register class methods (functions) in + QxOrm context (support static and non static methods). + Introspection engine of QxOrm library can invoke dynamically class methods. + All functions registered in QxOrm context are associated to a qx::IxFunction + instance. + To register a class method in QxOrm context, you have to use these functions : + + For example : we want to register in QxOrm context several methods of a person + class : +

    + * person.h file :
    + + + + + + + +
    +
    #ifndef _PERSON_H_
    +#define _PERSON_H_
    +
    +class person
    +{
    +public:
    +   long id;
    +   QString firstName;
    +   QString lastName;
    +   QDateTime birthDate;
    +
    +   person() : id(0) { ; }
    +   virtual ~person() { ; }
    +
    +   long getId() const;
    +   void myMethodWith2Params(int param1, const QString & param2);
    +
    +   static double myStaticMethodWith1Param(long param1);
    +
    +};
    +
    +QX_REGISTER_HPP_MY_TEST_EXE(person, qx::trait::no_base_class_defined, 0)
    +
    +#endif // _PERSON_H_
    +
    +
    + * person.cpp file :
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<person> & t)
    +{
    +  t.id(& person::id, "id");
    +  t.data(& person::firstName, "first_name");
    +  t.data(& person::lastName, "last_name");
    +  t.data(& person::birthDate, "birth_date");
    +
    +  t.fct_0<long>(& person::getId, "getId");
    +  t.fct_2<void, int, const QString &>(& person::myMethodWith2Params, "myMethodWith2Params");
    +
    +  t.fctStatic_1<double, long>(& person::myStaticMethodWith1Param, "myStaticMethodWith1Param");
    +}}
    +
    +

    + Once registered in QxOrm context, it is possible to call functions dynamically using qx::QxClassX::invoke() + and qx::QxClassX::invokeStatic() : +

    + + + + + + + +
    +
       // Generic pointer of type void * : we know that p is of type 'person'
    +   void * p = ...;
    +
    +   // Call method 'long getId() const' and get return value
    +   boost::any returnValue;
    +   qx::QxClassX::invoke("person", "getId", p, "", (& returnValue));
    +   long lId = boost::any_cast<long>(returnValue);
    +
    +   // Call method 'myMethodWith2Params' with 2 parameters encapsulated in a string (default separator for parameters is character '|')
    +   // This way to pass parameters to the function works only if parameters are numeric or string
    +   // If parameters are more complex, then you have to encapsulate parameters in a list of qx::any, as shown below
    +   qx::QxClassX::invoke("person", "myMethodWith2Params", p, "36|my string param 2");
    +
    +   // Call method 'myMethodWith2Params' with 2 parameters encapsulated in a list of qx::any : std::vector<qx::any>
    +   std::vector<boost::any> lstParams;
    +   int iParam1 = 36; lstParams.push_back(iParam1); // Parameter at position 1
    +   QString sParam2 = "my string param 2"; lstParams.push_back(sParam2); // Parameter at position 2
    +   qx::QxClassX::invoke("person", "myMethodWith2Params", p, lstParams);
    +
    +   // Call static method 'myStaticMethodWith1Param' with 1 parameter and get return value
    +   qx::QxClassX::invokeStatic("person", "myStaticMethodWith1Param", "19", (& returnValue));
    +   double dValue = boost::any_cast<double>(returnValue);
    +
    +

    +
    +

    Create a C++ instance + dynamically

    +
    + Introspection engine of QxOrm library is able to create class instances dynamically based on + class name (QxFactory + module, design pattern factory) using following functions : + + For example : QxService module of QxOrm library creates services + instances dynamically (based on service name) to execute server routines automatically : +

    + + + + + + + +
    +
       qx::service::IxService * ptr = qx::create_nude_ptr<qx::service::IxService>(m_sServiceName);   
    +
    +

    +
    +

    Iterate over all + classes/properties registered in QxOrm context

    +
    + Here is an example based on introspection engine of QxOrm library : how to iterate over all + classes, properties and methods registered in QxOrm context ?
    +
    + + + + + + + +
    +
    QString QxClassX::dumpAllClasses()
    +{
    +   QxClassX::registerAllClasses();
    +   QxCollection<QString, IxClass *> * pAllClasses = QxClassX::getAllClasses();
    +   if (! pAllClasses) { qAssert(false); return ""; }
    +
    +   QString sDump;
    +   long lCount = pAllClasses->count();
    +   qDebug("[QxOrm] start dump all registered classes (%ld)", lCount);
    +   _foreach(IxClass * pClass, (* pAllClasses))
    +   { if (pClass) { sDump += pClass->dumpClass(); } }
    +   qDebug("[QxOrm] %s", "end dump all registered classes");
    +
    +   return sDump;
    +}
    +
    +QString IxClass::dumpClass() const
    +{
    +   QString sDump;
    +   sDump += "-- class '" + m_sKey + "' (name '" + m_sName + "', ";
    +   sDump += "description '" + m_sDescription + "', version '" + QString::number(m_lVersion) + "', ";
    +   sDump += "base class '" + (getBaseClass() ? getBaseClass()->getKey() : "") + "')\n";
    +
    +   long lCount = (m_pDataMemberX ? m_pDataMemberX->count() : 0);
    +   sDump += "\t* list of registered properties (" + QString::number(lCount) + ")\n";
    +   if (m_pDataMemberX)
    +   {
    +      IxDataMember * pId = this->getId();
    +      for (long l = 0; l < lCount; l++)
    +      {
    +         IxDataMember * p = m_pDataMemberX->get(l); if (! p) { continue; }
    +         IxSqlRelation * pRelation = p->getSqlRelation();
    +         QString sInfos = p->getKey() + ((p == pId) ? QString(" (id)") : QString());
    +         sInfos += (pRelation ? (QString(" (") + pRelation->getDescription() + QString(")")) : QString());
    +         sDump += "\t\t" + sInfos + "\n";
    +      }
    +   }
    +
    +   lCount = (m_pFctMemberX ? m_pFctMemberX->count() : 0);
    +   sDump += "\t* list of registered functions (" + QString::number(lCount) + ")\n";
    +   if (m_pFctMemberX)
    +   {
    +      _foreach_if(IxFunction_ptr p, (* m_pFctMemberX), (p))
    +      { QString sKey = p->getKey(); sDump += "\t\t" + sKey + "\n"; }
    +   }
    +
    +   qDebug("%s", qPrintable(sDump));
    +   return sDump;
    +}
    +
    +
    + If we execute the qx::QxClassX::dumpAllClasses() function with qxBlog tutorial, here are output logs :
    +
    + + + + + + + +
    +
    [QxOrm] start dump all registered classes (4)
    +-- class 'author' (name 'author', description '', version '0', base class '')
    +	* list of registered properties (5)
    +		author_id (id)
    +		name
    +		birthdate
    +		sex
    +		list_blog (relation one-to-many)
    +	* list of registered functions (1)
    +		age
    +
    +-- class 'blog' (name 'blog', description '', version '0', base class '')
    +	* list of registered properties (6)
    +		blog_id (id)
    +		blog_text
    +		date_creation
    +		author_id (relation many-to-one)
    +		list_comment (relation one-to-many)
    +		list_category (relation many-to-many)
    +	* list of registered functions (0)
    +
    +-- class 'comment' (name 'comment', description '', version '0', base class '')
    +	* list of registered properties (4)
    +		comment_id (id)
    +		comment_text
    +		date_creation
    +		blog_id (relation many-to-one)
    +	* list of registered functions (0)
    +
    +-- class 'category' (name 'category', description '', version '0', base class '')
    +	* list of registered properties (4)
    +		category_id (id)
    +		name
    +		description
    +		list_blog (relation many-to-many)
    +	* list of registered functions (0)
    +
    +[QxOrm] end dump all registered classes
    +
    +
    +
    +
    + +

    Services : transfer + persistent data layer over network (QxService module)

    +
    + QxService module of + QxOrm library provides an easy and powerful way to create C++ application server + (services with request from client and response from server). + A tutorial is available on + QxOrm web site to show how to work with QxService module. + QxService module is based on + introspection engine and serialization engine of + QxOrm library to transfer persistent data layer over network and execute automatically server + routines on server side. +

    + Note : to enable QxService module, you have to define _QX_ENABLE_QT_NETWORK compilation option in QxOrm.pri (or QxOrm.cmake) configuration file. + This compilation option adds a dependency to QxOrm library : QtNetwork provided by Qt + framework. +

    + Other note : QxEntityEditor application is deployed with QxEECppServicesExport + plugin : this plugin generates automatically all C++ source code to transfer all project entities + over network. + A list of client/server methods are generated automatically (to manage CRUD operations) : +
      +
    • count() : client/server query to count entities (possibility to add a SQL query + filter) ;
    • +
    • fetchById() : client/server query to fetch entity properties based on its identifier + ;
    • +
    • fetchAll() : client/server query to fetch properties of all entities (mapped to a + database table) ;
    • +
    • fetchByQuery() : client/server query to fetch properties of entities filtered by a + SQL query ;
    • +
    • insert() : client/server query to insert entity values ;
    • +
    • update() : client/server query to update entity values ;
    • +
    • save() : client/server query to save entity values (insert or update) ;
    • +
    • deleteById() : client/server query to delete an entity based on its identifier ;
    • +
    • deleteAll() : client/server query to delete all entities (mapped to a database table) + ;
    • +
    • deleteByQuery() : client/server query to delete entities filtered by a SQL query ; +
    • +
    • destroyById() : client/server query to delete an entity based on its identifier + (manage soft delete behaviour, logical delete) ;
    • +
    • destroyAll() : client/server query to delete all entities mapped to a database table + (manage soft delete behaviour, logical delete) ;
    • +
    • destroyByQuery() : client/server query to delete entities filtered by a SQL query + (manage soft delete behaviour, logical delete) ;
    • +
    • executeQuery() : client/server query to execute a custom SQL + query or stored procedure ;
    • +
    • exist() : client/server query to check if an entity already exists based on its + identifier ;
    • +
    • isValid() : client/server query to check entity validity (QxValidator module).
    • +
    + It is possible to add and to customize services generated by QxEntityEditor application. +

    + The goal of this chapter is to show QxService module concepts : + +
    +

    Input/output service + parameters (request/response)

    +
    + Each function exposed by a service has input parameters (request from client) and output + parameters (response from server). + These input/output parameters must inherit from qx::service::IxParameter interface and must be registered in QxOrm + context (with void qx::register_class<T> function). +

    + For example : here is an example of input/output parameters generated by + QxEntityEditor application based on blog class of qxBlog tutorial : +

    + * blog.services.gen.h file :
    + + + + + + + +
    +
    namespace services {
    +
    +typedef boost::shared_ptr<blog> blog_ptr;
    +typedef qx::QxCollection<long, blog_ptr> list_of_blog;
    +typedef boost::shared_ptr<list_of_blog> list_of_blog_ptr;
    +
    +/* -- Service Input Parameters -- */
    +
    +class QXBLOG_SERVICES_EXPORT blog_input : public qx::service::IxParameter
    +{
    +
    +public:
    +
    +   blog_input();
    +   virtual ~blog_input();
    +
    +   long id;                   //!< Id to fetch or delete
    +   blog_ptr instance;         //!< Single instance to fetch, insert, update, delete or validate
    +   list_of_blog_ptr list;     //!< List of instances to fetch, insert, update, delete or validate
    +   qx_query query;            //!< Query to execute when fetching, updating or deleting
    +   QStringList columns;       //!< List of columns to fetch or update
    +   QStringList relations;     //!< List of relations to fetch
    +
    +};
    +
    +typedef boost::shared_ptr<services::blog_input> blog_input_ptr;
    +
    +/* -- Service Output Parameters -- */
    +
    +class QXBLOG_SERVICES_EXPORT blog_output : public qx::service::IxParameter
    +{
    +
    +public:
    +
    +   blog_output();
    +   virtual ~blog_output();
    +
    +   blog_ptr instance;            //!< Single instance from server
    +   list_of_blog_ptr list;        //!< List of instances from server
    +   QSqlError error;              //!< If a SQL error occurred, this output parameter is not empty
    +   qx::QxInvalidValueX invalid;  //!< Check if a single instance (or a list of instances) is valid
    +   qx_query query;               //!< Query which contains all results
    +   long count;                   //!< Count how many items in database using a query or not
    +   qx_bool exist;                //!< Check if a single instance (or a list of instances) exist in database
    +
    +};
    +
    +typedef boost::shared_ptr<services::blog_output> blog_output_ptr;
    +
    +} // namespace services
    +
    +QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QXBLOG_SERVICES(services::blog_input, qx::service::IxParameter, 0, services_blog_input)
    +QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QXBLOG_SERVICES(services::blog_output, qx::service::IxParameter, 0, services_blog_output)
    +
    +
    + * blog.services.gen.cpp file :
    + + + + + + + +
    +
    QX_REGISTER_COMPLEX_CLASS_NAME_CPP_QXBLOG_SERVICES(services::blog_input, services_blog_input)
    +QX_REGISTER_COMPLEX_CLASS_NAME_CPP_QXBLOG_SERVICES(services::blog_output, services_blog_output)
    +
    +namespace qx {
    +
    +template <>
    +void register_class(QxClass<services::blog_input> & t)
    +{
    +   t.data(& services::blog_input::id, "id");
    +   t.data(& services::blog_input::instance, "instance");
    +   t.data(& services::blog_input::list, "list");
    +   t.data(& services::blog_input::query, "query");
    +   t.data(& services::blog_input::columns, "columns");
    +   t.data(& services::blog_input::relations, "relations");
    +}
    +
    +template <>
    +void register_class(QxClass<services::blog_output> & t)
    +{
    +   t.data(& services::blog_output::instance, "instance");
    +   t.data(& services::blog_output::list, "list");
    +   t.data(& services::blog_output::error, "error");
    +   t.data(& services::blog_output::invalid, "invalid");
    +   t.data(& services::blog_output::query, "query");
    +   t.data(& services::blog_output::count, "count");
    +   t.data(& services::blog_output::exist, "exist");
    +}
    +
    +} // namespace qx
    +
    +

    + Note : input/output parameters can contain complex structures (containers, + smart-pointers, etc...). + So it is very easy to transfer complex classes (for example with relationships) with QxService module. +

    +
    +

    Define service functions + exposed to clients

    +
    + Each service registered in QxService module exposes a list of functions to clients (client/server + queries). + All services must inherit from qx::service::QxService<INPUT, OUTPUT> base class (INPUT and + OUTPUT template parameters are explained in chapter : Input/output + service parameters, request/response) and must be registered in QxOrm context (with + void qx::register_class<T> function). +

    + For example : here is a service example generated by QxEntityEditor application + based on blog class of qxBlog tutorial : +

    + * blog.services.gen.h file :
    + + + + + + + +
    +
    namespace services {
    +
    +/* -- Service Definition -- */
    +
    +typedef qx::service::QxService< blog_input, blog_output > blog_base_class;
    +class QXBLOG_SERVICES_EXPORT blog_services : public blog_base_class
    +{
    +
    +   QX_REGISTER_FRIEND_CLASS(services::blog_services)
    +
    +public:
    +
    +   blog_services();
    +   virtual ~blog_services();
    +
    +protected:
    +
    +   void fetchById_();
    +   void fetchAll_();
    +   void fetchByQuery_();
    +
    +   void insert_();
    +   void update_();
    +   void save_();
    +   void deleteById_();
    +   void deleteAll_();
    +   void deleteByQuery_();
    +   void destroyById_();
    +   void destroyAll_();
    +   void destroyByQuery_();
    +
    +   void executeQuery_();
    +   void callQuery_();
    +   void exist_();
    +   void count_();
    +   void isValid_();
    +
    +#ifdef _QXBLOG_SERVICES_MODE_CLIENT
    +
    +public:
    +
    +   blog_ptr fetchById(long id, const QStringList & columns = QStringList(), const QStringList & relations = QStringList());
    +   QSqlError fetchById(blog_ptr & p, const QStringList & columns = QStringList(), const QStringList & relations = QStringList());
    +   QSqlError fetchById(list_of_blog_ptr & lst, const QStringList & columns = QStringList(), const QStringList & relations = QStringList());
    +   QSqlError fetchAll(list_of_blog_ptr & lst, const QStringList & columns = QStringList(), const QStringList & relations = QStringList());
    +   QSqlError fetchByQuery(const qx_query & query, list_of_blog_ptr & lst, const QStringList & columns = QStringList(),
    +                                       const QStringList & relations = QStringList());
    +
    +   QSqlError insert(blog_ptr & p, const QStringList & relations = QStringList());
    +   QSqlError insert(list_of_blog_ptr & lst, const QStringList & relations = QStringList());
    +   QSqlError update(blog_ptr & p, const qx_query & query = qx_query(),
    +                              const QStringList & columns = QStringList(), const QStringList & relations = QStringList());
    +   QSqlError update(list_of_blog_ptr & lst, const qx_query & query = qx_query(),
    +                              const QStringList & columns = QStringList(), const QStringList & relations = QStringList());
    +   QSqlError save(blog_ptr & p, const QStringList & relations = QStringList());
    +   QSqlError save(list_of_blog_ptr & lst, const QStringList & relations = QStringList());
    +
    +   QSqlError deleteById(long id);
    +   QSqlError deleteById(blog_ptr & p);
    +   QSqlError deleteById(list_of_blog_ptr & lst);
    +   QSqlError deleteAll();
    +   QSqlError deleteByQuery(const qx_query & query);
    +   QSqlError destroyById(long id);
    +   QSqlError destroyById(blog_ptr & p);
    +   QSqlError destroyById(list_of_blog_ptr & lst);
    +   QSqlError destroyAll();
    +   QSqlError destroyByQuery(const qx_query & query);
    +
    +   QSqlError executeQuery(qx_query & query, blog_ptr & p);
    +   QSqlError executeQuery(qx_query & query, list_of_blog_ptr & lst);
    +   QSqlError callQuery(qx_query & query);
    +   qx_bool exist(blog_ptr & p);
    +   qx_bool exist(list_of_blog_ptr & lst);
    +   QSqlError count(long & lCount, const qx_query & query = qx_query());
    +   qx::QxInvalidValueX isValid(blog_ptr & p);
    +   qx::QxInvalidValueX isValid(list_of_blog_ptr & lst);
    +
    +#endif // _QXBLOG_SERVICES_MODE_CLIENT
    +
    +};
    +
    +typedef boost::shared_ptr<services::blog_services> blog_services_ptr;
    +
    +} // namespace services
    +
    +QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QXBLOG_SERVICES(services::blog_services, qx::service::IxService, 0, services_blog_services)
    +
    +
    + * blog.services.gen.cpp file :
    + + + + + + + +
    +
    QX_REGISTER_COMPLEX_CLASS_NAME_CPP_QXBLOG_SERVICES(services::blog_services, services_blog_services)
    +
    +namespace qx {
    +
    +template <>
    +void register_class(QxClass<services::blog_services> & t)
    +{
    +   t.fct_0<void>(& services::blog_services::fetchById_, "fetchById");
    +   t.fct_0<void>(& services::blog_services::fetchAll_, "fetchAll");
    +   t.fct_0<void>(& services::blog_services::fetchByQuery_, "fetchByQuery");
    +
    +   t.fct_0<void>(& services::blog_services::insert_, "insert");
    +   t.fct_0<void>(& services::blog_services::update_, "update");
    +   t.fct_0<void>(& services::blog_services::save_, "save");
    +   t.fct_0<void>(& services::blog_services::deleteById_, "deleteById");
    +   t.fct_0<void>(& services::blog_services::deleteAll_, "deleteAll");
    +   t.fct_0<void>(& services::blog_services::deleteByQuery_, "deleteByQuery");
    +   t.fct_0<void>(& services::blog_services::destroyById_, "destroyById");
    +   t.fct_0<void>(& services::blog_services::destroyAll_, "destroyAll");
    +   t.fct_0<void>(& services::blog_services::destroyByQuery_, "destroyByQuery");
    +
    +   t.fct_0<void>(& services::blog_services::executeQuery_, "executeQuery");
    +   t.fct_0<void>(& services::blog_services::callQuery_, "callQuery");
    +   t.fct_0<void>(& services::blog_services::exist_, "exist");
    +   t.fct_0<void>(& services::blog_services::count_, "count");
    +   t.fct_0<void>(& services::blog_services::isValid_, "isValid");
    +}
    +
    +} // namespace qx
    +
    +// Then there is the implementation of all functions provided by the service...
    +
    +

    + Note : once registered in QxOrm context, all clients connected to server can call these + functions exposed by the service : server routines are executed automatically. + Data serialization and network layer to transfer persistent classes are managed automatically by + QxService module. +

    +
    +

    List of options available + on server side

    +
    + C++ application server based on QxService module provides several parameters in qx::service::QxConnect singleton class : +
      +
    • setPort() : listening port number to receive request from client and send response + from server ;
    • +
    • setThreadCount() : threads count available on server side to manage several client + requests at the same time ;
    • +
    • setSerializationType() : serialization type used to send + response from server to client ;
    • +
    • setCompressData() : define if data sent from server to client are compressed or + not ;
    • +
    • setEncryptData() : define if data sent from server to client are encrypted or not + (with possibility to configure an encryption key).
    • +
    +
    +
    +

    Connection settings on + client side

    +
    + Client layer based on QxService module provides several parameters in qx::service::QxConnect singleton class : +
      +
    • setIp() : IP address of C++ application server ;
    • +
    • setPort() : port number used by C++ application server ;
    • +
    • setSerializationType() : serialization type used by + client layer to send requests from client to server ;
    • +
    • setCompressData() : define if data sent from client to server are compressed or + not ;
    • +
    • setEncryptData() : define if data sent from client to server are encrypted or not + (with possibility to configure an encryption key).
    • +
    +
    +
    +

    Service + authentication

    +
    + It is often necessary to add a control on server side to check users connected on client side. + qx::service::IxService interface (base class for all services registered + in QxService module) + provides virtual methods which can be overridden to manage authentication : +
      +
    • onBeforeProcess() : virtual method called before server routine execution ;
    • +
    • onAfterProcess() : virtual method called after server routine execution.
    • +
    +
    + For example : here is a class named ParameterAuthentication which can be used as + base class for all other parameters, this class provides 3 properties login, + password and token : +

    + * ParameterAuthentication.h file :
    + + + + + + + +
    +
    class MY_DLL_EXPORT ParameterAuthentication : public qx::service::IxParameter
    +{
    + 
    +public:
    + 
    +   ParameterAuthentication();
    +   virtual ~ParameterAuthentication();
    + 
    +   QString login;
    +   QString password;
    +   QString token;
    +   // etc..., put here all properties required by the authentication process
    + 
    +};
    + 
    +typedef boost::shared_ptr<ParameterAuthentication> ParameterAuthentication_ptr;
    + 
    +QX_REGISTER_COMPLEX_CLASS_NAME_HPP_MY_DLL(ParameterAuthentication, qx::service::IxParameter, 0, ParameterAuthentication)
    +
    +
    + * ParameterAuthentication.cpp file :
    + + + + + + + +
    +
    QX_REGISTER_COMPLEX_CLASS_NAME_CPP_MY_DLL(ParameterAuthentication, ParameterAuthentication)
    + 
    +namespace qx {
    + 
    +template <>
    +void register_class(QxClass<ParameterAuthentication> & t)
    +{
    +   t.data(& ParameterAuthentication::login, "login");
    +   t.data(& ParameterAuthentication::password, "password");
    +   t.data(& ParameterAuthentication::token, "token");
    +}
    + 
    +} // namespace qx
    +
    +

    + We have a base class for all parameters (ParameterAuthentication), we will now create a + base class for all services named ServiceAuthentication<INPUT, OUTPUT>. + This service base class will override onBeforeProcess() virtual method to manage + authentication before each service routine execution : +

    + * ServiceAuthentication.h file :
    + + + + + + + +
    +
    #include "ParameterAuthentication.h"
    + 
    +template <class INPUT, class OUTPUT>
    +class ServiceAuthentication : public qx::service::QxService<INPUT, OUTPUT>
    +{
    + 
    +public:
    + 
    +   ServiceAuthentication(const QString & sServiceName) : qx::service::QxService<INPUT, OUTPUT>(sServiceName) { ; }
    +   virtual ~ServiceAuthentication() { ; }
    + 
    +   virtual void onBeforeProcess()
    +   {
    +      // Here you can implement your own authentication control (checking login/password for example)
    +      // You can get input authentication parameters like this :
    +      ParameterAuthentication_ptr pParams = getInputParameter();
    +      pParams->login, pParams->password, etc...
    + 
    +      // If authentication is not valid, then you can throw an exception (and stop process before executing service function)
    +      throw qx::exception("Authentication error !");
    +   }
    + 
    +};
    +
    +

    + Now we have ParameterAuthentication base class and ServiceAuthentication<INPUT, + OUTPUT> base class : all parameters and services must inherit from these classes to + manage automatically authentication, and return an error message to client when user settings + are not valid. +

    + Note : like authentication, it is possible to manage logs on server side using + onBeforeProcess() and onAfterProcess() virtual methods. +

    +
    +

    Async client/server + queries

    +
    + By default, all client/server queries are synchronous operations : that means that client layer + waits for server response to continue its execution. + With a user interface (GUI), a client/server query locks application (freeze) if + it is executed in the main thread : so if server response is not sent quickly, users could think + that the application is crashed. + QxService module provides + an easy way to perform asynchronous client/server queries (so without freezing GUI user + interface) with qx::service::QxClientAsync class. +

    + qx::service::QxClientAsync class is based on introspection engine of QxOrm library and Qt SIGNAL-SLOT + feature. + qx::service::QxClientAsync class requires : +
      +
    • a service instance ;
    • +
    • input/output service parameters ;
    • +
    • server routine name to execute (string format) ;
    • +
    • a callback function called at the end of the transaction (connection to finished() + signal event). +
    • +
    +
    + Here is an example from qxClientServer tutorial which + runs a server routine asynchronously : +

    + + + + + + + +
    +
    void main_dlg::onClickBtnDateTimeAsync()
    +{
    +   if (m_pDateTimeAsync) { qDebug("[QxOrm] '%s' transaction is already running", "server_infos::get_current_date_time"); return; }
    +
    +   // Cr�ation d'une instance de service et appel � la m�thode pour recevoir la date-heure courante du serveur (mode asynchrone)
    +   server_infos_ptr service = server_infos_ptr(new server_infos());
    +   m_pDateTimeAsync.reset(new qx::service::QxClientAsync());
    +   QObject::connect(m_pDateTimeAsync.get(), SIGNAL(finished()), this, SLOT(onDateTimeAsyncFinished()));
    +   m_pDateTimeAsync->setService(service, "get_current_date_time");
    +   m_pDateTimeAsync->start();
    +}
    +
    +void main_dlg::onDateTimeAsyncFinished()
    +{
    +   if (! m_pDateTimeAsync || ! m_pDateTimeAsync->getService()) { return; }
    +   updateLastTransactionLog(m_pDateTimeAsync->getService()->getTransaction());
    +   m_pDateTimeAsync.reset();
    +}
    +
    +

    + Note : above example shows how to perform an asynchronous client/server query with these + steps : +
      +
    • create a service instance (of server_infos_ptr type in this example) ;
    • +
    • create a qx::service::QxClientAsync instance ;
    • +
    • connect finished event to a callback function (named + onDateTimeAsyncFinished() in this example) ; +
    • +
    • pass service instance and service function name to execute to + qx::service::QxClientAsync object ; +
    • +
    • run the transaction calling start() method.
    • +
    +
    +
    +
    + +

    Model View engine + (QxModelView module)

    +
    + QxModelView module + provides an easy way to work with Qt + model/view engine with all C++ classes registered in QxOrm context : +
      +
    • QML : each property defined in QxOrm context is exposed to QML + engine : QxModelView module makes easier integration between QML and + databases ;
    • +
    • Qt widgets : QTableView or QListView for example to + display/modify a database table content.
    • +
    + qx::IxModel interface + provides a generic way for all models linked to persistent classes registered in QxOrm context. + All methods of this class prefixed by 'qx' call functions from qx::dao namespace and then + communicate with database. + qx::IxModel interface + provides also Q_INVOKABLE methods which can be called in QML files + : +
      +
    • qxCount_() : entities count in table mapped to model (possibility to add a SQL query + filter) ;
    • +
    • qxFetchById_() : fetch model properties based on its identifier ;
    • +
    • qxFetchAll_() : fetch model with all entities in table mapped to model ;
    • +
    • qxFetchByQuery_() : fetch model with entities in table mapped to model filtered by a + SQL query ;
    • +
    • qxFetchRow_() : fetch (update) a model row (each model row provides its own + identifier) ;
    • +
    • qxInsert_() : insert all model entities (all model rows) to database ;
    • +
    • qxInsertRow_() : insert a model row to database ;
    • +
    • qxUpdate_() : update all model entities (all model rows) to database ;
    • +
    • qxUpdateRow_() : update a model row to database ;
    • +
    • qxSave_() : save all model entities (all model rows) to database (insert or update) ; +
    • +
    • qxSaveRow_() : save a model row to database (insert or update) ;
    • +
    • qxDeleteById_() : delete an entity from database based on the identifier parameter ; +
    • +
    • qxDeleteAll_() : delete all entities in table mapped to model ;
    • +
    • qxDeleteByQuery_() : delete entities in table mapped to model based on a SQL query ; +
    • +
    • qxDeleteRow_() : delete a model row in database (each model row provides its own + identifier) ;
    • +
    • qxDestroyById_() : delete an entity from database based on the identifier parameter + (support soft delete behaviour, logical delete) ;
    • +
    • qxDestroyAll_() : delete all entities in table mapped to model (support soft delete behaviour, logical delete) ;
    • +
    • qxDestroyByQuery_() : delete entities in table mapped to model based on a SQL query + (support soft delete behaviour, logical delete) ;
    • +
    • qxDestroyRow_() : delete a model row in database (each model row provides its own + identifier), support soft delete behaviour, logical delete ;
    • +
    • qxExecuteQuery_() : fetch model using a custom SQL query or + stored procedure ;
    • +
    • qxExist_() : check if an entity already exists based on the identifier parameter ; +
    • +
    • qxValidate_() : check validity of all model content (QxValidator module) ;
    • +
    • qxValidateRow_() : check validity of a model row (QxValidator + module).
    • +
    +
    + Note : qxBlogModelView sample project in ./test/ directory of QxOrm package + shows how to create quickly a QxOrm model and associate it to the Qt model/view engine + (first with a Qt widget, then with a QML view). +

    +

    Simple model (without + relationship)

    +
    + All classes registered in QxOrm context can be used as a model to display/modify values in + views. + qx::IxModel QxOrm model + base class inherits from Qt QAbstractItemModel base class : so QxOrm models are full compatible with + Qt model/view engine. +

    + Only 1 line in C++ source code to instantiate a QxOrm model : +

    + + + + + + + +
    +
       qx::IxModel * pModel = new qx::QxModel<MyClass>();   
    +
    +
    + Note : the QxOrm model created with this line of code exposes automatically all + properties registered in QxOrm context to Qt model/view engine. +

    +
    +

    Model with relationships + (nested models)

    +
    + Associate class relationships (1-n, n-1 and n-n) to Qt model/view engine is + complex : the solution provided by QxOrm library is based on nested models concept. + For more details about nested models concept, a french tutorial is available on famous developpez.com forum. +

    + To use relationships (1-n, n-1 and n-n) with QxModelView module, it + is very important to understand that there is a hierarchy between models (a parent model + can be associated to several child models, this is the nested models concept). +

    + To be able to work with relationships (nested models), it is necessary to create derived classes + based on qx::QxModel<T> base class. + This way, all simple properties (not relationship) are automatically exposed to views (thanks to + the base class), the only thing to do is to write accessors to manage relationships. + QxEntityEditor application is deployed with QxEECppModelViewExport plugin : + this plugin generates source code automatically to work with nested models. +

    + Here is a source code example generated by QxEntityEditor application to create a QxOrm + model based on blog class (read qxBlog + tutorial for more details). + blog class defines 3 relationships : author (n-1), list_of_comment (1-n) + and list_of_category (n-n) : +

    + * blog.model_view.gen.h file :
    + + + + + + + +
    +
    namespace model_view {
    +
    +typedef qx::QxModel<blog> blog_model_base_class;
    +
    +class QXBLOG_MODEL_VIEW_EXPORT blog_model : public blog_model_base_class
    +{
    +
    +   Q_OBJECT
    +
    +public:
    +
    +   blog_model(QObject * parent = 0);
    +   blog_model(qx::IxModel * other, QObject * parent);
    +   virtual ~blog_model();
    +
    +   Q_INVOKABLE QObject * author(int row, bool bLoadFromDatabase = false, const QString & sAppendRelations = QString());
    +   Q_INVOKABLE QObject * list_of_comment(int row, bool bLoadFromDatabase = false, const QString & sAppendRelations = QString());
    +   Q_INVOKABLE QObject * list_of_category(int row, bool bLoadFromDatabase = false, const QString & sAppendRelations = QString());
    +
    +   /* List of properties exposed by the model (3) :
    +      - blog_id
    +      - title
    +      - text
    +   */
    +
    +protected:
    +
    +   virtual void syncNestedModel(int row, const QStringList & relation);
    +   virtual void syncAllNestedModel(const QStringList & relation);
    +
    +};
    +
    +} // namespace model_view
    +
    +
    + * blog.model_view.gen.cpp file :
    +
    +
    namespace model_view {
    +
    +blog_model::blog_model(QObject * parent /* = 0 */) : blog_model_base_class(parent) { ; }
    +
    +blog_model::blog_model(qx::IxModel * other, QObject * parent) : blog_model_base_class(other, parent) { ; }
    +
    +blog_model::~blog_model() { ; }
    +
    +QObject * blog_model::author(int row, bool bLoadFromDatabase /* = false */, const QString & sAppendRelations /* = QString() */)
    +{
    +   QString sRelation = "author";
    +   qx::IxModel * pChild = (bLoadFromDatabase ? NULL : this->getChild(row, sRelation));
    +   if (pChild) { return static_cast<QObject *>(pChild); }
    +
    +   if ((row < 0) || (row >= this->m_model.count())) { qAssert(false); return NULL; }
    +   blog_model_base_class::type_ptr ptr = this->m_model.getByIndex(row);
    +   if (! ptr) { qAssert(false); return NULL; }
    +   long id = ptr->getblog_id();
    +   blog::type_author value = ptr->getauthor();
    +
    +   if (bLoadFromDatabase)
    +   {
    +      if (! sAppendRelations.isEmpty() && ! sAppendRelations.startsWith("->") && ! sAppendRelations.startsWith(">>")) { sRelation += "->" + sAppendRelations; }
    +      else if (! sAppendRelations.isEmpty()) { sRelation += sAppendRelations; }
    +      blog tmp;
    +      tmp.setblog_id(id);
    +      this->m_lastError = qx::dao::fetch_by_id_with_relation(sRelation, tmp);
    +      if (this->m_lastError.isValid()) { return NULL; }
    +      value = tmp.getauthor();
    +      ptr->setauthor(value);
    +   }
    +
    +   model_view::author_model * pNewChild = NULL;
    +   pChild = qx::model_view::create_nested_model_with_type(this, QModelIndex(), value, pNewChild);
    +   if (pChild) { this->insertChild(row, "author", pChild); }
    +   return static_cast<QObject *>(pChild);
    +}
    +
    +QObject * blog_model::list_of_comment(int row, bool bLoadFromDatabase /* = false */, const QString & sAppendRelations /* = QString() */)
    +{
    +   QString sRelation = "list_of_comment";
    +   qx::IxModel * pChild = (bLoadFromDatabase ? NULL : this->getChild(row, sRelation));
    +   if (pChild) { return static_cast<QObject *>(pChild); }
    +
    +   if ((row < 0) || (row >= this->m_model.count())) { qAssert(false); return NULL; }
    +   blog_model_base_class::type_ptr ptr = this->m_model.getByIndex(row);
    +   if (! ptr) { qAssert(false); return NULL; }
    +   long id = ptr->getblog_id();
    +   blog::type_list_of_comment value = ptr->getlist_of_comment();
    +
    +   if (bLoadFromDatabase)
    +   {
    +      if (! sAppendRelations.isEmpty() && ! sAppendRelations.startsWith("->") && ! sAppendRelations.startsWith(">>")) { sRelation += "->" + sAppendRelations; }
    +      else if (! sAppendRelations.isEmpty()) { sRelation += sAppendRelations; }
    +      blog tmp;
    +      tmp.setblog_id(id);
    +      this->m_lastError = qx::dao::fetch_by_id_with_relation(sRelation, tmp);
    +      if (this->m_lastError.isValid()) { return NULL; }
    +      value = tmp.getlist_of_comment();
    +      ptr->setlist_of_comment(value);
    +   }
    +
    +   model_view::comment_model * pNewChild = NULL;
    +   pChild = qx::model_view::create_nested_model_with_type(this, QModelIndex(), value, pNewChild);
    +   if (pChild) { this->insertChild(row, "list_of_comment", pChild); }
    +   return static_cast<QObject *>(pChild);
    +}
    +
    +QObject * blog_model::list_of_category(int row, bool bLoadFromDatabase /* = false */, const QString & sAppendRelations /* = QString() */)
    +{
    +   QString sRelation = "list_of_category";
    +   qx::IxModel * pChild = (bLoadFromDatabase ? NULL : this->getChild(row, sRelation));
    +   if (pChild) { return static_cast<QObject *>(pChild); }
    +
    +   if ((row < 0) || (row >= this->m_model.count())) { qAssert(false); return NULL; }
    +   blog_model_base_class::type_ptr ptr = this->m_model.getByIndex(row);
    +   if (! ptr) { qAssert(false); return NULL; }
    +   long id = ptr->getblog_id();
    +   blog::type_list_of_category value = ptr->getlist_of_category();
    +
    +   if (bLoadFromDatabase)
    +   {
    +      if (! sAppendRelations.isEmpty() && ! sAppendRelations.startsWith("->") && ! sAppendRelations.startsWith(">>")) { sRelation += "->" + sAppendRelations; }
    +      else if (! sAppendRelations.isEmpty()) { sRelation += sAppendRelations; }
    +      blog tmp;
    +      tmp.setblog_id(id);
    +      this->m_lastError = qx::dao::fetch_by_id_with_relation(sRelation, tmp);
    +      if (this->m_lastError.isValid()) { return NULL; }
    +      value = tmp.getlist_of_category();
    +      ptr->setlist_of_category(value);
    +   }
    +
    +   model_view::category_model * pNewChild = NULL;
    +   pChild = qx::model_view::create_nested_model_with_type(this, QModelIndex(), value, pNewChild);
    +   if (pChild) { this->insertChild(row, "list_of_category", pChild); }
    +   return static_cast<QObject *>(pChild);
    +}
    +
    +void blog_model::syncNestedModel(int row, const QStringList & relation)
    +{
    +   Q_UNUSED(relation);
    +   qx::IxModel * pNestedModel = NULL;
    +   if ((row < 0) || (row >= this->m_model.count())) { return; }
    +   blog_model_base_class::type_ptr ptr = this->m_model.getByIndex(row);
    +   if (! ptr) { return; }
    +
    +   pNestedModel = this->getChild(row, "author");
    +   if (pNestedModel)
    +   {
    +      this->syncNestedModelRecursive(pNestedModel, relation);
    +      blog::type_author value;
    +      qx::model_view::sync_nested_model(pNestedModel, value);
    +      ptr->setauthor(value);
    +   }
    +
    +   pNestedModel = this->getChild(row, "list_of_comment");
    +   if (pNestedModel)
    +   {
    +      this->syncNestedModelRecursive(pNestedModel, relation);
    +      blog::type_list_of_comment value;
    +      qx::model_view::sync_nested_model(pNestedModel, value);
    +      ptr->setlist_of_comment(value);
    +   }
    +
    +   pNestedModel = this->getChild(row, "list_of_category");
    +   if (pNestedModel)
    +   {
    +      this->syncNestedModelRecursive(pNestedModel, relation);
    +      blog::type_list_of_category value;
    +      qx::model_view::sync_nested_model(pNestedModel, value);
    +      ptr->setlist_of_category(value);
    +   }
    +}
    +
    +void blog_model::syncAllNestedModel(const QStringList & relation)
    +{
    +   if (this->m_lstChild.count() <= 0) { return; }
    +   for (long l = 0; l < this->m_model.count(); l++)
    +   { this->syncNestedModel(static_cast<int>(l), relation); }
    +}
    +
    +} // namespace model_view
    +
    +

    + Note : above example shows that the source code required to work with nested models is + verbose. + So to be able to work with models and relationships, it is strongly recommended to use + QxEntityEditor application to generate all C++ source code automatically. +

    +
    +

    Interaction with QML + views

    +
    + Here is an example in QML (with Qt5, QxModelView module supports Qt4 too). + This example uses 'author' table defined in qxBlog tutorial (source code of this QML example is available in + qxBlogModelView project sample of QxOrm package) :
    +
    + + + + + + + +
    +
    // Create a model and fetch all data from database
    +qx::IxModel * pModel = new qx::QxModel<author>();
    +pModel->qxFetchAll();
    +
    +// Associate the model to a QML view and display it
    +QQuickView qmlView;
    +qmlView.rootContext()->setContextProperty("myModel", pModel);
    +qmlView.setSource(QUrl("qrc:/documents/main.qml"));
    +qmlView.show();
    +
    +
    + Here is the 'main.qml' file content :
    +
    + + + + + + + +
    +
    import QtQuick 2.1
    +import QtQuick.Controls 1.0
    +
    +Item {
    +   width: 400
    +   height: 300
    +   Row {
    +      height: 20
    +      spacing: 20
    +      Button {
    +         text: "Clear"
    +         onClicked: myModel.clear()
    +      }
    +      Button {
    +         text: "Fetch All"
    +         onClicked: myModel.qxFetchAll_()
    +      }
    +      Button {
    +         text: "Save"
    +         onClicked: myModel.qxSave_()
    +      }
    +   }
    +   ListView {
    +      y: 30
    +      height: 270
    +      model: myModel
    +      delegate: Row {
    +         height: 20
    +         spacing: 10
    +         Text { text: "id: " + author_id }
    +         TextField {
    +            text: name
    +            onTextChanged: name = text
    +         }
    +      }
    +   }
    +}
    +
    +
    + After executing this code, following window should be displayed :
    +
    + qx_model_view_02
    +
    + Note : as you can see in the 'main.qml' file, 'author_id' and 'name' + properties of 'author' model (myModel variable) can be automatically read and + write (because they are registered in QxOrm context).
    + Moreover, qx::IxModel + interface provides a list of methods for QML side (Q_INVOKABLE) to communicate with + database : for example, the 'Save' button will save the model in database without having + to write a C++ function.
    +
    + Other note : a QxEntityEditor plugin generates automatically + source code to manage relationships using nested models concept (for more details about + nested models concept, please read this french tutorial on famous developpez.com web site). +

    +
    +

    Interaction with QtWidget + views

    +
    + Here is an example to display/modify data from 'author' table (read qxBlog tutorial for 'author' class + definition) in a QTableView + (source code of this example is available in qxBlogModelView project sample of QxOrm + package) :
    +
    + + + + + + + +
    +
    // Create a model and fetch all data from database
    +qx::IxModel * pModel = new qx::QxModel<author>();
    +pModel->qxFetchAll();
    +
    +// Associate the model to a QTableView and display it
    +QTableView tableView;
    +tableView.setModel(pModel);
    +tableView.show();
    +
    +
    + After executing this code, following window should be displayed :
    +
    + qx_model_view_01
    +
    + Note : Qt provides several QtWidget views which can be mapped to a model, for + example : QListView, QTableView, QTreeView. + It is also possible to use QDataWidgetMapper class to create your own form based on a model (a french tutorial is available on developpez.com web site). +

    +
    +

    Connect model to + QxService module

    +
    + QxModelView module + provides qx::QxModelService<T, S> class template (which inherits from : qx::QxModel<T> + >> qx::IxModel + >> QAbstractItemModel). + This class has 2 template parameters : +
      +
    • T : class registered in QxOrm context with all properties exposed to Qt model/view engine ; +
    • +
    • S : service class of QxService module to access/modify + data from model (client/server requests).
    • +
    + Data provided by this model comes from client/server requests thanks to QxService module (so data are not received from a database SQL query). + The S service class must provide some methods : +
      +
    • count() : client/server request to count entities in table mapped to model (with + possibility to add a SQL query filter) ;
    • +
    • fetchById() : client/server request to fetch model properties based on identifier + parameter ;
    • +
    • fetchAll() : client/server request to fetch model with all entities in table + mapped to model ;
    • +
    • fetchByQuery() : client/server request to fetch model with entities in table + mapped to model filtered by a SQL query ;
    • +
    • insert() : client/server request to insert model data to database ;
    • +
    • update() : client/server request to update model data to database ;
    • +
    • save() : client/server request to save model data to database (insert or update) ; +
    • +
    • deleteById() : client/server request to delete an entity based on identifier + parameter ;
    • +
    • deleteAll() : client/server request to delete all entities in table mapped to + model ;
    • +
    • deleteByQuery() : client/server request to delete entities in table mapped to + model based on a SQL query ;
    • +
    • destroyById() : client/server request to delete an entity based on identifier + parameter (support soft delete behaviour, logical delete) ; +
    • +
    • destroyAll() : client/server request to delete all entities in table mapped to + model (support soft delete behaviour, logical delete) ;
    • +
    • destroyByQuery() : client/server request to delete entities in table mapped to + model based on a SQL query (support soft delete behaviour, logical + delete) ;
    • +
    • executeQuery() : client/server request to execute a custom + SQL query or stored procedure ;
    • +
    • exist() : client/server request to check if model exists based on its identifier ; +
    • +
    • isValid() : client/server request to check model validity (QxValidator module).
    • +
    +
    + Note : QxEntityEditor application is deployed with QxEECppServicesExport + and QxEECppModelViewExport plugins : these plugins generate automatically all C++ source + code required to work with QxOrm models and the QxService module. + So to use qx::QxModelService<T, S> class, it is strongly recommended to + use QxEntityEditor application to generate source code automatically. +

    +
    +
    + +

    QxOrm and MongoDB database + (C++ ODM Object Document Mapper)

    +
    + QxOrm library is able to connect to standard relational databases (MySQL, PostgreSQL, SQLite, + Oracle, Microsoft SQLServer, MariaDB, etc...), and is also able to connect to the NoSQL MongoDB database. +

    + From Wikipedia website : + MongoDB is a free and open-source cross-platform document-oriented database program. + Classified as a NoSQL database program, MongoDB uses JSON-like documents with schemas. +

    + MongoDB database has several advantages compared to standard relational databases (non-exhaustive + list) : +
      +
    • Schema-less : you don't have to maintain tables and columns (so you don't need to + write some complex scripts to migrate your database from one version to another version). + MongoDB Collections can contain Documents with different fields, of different + sizes, etc... About QxOrm library, that means that you can write your C++ persistent classes + without having to deal with DDL database schema (useful in AGILE development + environment for example) ;
    • +
    • Data are stored in BSON format (similar to JSON) : easy to read even with complex + data structures ;
    • +
    • Powerful and flexible JSON query engine with possibility to put indexes on any fields + of MongoDB Documents ;
    • +
    • MongoDB database is free of charge, and provides a support for professionals ; +
    • +
    • From MongoDB version 3.6 : MongoDB query engine provides a way to simulate JOINS + queries (between Documents) like standard relational databases.
    • +
    +
    + QxOrm library API is the same for MongoDB database and any other standard relational databases. + All QxOrm library features are available for MongoDB database : so everything in this user guide + can be applied to MongoDB database. + Main differences to take into account are : +
      +
    • It is recommended to define a C++ primary key of type QString. There is no numeric + auto-incremented value : MongoDB provides an ObjectId type which can be mapped to QString C++ + type and generated automatically (you can also create your own custom C++ type to map to + MongoDB ObjectId).
    • +
    • Queries are not SQL : MongoDB provides a JSON query engine.
    • +
    +
    + Note : QxOrm package provides a sample project named qxBlogMongoDB (in ./test/ + directory). + This sample project shows how to connect and work with MongoDB database and QxOrm library. +

    +

    Prerequisites : driver + libmongoc and libbson

    +
    + QxOrm library is based on QtSql + module from Qt framework : this module doesn't provide connectors to MongoDB database. + So QxOrm library requires 2 extra-dependencies to connect to MongoDB database : + +
    + A guide is + available to install these libraries (libmongoc and libbson) on your + development environment. +

    +
    +

    QxOrm.pri (or + QxOrm.cmake) configuration file

    +
    + Once libmongoc and libbson libraries are installed on your development + environment, you have to enable _QX_ENABLE_MONGODB compilation option in QxOrm.pri + (or QxOrm.cmake) configuration file. +

    + + + + + + + +
    +
    #######################################
    +# MongoDB Driver Library Dependencies #
    +#######################################
    +
    +# If you enable _QX_ENABLE_MONGODB option, then QxOrm library will be able to use mongoc driver to store all QxOrm registered classes in a MongoDB database
    +# When _QX_ENABLE_MONGODB compilation option is defined, you must provide following paths to manage mongoc library dependencies :
    +#  - a BSON_INCLUDE environment variable to define where bson library source code is located (or a QX_BSON_INCLUDE_PATH qmake variable)
    +#  - a MONGOC_INCLUDE environment variable to define where mongoc library source code is located (or a QX_MONGOC_INCLUDE_PATH qmake variable)
    +#  - a BSON_LIB environment variable to define where bson library is located (or a QX_BSON_LIB_PATH qmake variable)
    +#  - a MONGOC_LIB environment variable to define where mongoc library is located (or a QX_MONGOC_LIB_PATH qmake variable)
    +
    +

    + Note : once _QX_ENABLE_MONGODB compilation option is defined, you can build and + execute qxBlogMongoDB sample project in ./test/ directory to validate your + development environment with QxOrm and MongoDB. +

    +
    +

    Connection to MongoDB + database

    +
    + Here is an example of settings to connect to MongoDB database : +

    + + + + + + + +
    +
    // Parameters to connect to MongoDB database
    +qx::QxSqlDatabase * pDatabase = qx::QxSqlDatabase::getSingleton();
    +pDatabase->setDriverName("QXMONGODB");
    +pDatabase->setDatabaseName("qxBlog");
    +pDatabase->setHostName("localhost");
    +pDatabase->setPort(27017);
    +pDatabase->setUserName("");
    +pDatabase->setPassword("");
    +
    +

    +
    +

    Register a MongoDB + persistent class (Collection) in QxOrm context (mapping)

    +
    + Register a MongoDB persistent class in QxOrm context is similar to register a persistent class for any other standard relational + databases. + Here is a persistent class example from qxBlogMongoDB sample project : +

    + File blog.h : + + + + + + + +
    +
    #ifndef _QX_BLOG_BLOG_H_
    +#define _QX_BLOG_BLOG_H_
    +
    +#include "author.h"
    +#include "comment.h"
    +#include "category.h"
    +
    +class QX_BLOG_DLL_EXPORT blog
    +{
    +public:
    +// -- properties
    +   QString        m_id;
    +   QString        m_text;
    +   QDateTime      m_dt_creation;
    +   author_ptr     m_author;
    +   list_comment   m_commentX;
    +   list_category  m_categoryX;
    +// -- contructor, virtual destructor
    +   blog() { ; }
    +   virtual ~blog() { ; }
    +};
    +
    +QX_REGISTER_PRIMARY_KEY(blog, QString)
    +QX_REGISTER_HPP_QX_BLOG(blog, qx::trait::no_base_class_defined, 0)
    +
    +typedef std::shared_ptr<blog> blog_ptr;
    +typedef std::vector<blog_ptr> list_blog;
    +
    +#endif // _QX_BLOG_BLOG_H_
    +
    +
    + File blog.cpp : + + + + + + + +
    +
    #include "../include/precompiled.h"
    +#include "../include/blog.h"
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_BLOG(blog)
    +
    +namespace qx {
    +template <> void register_class(QxClass<blog> & t)
    +{
    +   t.id(& blog::m_id, "blog_id");
    +
    +   t.data(& blog::m_text, "blog_text");
    +   t.data(& blog::m_dt_creation, "date_creation");
    +   t.data(& blog::m_categoryX, "list_category"); // Embedded relationship
    +
    +   t.relationManyToOne(& blog::m_author, "author_id"); // Referenced relationship
    +   t.relationOneToMany(& blog::m_commentX, "list_comment", "blog_id"); // Referenced relationship
    +}}
    +
    +
    + Note : this example shows how to define : + +
    +

    Manage ObjectId + (primary key)

    +
    + It is recommended to define a C++ primary key of type QString. + There is no numeric auto-incremented value : MongoDB + provides an ObjectId type which can be mapped to QString C++ type and generated + automatically (you can also create your own custom C++ type to map to MongoDB ObjectId). +

    +
    +
    +

    Insert a C++ instance + (Document) in MongoDB database

    +
    + Here is an example to insert a document in MongoDB database with primary key generated + automatically (MongoDB ObjectId) : +

    + + + + + + + +
    +
    // Insert one author without id
    +author_ptr author_1 = std::make_shared<author>();
    +author_1->m_name = "author_1";
    +author_1->m_sex = author::male;
    +author_1->m_birthdate = QDate(1998, 07, 12);
    +daoError = qx::dao::insert(author_1);
    +
    +

    + Here is an example to insert a document in MongoDB database providing a custom primary key : +

    + + + + + + + +
    +
    // Insert one author with a custom id
    +author_ptr author_2 = std::make_shared<author>();
    +author_2->m_id = "my_custom_id_author_2";
    +author_2->m_name = "author_2";
    +author_2->m_sex = author::female;
    +author_2->m_birthdate = QDate(2003, 02, 28);
    +daoError = qx::dao::insert(author_2);
    +
    +

    +

    Insert many C++ + instances (list of Documents) in MongoDB database

    +
    + Here is an example to insert several documents in MongoDB database : +

    + + + + + + + +
    +
    // Insert many authors with/without ids
    +QList<author> authorX;
    +author author_3; author_3.m_name = "author_3"; author_3.m_sex = author::female; author_3.m_birthdate = QDate(1968, 05, 01);
    +author author_4; author_4.m_id = "my_custom_id_author_4"; author_4.m_name = "author_4"; author_4.m_sex = author::male;
    +author author_5; author_5.m_name = "author_5"; author_5.m_sex = author::female; author_5.m_birthdate = QDate(1978, 03, 03);
    +authorX.append(author_3); authorX.append(author_4); authorX.append(author_5);
    +daoError = qx::dao::insert(authorX);
    +
    +

    + Note : QxOrm library supports several C++ types to manage lists + and collections. +

    +
    +
    +

    Update a C++ instance + (Document) in MongoDB database

    +
    + Here is an example to update a document in MongoDB database : +

    + + + + + + + +
    +
    // Update one author
    +author author_4;
    +author_4.m_id = "my_custom_id_author_4";
    +author_4.m_name = "author_4_modified";
    +daoError = qx::dao::update(author_4);
    +
    +

    +

    Update many C++ + instances (list of Documents) in MongoDB database

    +
    + Here is an example to update several documents in MongoDB database : +

    + + + + + + + +
    +
    // Update many authors
    +QList<author> authorX;
    +author_3.m_name = "author_3_modified_twice"; authorX.append(author_3);
    +author_2->m_name = "author_2_modified"; authorX.append(* author_2);
    +author_1->m_name = "author_1_modified"; authorX.append(* author_1);
    +daoError = qx::dao::update(authorX);
    +
    +

    + Note : QxOrm library supports several C++ types to manage lists + and collections. +

    +
    +
    +

    Delete a C++ instance + (Document) from MongoDB database

    +
    + Here is an example to delete a document from MongoDB database : +

    + + + + + + + +
    +
    // Delete one author by id
    +author_ptr pAuthor = std::make_shared<author>();
    +pAuthor->m_id = "my_custom_id_author_4";
    +daoError = qx::dao::delete_by_id(pAuthor);
    +
    +

    +

    Delete many C++ + instances (list of Documents) from MongoDB database

    +
    + Here is an example to delete several documents from MongoDB database by identifier (primary + key) : +

    + + + + + + + +
    +
    // Delete many authors by id
    +QList<author> authorX;
    +author_3.m_id = "id_author_3"; authorX.append(author_3);
    +author_2->m_id = "id_author_2"; authorX.append(* author_2);
    +author_1->m_id = "id_author_1"; authorX.append(* author_1);
    +daoError = qx::dao::delete_by_id(authorX);
    +
    +

    + Here is an example to delete several documents from MongoDB database by JSON query : +

    + + + + + + + +
    +
    // Delete authors by query (all male)
    +qx_query query{ { "sex", author::male } };
    +daoError = qx::dao::delete_by_query<author>(query);
    +
    +

    + To delete all documents from author collection : +

    + + + + + + + +
    +
    // Delete all authors
    +daoError = qx::dao::delete_all<author>();
    +
    +

    + Note : QxOrm library supports several C++ types to manage lists + and collections. +

    +
    +
    +

    Fetch a C++ instance + (Document) from MongoDB database

    +
    + Here is an example to fetch a document from MongoDB database by identifier (primary key) : +

    + + + + + + + +
    +
    // Fetch one author by id
    +author_ptr pAuthor = std::make_shared<author>();
    +pAuthor->m_id = "my_custom_id_author_2";
    +daoError = qx::dao::fetch_by_id(pAuthor);
    +
    +

    +

    Fetch many C++ + instances (list of Documents) from MongoDB database

    +
    + Here is an example to fetch several documents from MongoDB database by identifier (primary + key) : +

    + + + + + + + +
    +
    // Fetch many authors by id
    +QList<author> authorX;
    +author_3.m_id = "id_author_3"; authorX.append(author_3);
    +author_2->m_id = "id_author_2"; authorX.append(* author_2);
    +author_1->m_id = "id_author_1"; authorX.append(* author_1);
    +daoError = qx::dao::fetch_by_id(authorX);
    +
    +

    + Here is an example to fetch several documents from MongoDB database by JSON query : +

    + + + + + + + +
    +
    // Fetch many authors by query (only female)
    +list_author list_of_female_author;
    +qx_query query{ { "sex", author::female } };
    +daoError = qx::dao::fetch_by_query(query, list_of_female_author);
    +
    +

    + Here is an example to fetch all documents from author collection in MongoDB database : +

    + + + + + + + +
    +
    // Fetch all authors
    +list_author allAuthors;
    +daoError = qx::dao::fetch_all(allAuthors);
    +
    +

    + Here is an example to fetch all documents from author collection in MongoDB database + (providing which fields/columns to fetch) : +

    + + + + + + + +
    +
    // Fetch all authors (with only 'date_creation' and 'name' properties)
    +list_author allAuthors;
    +QStringList columns = QStringList() << "date_creation" << "name";
    +daoError = qx::dao::fetch_all(allAuthors, NULL, columns);
    +
    +

    + Note : QxOrm library supports several C++ types to manage lists + and collections. +

    +
    +
    +

    JSON queries

    +
    + The main difference between standard relational databases and MongoDB database is query format : + instead of SQL, MongoDB provides a JSON query + engine. +

    +

    Using qx::QxSqlQuery + class (or qx_query alias)

    +
    + qx::QxSqlQuery class (or qx_query alias) used to build standard + SQL queries is also able to build JSON queries for MongoDB database. + This class is based on C++11 std::initializer_list feature to write C++ queries like JSON + queries (similar syntax). + Please note that you can also write your JSON query with a string (if your compiler doesn't + support C++11 std::initializer_list feature for example). + For example : +

    + + + + + + + +
    +
    // Fetch many authors by query (only female)
    +list_author list_of_female_author;
    +qx_query query { { "sex", author::female } };
    +daoError = qx::dao::fetch_by_query(query, list_of_female_author);
    +
    +

    +
    +

    Using MongoDB + aggregation framework

    +
    + MongoDB database provides a powerful aggregation framework to build queries. + Here is an example to call this MongoDB aggregation engine with qx::QxSqlQuery class (or qx_query alias), the first constructor + parameter must be equal to aggregate : +

    + + + + + + + +
    +
    // Fetch by query using MongoDB aggregation framework (only female)
    +list_author list_of_female_author;
    +qx_query queryAggregate("aggregate",
    +               "[ { \"$match\" : { \"sex\" : " + QString::number(static_cast<int>(author::female)) + " } } ]");
    +daoError = qx::dao::fetch_by_query(queryAggregate, list_of_female_author);
    +
    +

    +
    +

    Add 'sort', 'limit', + 'skip', etc..., properties to JSON query

    +
    + It is often required to limit data received from database, or to sort them. + To manage these operations, MongoDB database provides projection. + Here is an example of projection with qx::QxSqlQuery class (or + qx_query alias), see the QStringList constructor parameter (or second + constructor parameter with std::initializer_list) : +

    + + + + + + + +
    +
    // Fetch by query (only female) adding 'sort', 'limit', 'skip', etc... commands (see second query QStringList parameter)
    +list_of_female_author.clear();
    +qx_query queryOpts(QStringList() << "{ \"sex\" : " + QString::number(static_cast(author::female)) + " }"
    +                              << "{ \"sort\" : { \"sex\" : -1 }, \"limit\" : 2 }");
    +daoError = qx::dao::fetch_by_query(queryOpts, list_of_female_author);
    +
    +

    +
    +

    Execute a custom + query

    +
    + To execute a custom query in MongoDB database, QxOrm library provides the qx::dao::call_query() function. + Query results can be converted to QVariantMap or QList<QVariantMap> (if query returns a cursor) to + iterate over all database response. + Here are some examples of custom queries : +

    + + + + + + + +
    +
    // Drop database
    +qx_query dropDB("{ \"dropDatabase\" : 1 }");
    +QSqlError daoError = qx::dao::call_query(dropDB);
    +
    +

    + + + + + + + +
    +
    // Call a custom query and get JSON response as QVariantMap
    +qx_query customQuery("{ \"find\": \"author\", \"filter\": { } }");
    +daoError = qx::dao::call_query(customQuery); qAssert(! daoError.isValid());
    +QString responseCustomQuery = customQuery.response().toString();
    +QVariantMap responseCustomQueryAsJson;
    +qx::serialization::json::from_string(responseCustomQueryAsJson, responseCustomQuery);
    +
    +

    + + + + + + + +
    +
    // Call a custom query with cursor and get JSON response as QList<QVariantMap>
    +qx_query customQueryCursor("cursor", "{ \"find\": \"author\", \"filter\": { } }");
    +daoError = qx::dao::call_query(customQueryCursor); qAssert(! daoError.isValid());
    +QString responseCustomQueryCursor = customQueryCursor.response().toString();
    +QList<QVariantMap> responseCustomQueryCursorAsJson;
    +qx::serialization::json::from_string(responseCustomQueryCursorAsJson, responseCustomQueryCursor);
    +
    +

    +
    +
    +

    Relationships engine + (MongoDB version 3.6 or + is required)

    +
    + QxOrm library relationship engine supports MongoDB database (MongoDB + version 3.6 or + is required). + So QxOrm library is able to fetch Document fields over several Collections using only one query + (similar to JOINS in SQL). +

    + Here is an example to fetch a Document and 1 level of relationships (parent > children) : +

    + + + + + + + +
    +
    // Fetch blog with all relations : 'author', 'comment' and 'category' (MongoDB version 3.6+ is required for relationships)
    +blog_ptr blog = std::make_shared<blog>();
    +blog->m_id = "id_blog_1";
    +daoError = qx::dao::fetch_by_id_with_all_relation(blog);
    +
    +

    + Here is an example to fetch a Document and 4 levels of relationships (using + *->*->*->* syntax) : +

    + + + + + + + +
    +
    // Fetch blog with many relations using "*->*->*->*" (4 levels of relationships)
    +blog_ptr blog = std::make_shared<blog>();
    +blog->m_id = "id_blog_1";
    +daoError = qx::dao::fetch_by_id_with_relation("*->*->*->*", blog);
    +
    +

    + Here is an example to fetch a Document providing a list of relationships and fields to fetch + (using { <col_1>, <col_2>, etc... } syntax) : +

    + + + + + + + +
    +
    // Fetch relations defining fields to fetch with syntax { col_1, col_2, etc... }
    +list_blog lstBlogComplexRelation;
    +QStringList relations = QStringList() << "{ blog_text }" << "author_id { name, birthdate }" << "list_comment { comment_text } -> blog_id -> *";
    +daoError = qx::dao::fetch_all_with_relation(relations, lstBlogComplexRelation);
    +
    +

    + Here is an example to fetch a Document providing a list of relationships and fields to not fetch + (using -{ <col_1>, <col_2>, etc... } syntax) : +

    + + + + + + + +
    +
    // Fetch relations defining columns to remove before fetching with syntax -{ col_1, col_2, etc... }
    +list_blog lstBlogComplexRelation2;
    +QStringList relations = QStringList() << "-{ blog_text }" << "author_id -{ name, birthdate }" << "list_comment -{ comment_text } -> blog_id -> *";
    +daoError = qx::dao::fetch_all_with_relation(relations, lstBlogComplexRelation2);
    +
    +

    +

    Embedded vs + Referenced

    +
    + One big advantage of MongoDB database is the possibility to store complex data structure (not + limited by a 2 dimensions table/column structure like standard relational databases). + A MongoDB Document can contain an objet and several sub-objects (hierarchy in Document + structure). + Include a sub-object in a Document has some advantages (no JOIN for example, so faster + to fetch) and some disadvantages (a same object can be duplicated in database). + So it is important to adopt the right strategy to store your data. +

    + QxOrm library supports both : +
      +
    • Embedded relationship : the sub-object is included in the Document ;
    • +
    • Referenced relationship : create a JOIN like standard relational + databases.
    • +
    +
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<blog> & t)
    +{
    +   t.id(& blog::m_id, "blog_id");
    +
    +   t.data(& blog::m_text, "blog_text");
    +   t.data(& blog::m_dt_creation, "date_creation");
    +   t.data(& blog::m_categoryX, "list_category"); // Embedded relationship
    +
    +   t.relationManyToOne(& blog::m_author, "author_id"); // Referenced relationship
    +   t.relationOneToMany(& blog::m_commentX, "list_comment", "blog_id"); // Referenced relationship
    +}}
    +
    +

    +
    +
    +

    Create automatically + indexes

    +
    + QxOrm library provides a way to generate indexes automatically (this function should be called + at the beginning of your program, for example in the main) : +
      +
    • all indexes to manage relationships between Collections (to optimize JOINS) ;
    • +
    • all indexes defined by qx::IxDataMember::setIndex() method (in qx::register_class() + function).
    • +
    +
    + + + + + + + +
    +
    // To optimize queries : create automatically indexes based on relationships and properties marked as 'index'
    +daoError = qx::dao::mongodb::QxMongoDB_Helper::autoCreateIndexes(true);
    +
    +

    +
    +

    +
    + +

    HTTP/HTTPS web server + (QxHttpServer module)

    +
    + QxOrm library provides a standalone, multi-threaded and easy to use HTTP 1.1 web server named + QxHttpServer module (based on QxService module). + QxHttpServer module doesn't require any Apache or Nginx + installation. +

    + QxHttpServer module supports several features : + + Combined with QxRestApi module (which provides a JSON API to request your + persistent data layer), QxHttpServer module is designed to develop modern web applications. + For example, SPA + (Single-Page Applications) web applications with famous Javascript frameworks like AngularJS, React, Meteor.js, + etc... +

    + Note : to enable QxHttpServer module, you have to define _QX_ENABLE_QT_NETWORK compilation option in QxOrm.pri (ou QxOrm.cmake) configuration file. + _QX_ENABLE_QT_NETWORK compilation option adds a dependency to QtNetwork binary provided + by Qt library. +

    + Other note : QxOrm package contains a test project named + qxBlogRestApi. + This test project is a web application with several examples to request a persistent data layer + from a web page (HTML and Javascript). +

    +

    Hello World !

    +
    + Here is a HTTP web server source code based on QxHttpServer module (this web server just + returns Hello World ! to web client) : +

    + + + + + + + +
    +
    #include <QtCore/qcoreapplication.h>
    +#include <QxOrm.h>
    +
    +int main(int argc, char * argv[])
    +{
    +   QCoreApplication app(argc, argv);
    +
    +   // HTTP server settings
    +   qx::service::QxConnect * serverSettings = qx::service::QxConnect::getSingleton();
    +   serverSettings->setPort(9642); // HTTP server listening port
    +   serverSettings->setKeepAlive(5000); // Keep-alive connection with client during 5s, then socket is disconnected and thread becomes available for other clients
    +   serverSettings->setThreadCount(50); // Number of threads waiting for client's requests,
    +                                                           // which means also how many requests can be handled simultaneously (in parallel) by HTTP server
    +
    +   // Create a QxOrm HTTP server instance
    +   qx::QxHttpServer httpServer;
    +
    +   // Define all HTTP server routes (dispatcher) to handle requests
    +   // Each callback is executed in a dedicated thread, so QxOrm HTTP server can handle several requests in parallel
    +   httpServer.dispatch("GET", "/", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
    +      response.data() = "Hello World !";
    +   });
    +
    +   // Start HTTP server
    +   httpServer.startServer();
    +
    +   // Start event loop
    +   return app.exec();
    +}
    +
    +

    + Result : open a web browser (Chrome, Firefox, Safari, Internet Explorer, Opera, etc...) + and go to this URL : http://localhost:9642/. Your web browser should display : +

    + QxHttpServer Hello World ! +

    +
    +

    HTTP/HTTPS web server + settings

    +
    + HTTP web server settings are available with qx::service::QxConnect singleton class : +
      +
    • setPort() : web server listening port (default web server port is 80 but you can + define what you want) ;
    • +
    • setThreadCount() : number of threads used by web server to handle HTTP requests + (which means number of simultaneous clients managed by web server) ;
    • +
    • setMaxWait() : timeout in milli-seconds (for example to read/write on socket), -1 + value means no timeout ;
    • +
    • setCompressData() : if HTTP client supports GZIP compression, then text responses + (HTML / Javascript / CSS files, JSON stream, etc...) will be compressed as GZIP ;
    • +
    • setKeepAlive() : socket stay connected to client during X milli-seconds, -1 + value means never disconnect ;
    • +
    • setSessionTimeOut() : timeout in milli-seconds before deleting unused sessions (server side storage per client).
    • +
    +
    +

    Secured connections + SSL/TLS

    +
    + qx::service::QxConnect singleton class provides also some parameters + to manage HTTPS secured connections (SSL and/or TLS).
    + Here is a secured connection settings example with server certificate and CA certificate + authority (you can test this code with qxBlogRestApi project + example) : +

    + + + + + + + +
    +
    // Certificates created with this tutorial : https://deliciousbrains.com/ssl-certificate-authority-for-local-https-development/
    +QFile::copy(":/documents/cert_qxorm_ca.pem", appPath.filePath("files/cert_qxorm_ca.pem"));
    +QFile::copy(":/documents/cert_qxorm_server.crt", appPath.filePath("files/cert_qxorm_server.crt"));
    +QFile::copy(":/documents/cert_qxorm_server.key", appPath.filePath("files/cert_qxorm_server.key"));
    +
    +QFile fileCertCA(appPath.filePath("files/cert_qxorm_ca.pem"));
    +fileCertCA.open(QIODevice::ReadOnly);
    +QList<QSslCertificate> certCA; certCA << QSslCertificate(fileCertCA.readAll());
    +
    +QFile fileCertServerPublic(appPath.filePath("files/cert_qxorm_server.crt"));
    +fileCertServerPublic.open(QIODevice::ReadOnly);
    +QSslCertificate certServerPublic(fileCertServerPublic.readAll());
    +
    +QFile fileCertServerPrivate(appPath.filePath("files/cert_qxorm_server.key"));
    +fileCertServerPrivate.open(QIODevice::ReadOnly);
    +QSslKey certServerPrivate(fileCertServerPrivate.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, "qxorm");
    +
    +qx::service::QxConnect * serverSettings = qx::service::QxConnect::getSingleton();
    +serverSettings->setSSLEnabled(true);
    +serverSettings->setSSLCACertificates(certCA);
    +serverSettings->setSSLLocalCertificate(certServerPublic);
    +serverSettings->setSSLPrivateKey(certServerPrivate);
    +
    +
    +

    + Note : by default, all SSL errors are ignored (often certificates errors). + To manage your own security level, you can use following functions (from qx::service::QxConnect singleton class) : + +
    +
    +
    +

    Routing URL (dispatcher / + endpoints)

    +
    + QxHttpServer module provides an URL routing engine (dispatcher) to define + functions (or lambda) to execute based on HTTP request parameters (HTTP method GET, POST, + DELETE, etc... + URL).
    + Functions (or lambda) must be defined with this signature : void myRequestHandler(qx::QxHttpRequest & request, + qx::QxHttpResponse & response); +

    + qx::QxHttpServer class (or its qx_http_server alias) has following methods : +
      +
    • setCustomRequestHandler() : define a function (or lambda) executed if dispatcher + doesn't find any other matched function ;
    • +
    • dispatch() : first parameter is HTTP method (GET, POST, DELETE, etc...), + second parameter is requested URL (or its pattern), third parameter is function (or + lambda) to execute ;
    • +
    • beforeDispatching() : function (or lambda) executed before handling HTTP request + (can be used for example to log, or to manage an authentication process) ;
    • +
    • afterDispatching() : function (or lambda) executed after handling HTTP request + (can be used for example to log) ;
    • +
    • clearDispatcher() : remove all routing rules from dispatcher (only function or + lambda defined by setCustomRequestHandler() will be executed).
    • +
    + Note : dispatcher is thread-safe, so you can define URL routing rules even if web server + is running. +

    + Other note : each function (or lambda) is + executed in its own thread + . + So QxOrm library HTTP web server can handle several HTTP requests simultaneously. +

    + Example n�1 : this routing rule handles all HTTP requests with method GET + URL + starts with /files/, and returns a static file content stored on server + (QDir::currentPath() is static files root directory, and 5000 is chunked response + size, this last parameter is optional) : +

    + + + + + + + +
    +
    httpServer.dispatch("GET", "/files/*", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
    +   qx::QxHttpServer::buildResponseStaticFile(request, response, QDir::currentPath(), 5000);
    +});
    +
    +

    + Example n�2 : this routing rule handles all HTTP requests with method POST + URL + is /qx, and calls QxRestApi module (which provides a JSON API to + request persistent data layer). + Example n�1 (static files) and example n�2 (QxRestApi module) are a good starting + point to develop a SPA (Single-Page Applications) web application with famous Javascript + frameworks like AngularJS, React, Meteor.js, etc... +

    + + + + + + + +
    +
    httpServer.dispatch("POST", "/qx", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
    +   qx::QxHttpServer::buildResponseQxRestApi(request, response);
    +});
    +
    +

    + Example n�3 : this routing rule handles all HTTP requests with method GET + URL is + /test_big_json, and builds a JSON response with an array of 10000 items : +

    + + + + + + + +
    +
    httpServer.dispatch("GET", "/test_big_json", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
    +   // To compare with this benchmark : https://blog.binaryspaceship.com/2017/cpp-rest-api-frameworks-benchmark/
    +   // This is more a JSON benchmark than HTTP server benchmark (RapidJSON is faster than Qt QJson engine)
    +   QJsonArray arr; Q_UNUSED(request);
    +   for (int i = 0; i < 10000; ++i)
    +   {
    +      QJsonObject item;
    +      item.insert("id", QString::number(i));
    +      item.insert("name", QString("Hello World"));
    +      item.insert("type", QString("application"));
    +      arr.append(item);
    +   }
    +   QJsonDocument doc(arr);
    +   response.headers().insert("Content-Type", "application/json; charset=utf-8");
    +   response.data() = doc.toJson(QJsonDocument::Compact);
    +});
    +
    +

    + Note : + dispatch() order is very important. + The first item found by the dispatcher which matches requested URL is executed (all other + dispatcher items are ignored). + So you have to define first the most specific URL, and you have to define last the most generic + URL (for example, pattern /* matches all URLs, so this is the most generic dispatcher + item). +

    +

    Dynamic URL + routing

    +
    + QxHttpServer dispatcher supports dynamic URL routing.
    + You can define some variables inside URL pattern with this syntax : <var_name:var_type> (var_type + is optional, and can be equal to : int, long, float, double, string). +

    + Dynamic URL routing is useful to define REST API.
    + For example, /blog/<blog_id:int> pattern + GET HTTP method can be used to + fetch a blog based on its numeric unique identifier (fetch_by_id). +

    + URL is a list of segments splitted by character /.
    + QxHttpServer dispatcher checks each segment from requested URL : if all segments match + the pattern, then the function (or lambda) is executed.
    + To get dynamic variables values from URL, you must write : request.dispatchParams().value("var_name") + (which returns a QVariant). +

    + Example : this routing rule handles all HTTP requests with method GET + URL + starts with /params/, followed by a segment which will contain the value of + var1 variable, followed by a numeric segment which will contain the + value of var2 variable. + The lambda returns a HTTP response which displays the values of var1 and var2 + variables from URL. + If the web browser calls /params/abc/123/ URL then the function (or lambda) will be + executed, BUT if the web browser calls /params/abc/def/ URL then the function + (or lambda) won't be executed (because def is not numeric) and dispatcher will search + another item to execute : +

    + + + + + + + +
    +
    httpServer.dispatch("GET", "/params/<var1>/<var2:int>", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
    +   response.data() = "Test URL dispatch parameters :\r\n";
    +   response.data() += " - var1 = " + request.dispatchParams().value("var1").toByteArray() + "\r\n";
    +   response.data() += " - var2 = " + request.dispatchParams().value("var2").toByteArray() + "\r\n";
    +});
    +
    +

    + Note : you can also define a regular expression to route URLs with this syntax : <var_name:{my_reg_exp}>. +

    +
    +
    +

    Get HTTP request + parameters

    +
    + qx::QxHttpRequest class (or its qx_http_request alias) contains all HTTP request parameters : +
      +
    • QUrl & url() : web browser requested URL ;
    • +
    • QString & command() : HTTP method (GET, POST, PUT, DELETE, etc...) ;
    • +
    • QString & version() : HTTP version provided by web browser (for example + HTTP/1.1) ; +
    • +
    • QByteArray & data() : HTTP request content ;
    • +
    • QByteArray header(const QByteArray & key) : get a HTTP header value provided by + web browser (for example : request.header("Accept-Encoding")) ;
    • +
    • QxHttpCookie cookie(const QByteArray & name) : get a HTTP + cookie provided by web browser ;
    • +
    • QString param(const QString & key) : get a HTTP parameter value (from URL, or from + HTTP request content if 'content-type' is + 'application/x-www-form-urlencoded') ; +
    • +
    • QHash<QString, QVariant> & dispatchParams() : list of URL dynamic parameters + computed by dispatcher (routing engine) ;
    • +
    • QString & sourceAddress() : IP address of client web browser ;
    • +
    • long & sourcePort() : port used by client web browser ;
    • +
    • QString guid() : internal HTTP request unique identifier (can be used to log for + example).
    • +
    +
    +
    +

    Build HTTP response +

    +
    + qx::QxHttpResponse class (or its qx_http_response alias) is used to build HTTP response : +
      +
    • int & status() : HTTP response code (by default 200 which means OK) ;
    • +
    • QByteArray & data() : HTTP response content ;
    • +
    • QByteArray header(const QByteArray & key) : send a HTTP header to web browser (by + default, some headers are created automatically : Server, Date, + Content-Type and Connection) ; +
    • +
    • QxHttpCookie cookie(const QByteArray & name) : send a HTTP + cookie to web browser ;
    • +
    • qx_bool writeChunked(const QByteArray & data) : can be used to send chunked responses.
    • +
    +
    +
    +

    Sessions (storage per + client on server side)

    +
    + HTTP sessions are a way to store some data related to a client on server side. + Session data are available for each client's requests (until session is expired). + The first time server access to a client's session, a HTTP cookie with + a unique identifier is generated and attached to HTTP response. + Then all HTTP requests sent by client web browser will contain automatically a HTTP cookie with the same unique identifier. + When a session is unused and expired, then it is deleted automatically. +

    + qx::QxHttpSession class (or its qx_http_session alias) is a HTTP session on server side.
    + qx::QxHttpSessionManager singleton class must be used to access to a + session : +

    + + + + + + + +
    +
    httpServer.dispatch("GET", "/", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
    +   // If this is the first time to access to session, then a cookie is created automatically and attached to the response
    +   // Then each request sent by web browser will contain a cookie with the session id
    +   // The session expires on server side after qx::service::QxConnect::setSessionTimeOut() milliseconds
    +   qx::QxHttpSession_ptr session = qx::QxHttpSessionManager::getSession(request, response);
    +   if (session) { session->set("last_request_per_user", QDateTime::currentDateTime()); }
    +});
    +
    +

    + Note : qx::QxHttpSession class contains a hash-map + (QHash<QByteArray, QVariant>) to store any values related to a client. +

    + Other note : qx::service::QxConnect::setSessionTimeOut() method can be used to define + a timeout (in milli-seconds) to delete unused sessions. +

    +
    +

    Cookies

    +
    + From Wikipedia website : + an HTTP cookie (also called web cookie, Internet cookie, browser cookie, or simply cookie) is a + small piece of data sent from a website and stored on the user's computer by the user's web + browser while the user is browsing. + Cookies were designed to be a reliable mechanism for websites to remember stateful information + (such as items added in the shopping cart in an online store) or to record the user's browsing + activity (including clicking particular buttons, logging in, or recording which pages were + visited in the past). +

    + qx::QxHttpRequest and qx::QxHttpResponse + classes provide the method cookies() to get cookies sent by web browser or + generate some cookies in HTTP response. + For example : +

    + + + + + + + +
    +
    qx::QxHttpCookie cookie;
    +cookie.name = "my_http_cookie";
    +cookie.value = "my_value";
    +response.cookies().insert(cookie.name, cookie);
    +
    +

    + Note : an HTTP cookie is added automatically to HTTP response when accessing for the + first time to a session (server side storage per client). +

    +
    +

    Static files

    +
    + qx::QxHttpServer class (or its qx_http_server alias) provides a static method to send to client + web browser a file content stored on server side (for example : HTML, Javascript, CSS, PNG, + JPEG, videos, etc...). +

    + + + + + + + +
    +
    httpServer.dispatch("GET", "/files/*", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
    +   qx::QxHttpServer::buildResponseStaticFile(request, response, QDir::currentPath(), 5000);
    +});
    +
    +
    +
      +
    • The third parameter (QDir::currentPath() in the example) defines the root + directory where files are stored on the server ;
    • +
    • The fourth parameter (5000 in the example) is optional and defines chunked response size. This parameter can be useful to send big + files (streaming).
    • +
    +
    +
    +

    Chunked responses

    +
    + From Wikipedia + website : Chunked transfer encoding is a streaming data transfer mechanism available in + version 1.1 of the Hypertext Transfer Protocol (HTTP). + In chunked transfer encoding, the data stream is divided into a series of non-overlapping + "chunks". + The chunks are sent out and received independently of one another. + No knowledge of the data stream outside the currently-being-processed chunk is necessary for + both the sender and the receiver at any given time. + Each chunk is preceded by its size in bytes. + The transmission ends when a zero-length chunk is received. + The chunked keyword in the Transfer-Encoding header is used to indicate chunked transfer. +

    + The introduction of chunked encoding in HTTP 1.1 provided various benefits : +
      +
    • Chunked transfer encoding allows a server to maintain an HTTP persistent connection for + dynamically generated content.
    • +
    • Chunked encoding allows the sender to send additional header fields after the message + body. This is important in cases where values of a field cannot be known until the content + has been produced, such as when the content of the message must be digitally signed.
    • +
    +
    + qx::QxHttpResponse class provides the qx_bool writeChunked(const + QByteArray & data) method to send chunked response. + It is used for example to send big static files (streaming) : +

    + + + + + + + +
    +
    while (! file.atEnd())
    +{
    +   if (! response.writeChunked(file.read(chunkedSize))) { return; }
    +}
    +
    +

    + Note : the first response.writeChunked() call sends automatically all HTTP + response headers. + So you have to define all HTTP response headers before calling response.writeChunked(). +

    +
    +

    Requests using JSON API + (QxRestApi module)

    +
    + QxRestApi module provides a generic JSON API to request your persistent + data layer (CRUD operations, complex queries, several levels of relationships, custom JSON + output format, call dynamically native C++ functions registered in QxOrm context, instance + validation, call custom database queries). +

    + This user manual has a full chapter dedicated to QxRestApi module : it + contains several examples to request persistent data layer. + Combining QxRestApi module and QxHttpServer module : you have + all tools to develop modern web applications. + For example, SPA + (Single-Page Applications) web applications with famous Javascript frameworks like + AngularJS, React, Meteor.js, + etc... +

    + Note : QxOrm package provides a test project named + qxBlogRestApi. + This project includes a HTTP web server developed with QxOrm library, and a client source code + developed with HTML + Javascript (with jQuery). +

    + For example, here is the Javascript function used to send JSON requests (POST method) from + client web browser to QxOrm HTTP web server (all requests are sent to the same URL /qx) : +

    + + + + + + + +
    +
    function sendRequest(request) {
    +   $.post("/qx", request, function(data, status, xhr) {
    +      $("#txtResponse").val(JSON.stringify(data, null, 3));
    +   }, "json").fail(function(error) {
    +      alert("An error occurred sending request to QxOrm HTTP server : " + error);
    +   });
    +}
    +
    +

    + On server side, handling these requests is very easy : qx::QxHttpServer class (or its qx_http_server alias) provides the + qx::QxHttpServer::buildResponseQxRestApi() static method : +

    + + + + + + + +
    +
    httpServer.dispatch("POST", "/qx", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
    +   qx::QxHttpServer::buildResponseQxRestApi(request, response);
    +});
    +
    +

    + Here is a JSON request example sent by client web browser to get the list of all blogs stored in + database (fetch_all) : +

    +
    +
    {
    +   "request_id": "2b393e4c-a00c-45dc-a279-e9d76f1c55cf",
    +   "action": "fetch_all",
    +   "entity": "blog"
    +}
    +
    +

    + Here is the JSON response with the list of all blogs : +

    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 1,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-03-27T20:51:23.107",
    +         "list_category": [],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 2,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-03-27T20:51:23.107",
    +         "list_category": [],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 3,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-03-27T20:51:23.107",
    +         "list_category": [],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 4,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-03-27T20:51:23.107",
    +         "list_category": [],
    +         "list_comment": []
    +      }
    +   ],
    +   "request_id": "2b393e4c-a00c-45dc-a279-e9d76f1c55cf"
    +}
    +
    +

    +
    +

    WebSocket

    +
    + From Wikipedia website : + WebSocket is a computer communications protocol, providing full-duplex communication channels + over a single TCP connection. + The WebSocket protocol enables interaction between a web browser (or other client application) + and a web server with lower overheads, facilitating real-time data transfer from and to the + server. + This is made possible by providing a standardized way for the server to send content to the + client without being first requested by the client, and allowing messages to be passed back and + forth while keeping the connection open. + In this way, a two-way ongoing conversation can take place between the client and the server. +

    + QxOrm library is based on Qt framework which already provides a WebSocket + implementation.
    + Create a web server with Qt WebSocket is very easy : there are several examples in + Qt documentation. +

    + So you can implement a web server like this : +
      +
    • a listening port for HTTP connections (using QxHttpServer module) ;
    • +
    • another listening port for WebSocket connections (using QtWebSockets + module provided by Qt).
    • +
    +
    + Note : a WebSocket connection is often created in Javascript code from client web + browser, so having 2 listening ports (1 for HTTP, and another for WebSocket) is not a problem. +

    +
    +

    Performance (tested with + Apache Benchmark)

    +
    + Here is a performance test result with following parameters : +
      +
    • Operating System (OS) : Windows 2010 64bits ;
    • +
    • CPU : Intel Core i7-6820HQ @ 2.70GHz (laptop) ;
    • +
    • Qt version : 5.1.1 (release mode) ;
    • +
    • QxOrm version : 1.4.6 (built in release mode with Visual Studio 2012, default + parameters, no specific optimization) ;
    • +
    • HTTP web server : qxBlogRestApi test project ;
    • +
    • Tool to execute the test : Apache Benchmark ;
    • +
    • Simulate 20000 requests with 50 concurrents connections simultaneously : ab -n + 20000 -c 50 -k http://localhost:9642/params/abc/123
    • +
    +
    + Test results show that QxOrm HTTP web server can handle + more than 12000 requests per second : +

    + QxHttpServer performance +

    +

    Improve performance + with epoll dispatcher on Linux

    +
    + On Linux, you can improve HTTP web server performance using epoll to manage your + sockets. + By default, Qt framework is based on a slower process (select), but a method exists to + define another event loop and dispatcher. + Several libraries are available, for example : + +
    + qx::QxHttpServer class (or its qx_http_server alias) provides a method to define a custom + epoll event dispatcher (you must call it before running the QxOrm HTTP web server) : +

    + + + + + + + +
    +
       httpServer.setEventDispatcher(new QEventDispatcherEpoll());   
    +
    +

    +
    +
    +
    + +

    JSON REST API (QxRestApi + module)

    +
    + QxRestApi module is a JSON API to manage (in a generic way) your persistent data layer + (database) or call C++ native functions (registered in QxOrm context). + QxRestApi module is based on a request/response mechanism : send a request in JSON format + and receive a response in JSON format. + QxRestApi module can be used for example to develop REST + services. +

    + QxRestApi module supports following features : +
      +
    • CRUD operations ;
    • +
    • complex queries with several levels of relationships ;
    • +
    • custom JSON output format ;
    • +
    • call dynamically C++ native functions registered in QxOrm context ;
    • +
    • instance validation ;
    • +
    • call custom database queries or stored procedures.
    • +
    +
    +

    How it works

    +
    + QxRestApi module is very easy to use : qx::QxRestApi + class has only 1 main method : processRequest().
    + Prerequisites : all classes registered into QxOrm context must implement qx::IxPersistable + interface. +

    + A JSON query contains following properties : +

    +
    +
    {
    +   "request_id" : // [optional] unique identifier generated by client to associate response to request (if provided by caller, then the response will contain the same unique identifier)
    +   "action" : // [required] what is the action to execute on the server
    +   "entity" : // [optional or required depending on action] C++ class registered in QxOrm context
    +   "data" : // [optional or required depending on action] data in JSON format needed to execute action
    +   "columns" : // [optional] list of columns to fetch or update (if empty, means all columns)
    +   "relations" : // [optional] list of relationships to fetch or save
    +   "query" : // [optional or required depending on action] query to execute on database
    +   "output_format" : // [optional] output fields for the response (filter), if empty then response will contain all fields
    +   "fct" : // [required only with action 'call_entity_function'] used to call C++ native functions
    +   "save_mode" : // [optional] used only with action 'save' to define insert or update or check both insert/update
    +}
    +
    +

    + A JSON response contains following properties : +

    +
    +
    {
    +   "request_id" : // unique identifier generated by client's request (if any)
    +   "data" : // contain the response data
    +   "error" : // if an error occured, then contain a code and description of the error
    +}
    +
    +

    +

    Use cases

    +
    + Several programming languages support natively JSON format (Javascript, PHP, Python, etc...). + QxRestApi module provides an interoperability between QxOrm library and any other + applications developed with another technology (not C++/Qt for example). +

    + QxRestApi module can be useful for : + +
    +
    +
    +

    qxBlogRestApi example + project (QML and HTTP web server)

    +
    + QxOrm package contains a test project named qxBlogRestApi (in + ./test/qxBlogRestApi/ directory).
    + This test project shows 2 ways to work with QxRestApi module : +
      +
    • The first screen is a QML application which + uses embedded QML Javascript engine to request a persistent data layer or call C++ native + functions : +

      QxHttpServer performance


      +
    • +
    • The second screen runs an HTTP web server + based on QxHttpServer module, then opens default web browser (to + load HTML + Javascript with jQuery) : +

      QxHttpServer performance
      +
    • +
    +
    + These 2 windows are developed with different programming languages (QML versus HTML + + Javascript), but provide exactly same features : +
      +
    • On top-left position : a text area to write a JSON request to send to QxRestApi + module ;
    • +
    • Just below the JSON request text area : a button to send JSON request to QxRestApi + module ;
    • +
    • On bottom-left position : a list of pre-loaded JSON requests examples (a click on this + list fills automatically the JSON request text area) ;
    • +
    • On right side : a JSON response text area received from QxRestApi module (after + executing a JSON request).
    • +
    +
    +
    +

    Fetch

    +
    + This chapter provides several ways to get data from database (fetch) : + +
    +

    fetch_all

    +
    + The fetch_all action gets all items from a table in database (and eventually several levels of relationships). +

    + -- Example n�1 -- fetch all blogs (as + list format) : +

    + JSON request :
    +
    +
    {
    +   "request_id": "5e988bac-c812-4cb1-b0d8-6a2c9dc4478b",
    +   "action": "fetch_all",
    +   "entity": "blog"
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 1,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 2,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 3,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 4,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [],
    +         "list_comment": []
    +      }
    +   ],
    +   "request_id": "5e988bac-c812-4cb1-b0d8-6a2c9dc4478b"
    +}
    +
    +

    + -- Example n�2 -- fetch all blogs (as + hash-map with key/value format) : +

    + JSON request :
    +
    +
    {
    +   "request_id": "ad400135-19fd-40e0-8034-201be6a2ff7a",
    +   "action": "fetch_all",
    +   "entity": "blog",
    +   "data": [
    +      {
    +         "key": "",
    +         "value": ""
    +      }
    +   ]
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": [
    +      {
    +         "key": 1,
    +         "value": {
    +            "author_id": {
    +               "author_id": "author_id_2",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 1,
    +            "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +            "date_creation": "2019-04-01T16:18:54",
    +            "list_category": [],
    +            "list_comment": []
    +         }
    +      },
    +      {
    +         "key": 2,
    +         "value": {
    +            "author_id": {
    +               "author_id": "author_id_2",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 2,
    +            "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +            "date_creation": "2019-04-01T16:18:54",
    +            "list_category": [],
    +            "list_comment": []
    +         }
    +      },
    +      {
    +         "key": 3,
    +         "value": {
    +            "author_id": {
    +               "author_id": "author_id_2",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 3,
    +            "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +            "date_creation": "2019-04-01T16:18:54",
    +            "list_category": [],
    +            "list_comment": []
    +         }
    +      },
    +      {
    +         "key": 4,
    +         "value": {
    +            "author_id": {
    +               "author_id": "author_id_2",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 4,
    +            "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +            "date_creation": "2019-04-01T16:18:54",
    +            "list_category": [],
    +            "list_comment": []
    +         }
    +      }
    +   ],
    +   "request_id": "ad400135-19fd-40e0-8034-201be6a2ff7a"
    +}
    +
    +

    + -- Example n�3 -- fetch all blogs and 2 levels of relationships : +

    + JSON request :
    +
    +
    {
    +   "request_id": "cf9ea2a8-3e41-438f-9a48-bbc8593d2b99",
    +   "action": "fetch_all",
    +   "entity": "blog",
    +   "relations": [
    +      "*->*"
    +   ]
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "list_blog": [
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 2,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 3,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 4,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               }
    +            ],
    +            "name": "author name modified at index 1 => container is dirty !!!",
    +            "sex": 1
    +         },
    +         "blog_id": 1,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [
    +            {
    +               "key": 1,
    +               "value": {
    +                  "category_id": 1,
    +                  "description": "desc_1",
    +                  "list_blog": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "author_id": {
    +                              "author_id": "author_id_2",
    +                              "birthdate": null,
    +                              "list_blog": [],
    +                              "name": "",
    +                              "sex": 2
    +                           },
    +                           "blog_id": 1,
    +                           "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                           "date_creation": "2019-04-01T16:18:54",
    +                           "list_category": [],
    +                           "list_comment": []
    +                        }
    +                     }
    +                  ],
    +                  "name": "category_1"
    +               }
    +            },
    +            {
    +               "key": 3,
    +               "value": {
    +                  "category_id": 3,
    +                  "description": "desc_3",
    +                  "list_blog": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "author_id": {
    +                              "author_id": "author_id_2",
    +                              "birthdate": null,
    +                              "list_blog": [],
    +                              "name": "",
    +                              "sex": 2
    +                           },
    +                           "blog_id": 1,
    +                           "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                           "date_creation": "2019-04-01T16:18:54",
    +                           "list_category": [],
    +                           "list_comment": []
    +                        }
    +                     }
    +                  ],
    +                  "name": "category_3"
    +               }
    +            }
    +         ],
    +         "list_comment": [
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               "comment_id": 1,
    +               "comment_text": "comment_1 text",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               "comment_id": 3,
    +               "comment_text": "comment_1 text",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               "comment_id": 5,
    +               "comment_text": "comment_1 text",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               "comment_id": 7,
    +               "comment_text": "comment_1 text",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               "comment_id": 2,
    +               "comment_text": "comment_2 text",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               "comment_id": 4,
    +               "comment_text": "comment_2 text",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               "comment_id": 6,
    +               "comment_text": "comment_2 text",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               "comment_id": 8,
    +               "comment_text": "comment_2 text",
    +               "date_creation": "2019-04-01T16:18:54"
    +            }
    +         ]
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "list_blog": [
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 2,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 3,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 4,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               }
    +            ],
    +            "name": "author name modified at index 1 => container is dirty !!!",
    +            "sex": 1
    +         },
    +         "blog_id": 2,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [
    +            {
    +               "key": 4,
    +               "value": {
    +                  "category_id": 4,
    +                  "description": "desc_1",
    +                  "list_blog": [
    +                     {
    +                        "key": 2,
    +                        "value": {
    +                           "author_id": {
    +                              "author_id": "author_id_2",
    +                              "birthdate": null,
    +                              "list_blog": [],
    +                              "name": "",
    +                              "sex": 2
    +                           },
    +                           "blog_id": 2,
    +                           "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                           "date_creation": "2019-04-01T16:18:54",
    +                           "list_category": [],
    +                           "list_comment": []
    +                        }
    +                     }
    +                  ],
    +                  "name": "category_1"
    +               }
    +            },
    +            {
    +               "key": 5,
    +               "value": {
    +                  "category_id": 5,
    +                  "description": "desc_3",
    +                  "list_blog": [
    +                     {
    +                        "key": 2,
    +                        "value": {
    +                           "author_id": {
    +                              "author_id": "author_id_2",
    +                              "birthdate": null,
    +                              "list_blog": [],
    +                              "name": "",
    +                              "sex": 2
    +                           },
    +                           "blog_id": 2,
    +                           "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                           "date_creation": "2019-04-01T16:18:54",
    +                           "list_category": [],
    +                           "list_comment": []
    +                        }
    +                     }
    +                  ],
    +                  "name": "category_3"
    +               }
    +            }
    +         ],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "list_blog": [
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 2,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 3,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 4,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               }
    +            ],
    +            "name": "author name modified at index 1 => container is dirty !!!",
    +            "sex": 1
    +         },
    +         "blog_id": 3,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [
    +            {
    +               "key": 6,
    +               "value": {
    +                  "category_id": 6,
    +                  "description": "desc_1",
    +                  "list_blog": [
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "author_id": {
    +                              "author_id": "author_id_2",
    +                              "birthdate": null,
    +                              "list_blog": [],
    +                              "name": "",
    +                              "sex": 2
    +                           },
    +                           "blog_id": 3,
    +                           "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                           "date_creation": "2019-04-01T16:18:54",
    +                           "list_category": [],
    +                           "list_comment": []
    +                        }
    +                     }
    +                  ],
    +                  "name": "category_1"
    +               }
    +            },
    +            {
    +               "key": 7,
    +               "value": {
    +                  "category_id": 7,
    +                  "description": "desc_3",
    +                  "list_blog": [
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "author_id": {
    +                              "author_id": "author_id_2",
    +                              "birthdate": null,
    +                              "list_blog": [],
    +                              "name": "",
    +                              "sex": 2
    +                           },
    +                           "blog_id": 3,
    +                           "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                           "date_creation": "2019-04-01T16:18:54",
    +                           "list_category": [],
    +                           "list_comment": []
    +                        }
    +                     }
    +                  ],
    +                  "name": "category_3"
    +               }
    +            }
    +         ],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "list_blog": [
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 2,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 3,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 4,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               }
    +            ],
    +            "name": "author name modified at index 1 => container is dirty !!!",
    +            "sex": 1
    +         },
    +         "blog_id": 4,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [
    +            {
    +               "key": 8,
    +               "value": {
    +                  "category_id": 8,
    +                  "description": "desc_1",
    +                  "list_blog": [
    +                     {
    +                        "key": 4,
    +                        "value": {
    +                           "author_id": {
    +                              "author_id": "author_id_2",
    +                              "birthdate": null,
    +                              "list_blog": [],
    +                              "name": "",
    +                              "sex": 2
    +                           },
    +                           "blog_id": 4,
    +                           "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                           "date_creation": "2019-04-01T16:18:54",
    +                           "list_category": [],
    +                           "list_comment": []
    +                        }
    +                     }
    +                  ],
    +                  "name": "category_1"
    +               }
    +            },
    +            {
    +               "key": 9,
    +               "value": {
    +                  "category_id": 9,
    +                  "description": "desc_3",
    +                  "list_blog": [
    +                     {
    +                        "key": 4,
    +                        "value": {
    +                           "author_id": {
    +                              "author_id": "author_id_2",
    +                              "birthdate": null,
    +                              "list_blog": [],
    +                              "name": "",
    +                              "sex": 2
    +                           },
    +                           "blog_id": 4,
    +                           "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                           "date_creation": "2019-04-01T16:18:54",
    +                           "list_category": [],
    +                           "list_comment": []
    +                        }
    +                     }
    +                  ],
    +                  "name": "category_3"
    +               }
    +            }
    +         ],
    +         "list_comment": []
    +      }
    +   ],
    +   "request_id": "cf9ea2a8-3e41-438f-9a48-bbc8593d2b99"
    +}
    +
    +

    + -- Example n�4 -- fetch all blogs and some relationships + define an output JSON format (all properties + will not be exported to JSON response) : +

    + JSON request :
    +
    +
    {
    +   "request_id": "4c45fdf9-8001-4509-bb4b-ce27a4a8708a",
    +   "action": "fetch_all",
    +   "entity": "blog",
    +   "relations": [
    +      "<blog_alias> { blog_text }",
    +      "author_id <author_alias> { name, birthdate }",
    +      "list_comment <list_comment_alias> { comment_text } -> blog_id <blog_alias_2> -> * <..._my_alias_suffix>"
    +   ],
    +   "output_format": [
    +      "{ blog_text }",
    +      "author_id { name, birthdate }",
    +      "list_comment { comment_text } -> blog_id -> *"
    +   ]
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "name": "author name modified at index 1 => container is dirty !!!"
    +         },
    +         "blog_id": 1,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "list_comment": [
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": "2019-04-01",
    +                     "name": "author name modified at index 1 => container is dirty !!!",
    +                     "sex": 1
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "category_id": 1,
    +                           "description": "desc_1",
    +                           "name": "category_1"
    +                        }
    +                     },
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "category_id": 3,
    +                           "description": "desc_3",
    +                           "name": "category_3"
    +                        }
    +                     }
    +                  ],
    +                  "list_comment": [
    +                     {
    +                        "comment_id": 1,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 3,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 5,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 7,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 2,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 4,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 6,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 8,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     }
    +                  ]
    +               },
    +               "comment_id": 1,
    +               "comment_text": "comment_1 text"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": "2019-04-01",
    +                     "name": "author name modified at index 1 => container is dirty !!!",
    +                     "sex": 1
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "category_id": 1,
    +                           "description": "desc_1",
    +                           "name": "category_1"
    +                        }
    +                     },
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "category_id": 3,
    +                           "description": "desc_3",
    +                           "name": "category_3"
    +                        }
    +                     }
    +                  ],
    +                  "list_comment": [
    +                     {
    +                        "comment_id": 1,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 3,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 5,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 7,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 2,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 4,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 6,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 8,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     }
    +                  ]
    +               },
    +               "comment_id": 3,
    +               "comment_text": "comment_1 text"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": "2019-04-01",
    +                     "name": "author name modified at index 1 => container is dirty !!!",
    +                     "sex": 1
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "category_id": 1,
    +                           "description": "desc_1",
    +                           "name": "category_1"
    +                        }
    +                     },
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "category_id": 3,
    +                           "description": "desc_3",
    +                           "name": "category_3"
    +                        }
    +                     }
    +                  ],
    +                  "list_comment": [
    +                     {
    +                        "comment_id": 1,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 3,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 5,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 7,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 2,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 4,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 6,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 8,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     }
    +                  ]
    +               },
    +               "comment_id": 5,
    +               "comment_text": "comment_1 text"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": "2019-04-01",
    +                     "name": "author name modified at index 1 => container is dirty !!!",
    +                     "sex": 1
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "category_id": 1,
    +                           "description": "desc_1",
    +                           "name": "category_1"
    +                        }
    +                     },
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "category_id": 3,
    +                           "description": "desc_3",
    +                           "name": "category_3"
    +                        }
    +                     }
    +                  ],
    +                  "list_comment": [
    +                     {
    +                        "comment_id": 1,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 3,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 5,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 7,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 2,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 4,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 6,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 8,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     }
    +                  ]
    +               },
    +               "comment_id": 7,
    +               "comment_text": "comment_1 text"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": "2019-04-01",
    +                     "name": "author name modified at index 1 => container is dirty !!!",
    +                     "sex": 1
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "category_id": 1,
    +                           "description": "desc_1",
    +                           "name": "category_1"
    +                        }
    +                     },
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "category_id": 3,
    +                           "description": "desc_3",
    +                           "name": "category_3"
    +                        }
    +                     }
    +                  ],
    +                  "list_comment": [
    +                     {
    +                        "comment_id": 1,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 3,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 5,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 7,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 2,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 4,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 6,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 8,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     }
    +                  ]
    +               },
    +               "comment_id": 2,
    +               "comment_text": "comment_2 text"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": "2019-04-01",
    +                     "name": "author name modified at index 1 => container is dirty !!!",
    +                     "sex": 1
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "category_id": 1,
    +                           "description": "desc_1",
    +                           "name": "category_1"
    +                        }
    +                     },
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "category_id": 3,
    +                           "description": "desc_3",
    +                           "name": "category_3"
    +                        }
    +                     }
    +                  ],
    +                  "list_comment": [
    +                     {
    +                        "comment_id": 1,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 3,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 5,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 7,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 2,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 4,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 6,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 8,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     }
    +                  ]
    +               },
    +               "comment_id": 4,
    +               "comment_text": "comment_2 text"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": "2019-04-01",
    +                     "name": "author name modified at index 1 => container is dirty !!!",
    +                     "sex": 1
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "category_id": 1,
    +                           "description": "desc_1",
    +                           "name": "category_1"
    +                        }
    +                     },
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "category_id": 3,
    +                           "description": "desc_3",
    +                           "name": "category_3"
    +                        }
    +                     }
    +                  ],
    +                  "list_comment": [
    +                     {
    +                        "comment_id": 1,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 3,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 5,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 7,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 2,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 4,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 6,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 8,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     }
    +                  ]
    +               },
    +               "comment_id": 6,
    +               "comment_text": "comment_2 text"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": "2019-04-01",
    +                     "name": "author name modified at index 1 => container is dirty !!!",
    +                     "sex": 1
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "category_id": 1,
    +                           "description": "desc_1",
    +                           "name": "category_1"
    +                        }
    +                     },
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "category_id": 3,
    +                           "description": "desc_3",
    +                           "name": "category_3"
    +                        }
    +                     }
    +                  ],
    +                  "list_comment": [
    +                     {
    +                        "comment_id": 1,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 3,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 5,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 7,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 2,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 4,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 6,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 8,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     }
    +                  ]
    +               },
    +               "comment_id": 8,
    +               "comment_text": "comment_2 text"
    +            }
    +         ]
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "name": "author name modified at index 1 => container is dirty !!!"
    +         },
    +         "blog_id": 2,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "name": "author name modified at index 1 => container is dirty !!!"
    +         },
    +         "blog_id": 3,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "name": "author name modified at index 1 => container is dirty !!!"
    +         },
    +         "blog_id": 4,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "list_comment": []
    +      }
    +   ],
    +   "request_id": "4c45fdf9-8001-4509-bb4b-ce27a4a8708a"
    +}
    +
    +

    +
    +

    fetch_by_id

    +
    + The fetch_by_id action gets an item from a table based on its unique + identifier. +

    + -- Example n�1 -- fetch a blog which has + an unique identifier equals to 1 : +

    + JSON request :
    +
    +
    {
    +   "request_id": "4d6fbb9e-e088-482a-abfa-4e7ddee80569",
    +   "action": "fetch_by_id",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 1
    +   }
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "author_id": {
    +         "author_id": "author_id_2",
    +         "birthdate": null,
    +         "list_blog": [],
    +         "name": "",
    +         "sex": 2
    +      },
    +      "blog_id": 1,
    +      "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +      "date_creation": "2019-04-01T16:18:54",
    +      "list_category": [],
    +      "list_comment": []
    +   },
    +   "request_id": "4d6fbb9e-e088-482a-abfa-4e7ddee80569"
    +}
    +
    +

    + -- Example n�2 -- fetch only some blog's + columns which has an unique identifier equals to 1 (other columns are part of JSON response + but with an empty or null value) : +

    + JSON request :
    +
    +
    {
    +   "request_id": "72c9b362-d194-410e-98ed-23797a34318e",
    +   "action": "fetch_by_id",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 1
    +   },
    +   "columns": [
    +      "blog_text",
    +      "date_creation"
    +   ]
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "author_id": null,
    +      "blog_id": 1,
    +      "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +      "date_creation": "2019-04-01T16:18:54",
    +      "list_category": [],
    +      "list_comment": []
    +   },
    +   "request_id": "72c9b362-d194-410e-98ed-23797a34318e"
    +}
    +
    +

    + -- Example n�3 -- fetch a list of blogs + based on their unique identifier : +

    + JSON request :
    +
    +
    {
    +   "request_id": "59c37f70-26ee-42e5-9177-b32c331adce1",
    +   "action": "fetch_by_id",
    +   "entity": "blog",
    +   "data": [
    +      {
    +         "blog_id": 1
    +      },
    +      {
    +         "blog_id": 2
    +      },
    +      {
    +         "blog_id": 3
    +      }
    +   ]
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 1,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 2,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 3,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [],
    +         "list_comment": []
    +      }
    +   ],
    +   "request_id": "59c37f70-26ee-42e5-9177-b32c331adce1"
    +}
    +
    +

    + -- Example n�4 -- fetch a list of blogs + (with some relationships) based on their unique identifier, and define a JSON output format + (all properties will not be exported to JSON response) : +

    + JSON request :
    +
    +
    {
    +   "request_id": "325d64f4-29ac-47ab-9846-d6a71a9e9d73",
    +   "action": "fetch_by_id",
    +   "entity": "blog",
    +   "data": [
    +      {
    +         "blog_id": 1
    +      },
    +      {
    +         "blog_id": 2
    +      }
    +   ],
    +   "relations": [
    +      "{ blog_text }",
    +      "author_id <author_alias> { name, birthdate }",
    +      "list_comment <list_comment_alias> { comment_text }"
    +   ],
    +   "output_format": [
    +      "{ blog_text }",
    +      "author_id { name, birthdate }",
    +      "list_comment { comment_text }"
    +   ]
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "name": "author name modified at index 1 => container is dirty !!!"
    +         },
    +         "blog_id": 1,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "list_comment": [
    +            {
    +               "comment_id": 1,
    +               "comment_text": "comment_1 text"
    +            },
    +            {
    +               "comment_id": 2,
    +               "comment_text": "comment_2 text"
    +            },
    +            {
    +               "comment_id": 3,
    +               "comment_text": "comment_1 text"
    +            },
    +            {
    +               "comment_id": 4,
    +               "comment_text": "comment_2 text"
    +            },
    +            {
    +               "comment_id": 5,
    +               "comment_text": "comment_1 text"
    +            },
    +            {
    +               "comment_id": 6,
    +               "comment_text": "comment_2 text"
    +            },
    +            {
    +               "comment_id": 7,
    +               "comment_text": "comment_1 text"
    +            },
    +            {
    +               "comment_id": 8,
    +               "comment_text": "comment_2 text"
    +            }
    +         ]
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "name": "author name modified at index 1 => container is dirty !!!"
    +         },
    +         "blog_id": 2,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "list_comment": []
    +      }
    +   ],
    +   "request_id": "325d64f4-29ac-47ab-9846-d6a71a9e9d73"
    +}
    +
    +

    +
    +

    fetch_by_query +

    +
    + The fetch_by_query action gets some items from a table filtered by a query. +

    + -- Example n�1 -- fetch only items from + author table with a sex of type female (female == enum equals to 1) : +

    + JSON request :
    +
    +
    {
    +   "request_id": "c178194c-a76f-4a77-af12-2b97fc7078e4",
    +   "action": "fetch_by_query",
    +   "entity": "author",
    +   "query": {
    +      "sql": "WHERE author.sex = :sex",
    +      "params": [
    +         {
    +            "key": ":sex",
    +            "value": 1
    +         }
    +      ]
    +   }
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": "author_id_2",
    +         "birthdate": "2019-04-01",
    +         "list_blog": [],
    +         "name": "author name modified at index 1 => container is dirty !!!",
    +         "sex": 1
    +      },
    +      {
    +         "author_id": "author_id_3",
    +         "birthdate": "1998-03-06",
    +         "list_blog": [],
    +         "name": "author_3",
    +         "sex": 1
    +      }
    +   ],
    +   "request_id": "c178194c-a76f-4a77-af12-2b97fc7078e4"
    +}
    +
    +

    + -- Example n�2 -- fetch some items from + author table (and all relationships) with a sex of type + female : +

    + JSON request :
    +
    +
    {
    +   "request_id": "84e2e13a-0bf9-4d78-b655-970568a97e4c",
    +   "action": "fetch_by_query",
    +   "entity": "author",
    +   "query": {
    +      "sql": "WHERE author.sex = :sex",
    +      "params": [
    +         {
    +            "key": ":sex",
    +            "value": 1,
    +            "type": "in"
    +         }
    +      ]
    +   },
    +   "relations": [
    +      "*"
    +   ]
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": "author_id_2",
    +         "birthdate": "2019-04-01",
    +         "list_blog": [
    +            {
    +               "author_id": {
    +                  "author_id": "author_id_2",
    +                  "birthdate": null,
    +                  "list_blog": [],
    +                  "name": "",
    +                  "sex": 2
    +               },
    +               "blog_id": 1,
    +               "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +               "date_creation": "2019-04-01T16:18:54",
    +               "list_category": [],
    +               "list_comment": []
    +            },
    +            {
    +               "author_id": {
    +                  "author_id": "author_id_2",
    +                  "birthdate": null,
    +                  "list_blog": [],
    +                  "name": "",
    +                  "sex": 2
    +               },
    +               "blog_id": 2,
    +               "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +               "date_creation": "2019-04-01T16:18:54",
    +               "list_category": [],
    +               "list_comment": []
    +            },
    +            {
    +               "author_id": {
    +                  "author_id": "author_id_2",
    +                  "birthdate": null,
    +                  "list_blog": [],
    +                  "name": "",
    +                  "sex": 2
    +               },
    +               "blog_id": 3,
    +               "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +               "date_creation": "2019-04-01T16:18:54",
    +               "list_category": [],
    +               "list_comment": []
    +            },
    +            {
    +               "author_id": {
    +                  "author_id": "author_id_2",
    +                  "birthdate": null,
    +                  "list_blog": [],
    +                  "name": "",
    +                  "sex": 2
    +               },
    +               "blog_id": 4,
    +               "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +               "date_creation": "2019-04-01T16:18:54",
    +               "list_category": [],
    +               "list_comment": []
    +            }
    +         ],
    +         "name": "author name modified at index 1 => container is dirty !!!",
    +         "sex": 1
    +      },
    +      {
    +         "author_id": "author_id_3",
    +         "birthdate": "1998-03-06",
    +         "list_blog": [],
    +         "name": "author_3",
    +         "sex": 1
    +      }
    +   ],
    +   "request_id": "84e2e13a-0bf9-4d78-b655-970568a97e4c"
    +}
    +
    +

    + -- Example n�3 -- fetch some items from + author table (and all relationships) with a sex of type + female, and define a JSON output format (all properties will not be exported to JSON + response) : +

    + JSON request :
    +
    +
    {
    +   "request_id": "c18b59e7-54f9-4a4f-843d-f0797f4fb676",
    +   "action": "fetch_by_query",
    +   "entity": "author",
    +   "query": {
    +      "sql": "WHERE author.sex = :sex",
    +      "params": [
    +         {
    +            "key": ":sex",
    +            "value": 1,
    +            "type": "in"
    +         }
    +      ]
    +   },
    +   "relations": [
    +      "*"
    +   ],
    +   "output_format": [
    +      "{ birthdate, name }",
    +      "list_blog { blog_text, date_creation }"
    +   ]
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": "author_id_2",
    +         "birthdate": "2019-04-01",
    +         "list_blog": [
    +            {
    +               "blog_id": 1,
    +               "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": 2,
    +               "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": 3,
    +               "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": 4,
    +               "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +               "date_creation": "2019-04-01T16:18:54"
    +            }
    +         ],
    +         "name": "author name modified at index 1 => container is dirty !!!"
    +      },
    +      {
    +         "author_id": "author_id_3",
    +         "birthdate": "1998-03-06",
    +         "list_blog": [],
    +         "name": "author_3"
    +      }
    +   ],
    +   "request_id": "c18b59e7-54f9-4a4f-843d-f0797f4fb676"
    +}
    +
    +

    +
    +

    count

    +
    + The count action returns a number of items from a table in database with or + without a query (and with or without relationships). +

    + -- Example n�1 -- count all blogs stored + in database : +

    + JSON request :
    +
    +
    {
    +   "request_id": "1ef62fd7-d847-4d67-9fd0-0207af463aa4",
    +   "action": "count",
    +   "entity": "blog"
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "count": 4
    +   },
    +   "request_id": "1ef62fd7-d847-4d67-9fd0-0207af463aa4"
    +}
    +
    +

    + -- Example n�2 -- count items from + author table with a sex of type female : +

    + JSON request :
    +
    +
    {
    +   "request_id": "a80646d1-5a42-46fb-9306-3b91c7f594c8",
    +   "action": "count",
    +   "entity": "author",
    +   "query": {
    +      "sql": "WHERE author.sex = :sex",
    +      "params": [
    +         {
    +            "key": ":sex",
    +            "value": 1
    +         }
    +      ]
    +   }
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "count": 2
    +   },
    +   "request_id": "a80646d1-5a42-46fb-9306-3b91c7f594c8"
    +}
    +
    +

    + -- Example n�3 -- count all blogs + associated to an author with a sex of type female : +

    + JSON request :
    +
    +
    {
    +   "request_id": "6ef252f7-385c-465e-8304-b9afa9fea490",
    +   "action": "count",
    +   "entity": "blog",
    +   "query": {
    +      "sql": "WHERE author_alias.sex = :sex",
    +      "params": [
    +         {
    +            "key": ":sex",
    +            "value": 1
    +         }
    +      ]
    +   },
    +   "relations": [
    +      "author_id <author_alias> { sex }"
    +   ]
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "count": 4
    +   },
    +   "request_id": "6ef252f7-385c-465e-8304-b9afa9fea490"
    +}
    +
    +

    +
    +

    exist

    +
    + The exist action checks if an item from a table exists in database based on its + unique identifier. +

    + -- Example n�1 -- check if a blog with + unique identifier equals to 1 exists in database : +

    + JSON request :
    +
    +
    {
    +   "request_id": "e8db33db-b249-4349-93fe-ad12e208520e",
    +   "action": "exist",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 1
    +   }
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "exist": true
    +   },
    +   "request_id": "e8db33db-b249-4349-93fe-ad12e208520e"
    +}
    +
    +

    + -- Example n�2 -- check if several blogs + exist (based on their unique identifier) : +

    + JSON request :
    +
    +
    {
    +   "request_id": "f2d6ca3f-36de-4920-8f4c-c04842603467",
    +   "action": "exist",
    +   "entity": "blog",
    +   "data": [
    +      {
    +         "blog_id": 1
    +      },
    +      {
    +         "blog_id": 999
    +      },
    +      {
    +         "blog_id": 3
    +      }
    +   ]
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "exist": false
    +   },
    +   "request_id": "f2d6ca3f-36de-4920-8f4c-c04842603467"
    +}
    +
    +

    + -- Example n�3 -- check if an + author exists : +

    + JSON request :
    +
    +
    {
    +   "request_id": "2c7df172-8010-4816-b8e1-3edbb0b0b90e",
    +   "action": "exist",
    +   "entity": "author",
    +   "data": {
    +      "author_id": "author_id_2"
    +   }
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "exist": true
    +   },
    +   "request_id": "2c7df172-8010-4816-b8e1-3edbb0b0b90e"
    +}
    +
    +

    +
    +
    +

    Insert

    +
    + The insert action adds 1 or several items in database. + Unique identifiers generated by database (for example auto-incremented identifiers) are provided + in JSON response. +

    + -- Example n�1 -- insert a blog in database + : +

    + JSON request :
    +
    +
    {
    +   "request_id": "573e4940-607a-4037-8a09-11ec52deb21c",
    +   "action": "insert",
    +   "entity": "blog",
    +   "data": {
    +      "blog_text": "this is a new blog from QxOrm REST API !",
    +      "date_creation": "2018-01-30T12:42:01",
    +      "author_id": "author_id_2"
    +   }
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "blog_id": 5
    +   },
    +   "request_id": "573e4940-607a-4037-8a09-11ec52deb21c"
    +}
    +
    +

    + -- Example n�2 -- insert a list of blogs in + database : +

    + JSON request :
    +
    +
    {
    +   "request_id": "6ade2d01-086c-45d6-971b-b65e8836475f",
    +   "action": "insert",
    +   "entity": "blog",
    +   "data": [
    +      {
    +         "blog_text": "new blog from QxOrm REST API !",
    +         "date_creation": "2018-01-30T12:42:01",
    +         "author_id": "author_id_2"
    +      },
    +      {
    +         "blog_text": "another blog from QxOrm REST API !",
    +         "date_creation": "2016-06-12T08:33:12",
    +         "author_id": "author_id_1"
    +      }
    +   ]
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": [
    +      {
    +         "blog_id": 6
    +      },
    +      {
    +         "blog_id": 7
    +      }
    +   ],
    +   "request_id": "6ade2d01-086c-45d6-971b-b65e8836475f"
    +}
    +
    +

    + -- Example n�3 -- insert an author in + database : +

    + JSON request :
    +
    +
    {
    +   "request_id": "0cffa916-99f4-4395-bccd-02918a4b3c57",
    +   "action": "insert",
    +   "entity": "author",
    +   "data": {
    +      "author_id": "author_id_from_rest_api",
    +      "birthdate": "1978-05-11",
    +      "name": "new author created by QxOrm REST API",
    +      "sex": 1
    +   }
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "author_id": "author_id_from_rest_api"
    +   },
    +   "request_id": "0cffa916-99f4-4395-bccd-02918a4b3c57"
    +}
    +
    +
    + Note : the author table requires an unique identifier filled by caller (because + it's not auto-incremented). + If we execute the same JSON request a second time, then we have following error : +
    +
    +
    {
    +   "error": {
    +      "code": 19,
    +      "desc": "Unable to fetch row\ncolumn author_id is not unique"
    +   },
    +   "request_id": "0cffa916-99f4-4395-bccd-02918a4b3c57"
    +}
    +
    +

    +
    +

    Update

    +
    + The update action modifies 1 or several items in database. +

    + -- Example n�1 -- update a blog with unique + identifier equals to 1 : +

    + JSON request :
    +
    +
    {
    +   "request_id": "4fa24a7f-a3d8-4bbf-85c1-c86df83dec0b",
    +   "action": "update",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 1,
    +      "blog_text": "modify blog from QxOrm REST API",
    +      "date_creation": "2013-11-25T09:56:33",
    +      "author_id": "author_id_1"
    +   }
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "blog_id": 1
    +   },
    +   "request_id": "4fa24a7f-a3d8-4bbf-85c1-c86df83dec0b"
    +}
    +
    +

    + -- Example n�2 -- update only some columns + of a blog : +

    + JSON request :
    +
    +
    {
    +   "request_id": "d0704db1-5c3a-48ad-b27e-14aa54ac0efb",
    +   "action": "update",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 2,
    +      "blog_text": "modify blog from QxOrm REST API",
    +      "date_creation": "2013-11-25T09:56:33"
    +   },
    +   "columns": [
    +      "blog_text",
    +      "date_creation"
    +   ]
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "blog_id": 2
    +   },
    +   "request_id": "d0704db1-5c3a-48ad-b27e-14aa54ac0efb"
    +}
    +
    +

    + -- Example n�3 -- update a list of + author : +

    + JSON request :
    +
    +
    {
    +   "request_id": "26ec3a7b-cf2d-47f7-bab7-db303f15ee51",
    +   "action": "update",
    +   "entity": "author",
    +   "data": [
    +      {
    +         "author_id": "author_id_from_rest_api",
    +         "birthdate": "1992-11-03",
    +         "name": "modify author from QxOrm REST API",
    +         "sex": 0
    +      },
    +      {
    +         "author_id": "author_id_1",
    +         "birthdate": "1978-12-25",
    +         "name": "modify another author from QxOrm REST API",
    +         "sex": 2
    +      }
    +   ]
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": "author_id_from_rest_api"
    +      },
    +      {
    +         "author_id": "author_id_1"
    +      }
    +   ],
    +   "request_id": "26ec3a7b-cf2d-47f7-bab7-db303f15ee51"
    +}
    +
    +

    +
    +

    Save (insert or + update)

    +
    + The save action adds or modifies (insert or update) 1 or several + items in database. + When inserting, unique identifiers generated by database (for example auto-incremented + identifiers) are provided in JSON response. +

    + JSON request has an optional parameter named save_mode which can have following values : +
      +
    • check_insert_or_update : save the instance and its relationships recursively + (several levels of relations) checking for each relation if an insert or + update is required (can be slow with a lot of relations) ; +
    • +
    • insert_only : insert recursively (several levels of relations) the instance and + its relationships ;
    • +
    • update_only : update recursively (several levels of relations) the instance and + its relationships.
    • +
    +
    + -- Example n�1 -- save (insert or update + based on the unique identifier) a blog in database : +

    + JSON request :
    +
    +
    {
    +   "request_id": "ec3c71eb-5014-4b36-85a0-aeb7ae48a5e9",
    +   "action": "save",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 1,
    +      "blog_text": "modify blog from QxOrm REST API",
    +      "date_creation": "2013-11-25T09:56:33",
    +      "author_id": "author_id_1"
    +   }
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "blog_id": 1
    +   },
    +   "request_id": "ec3c71eb-5014-4b36-85a0-aeb7ae48a5e9"
    +}
    +
    +

    + -- Example n�2 -- save (insert or update + based on the unique identifier) a list of blogs in database : +

    + JSON request :
    +
    +
    {
    +   "request_id": "dc7c804e-f95a-4a9b-a4e3-547adcacf090",
    +   "action": "save",
    +   "entity": "blog",
    +   "data": [
    +      {
    +         "blog_id": 1,
    +         "blog_text": "save blog from QxOrm REST API !",
    +         "date_creation": "2018-01-30T12:42:01",
    +         "author_id": "author_id_2"
    +      },
    +      {
    +         "blog_text": "save another blog from QxOrm REST API !",
    +         "date_creation": "2016-06-12T08:33:12",
    +         "author_id": "author_id_1"
    +      }
    +   ]
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": [
    +      {
    +         "blog_id": 1
    +      },
    +      {
    +         "blog_id": 5
    +      }
    +   ],
    +   "request_id": "dc7c804e-f95a-4a9b-a4e3-547adcacf090"
    +}
    +
    +

    + -- Example n�3 -- save (insert or update + based on the unique identifier) a blog and all its relationships recursively (several levels) : +

    + JSON request :
    +
    +
    {
    +   "request_id": "5b78e468-2fa3-4aeb-82ce-4d85408f5fa7",
    +   "action": "save",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 1,
    +      "blog_text": "save recursive blog from QxOrm REST API",
    +      "date_creation": "2013-11-25T09:56:33",
    +      "author_id": {
    +         "author_id": "author_id_1",
    +         "birthdate": "1965-07-21",
    +         "name": "save recursive author from QxOrm REST API",
    +         "sex": 0
    +      }
    +   },
    +   "save_mode": "check_insert_or_update"
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "blog_id": 1
    +   },
    +   "request_id": "5b78e468-2fa3-4aeb-82ce-4d85408f5fa7"
    +}
    +
    +

    + -- Example n�4 -- insert (save_mode = + insert_only) a blog and all its relationships recursively (several levels) : +

    + JSON request :
    +
    +
    {
    +   "request_id": "ef147c62-74e0-4be2-a294-ffeb020d5304",
    +   "action": "save",
    +   "entity": "blog",
    +   "data": {
    +      "blog_text": "save recursive - new blog from QxOrm REST API",
    +      "date_creation": "2013-11-25T09:56:33",
    +      "author_id": {
    +         "author_id": "author_id_save_recursive",
    +         "birthdate": "1965-07-21",
    +         "name": "save recursive (insert only) author from QxOrm REST API",
    +         "sex": 0
    +      }
    +   },
    +   "save_mode": "insert_only"
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "blog_id": 7
    +   },
    +   "request_id": "ef147c62-74e0-4be2-a294-ffeb020d5304"
    +}
    +
    +

    +
    +

    Delete

    +
    + This chapter provides several ways to remove data from database (delete or destroy) : + +
    + Note : difference between delete and destroy is soft + delete behaviour (logical delete). +

    +

    delete_all / + destroy_all

    +
    + The delete_all and destroy_all actions remove all items (rows) + from a table. + The difference between delete and destroy is soft delete + behaviour (logical delete). +

    + -- Example n�1 -- delete all rows from + the comment table : +

    + JSON request :
    +
    +
    {
    +   "request_id": "7b06b5c0-409f-4e0d-bfc4-acafbfe7e796",
    +   "action": "delete_all",
    +   "entity": "comment"
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "deleted": true
    +   },
    +   "request_id": "7b06b5c0-409f-4e0d-bfc4-acafbfe7e796"
    +}
    +
    +

    +
    +

    delete_by_query / + destroy_by_query

    +
    + The delete_by_query and destroy_by_query actions remove some + items (rows) from a table based on a query. + The difference between delete and destroy is soft delete + behaviour (logical delete). +

    + -- Example n�1 -- delete all rows from + the author table with a sex of type female (female = enum with value 1) + : +

    + JSON request :
    +
    +
    {
    +   "request_id": "169ff0be-6e49-457b-a99c-22bd7141dc02",
    +   "action": "delete_by_query",
    +   "entity": "author",
    +   "query": {
    +      "sql": "WHERE author.sex = :sex",
    +      "params": [
    +         {
    +            "key": ":sex",
    +            "value": 1
    +         }
    +      ]
    +   }
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "deleted": true
    +   },
    +   "request_id": "169ff0be-6e49-457b-a99c-22bd7141dc02"
    +}
    +
    +

    +
    +

    delete_by_id / + destroy_by_id

    +
    + The delete_by_id and destroy_by_id actions remove some items + (rows) from a table based on the unique identifier. + The difference between delete and destroy is soft delete + behaviour (logical delete). +

    + -- Example n�1 -- delete from database + the blog with unique identifier equals to 4 : +

    + JSON request :
    +
    +
    {
    +   "request_id": "80bff383-8ebd-4bde-bb42-37b6f67bc39f",
    +   "action": "delete_by_id",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 4
    +   }
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "blog_id": 4
    +   },
    +   "request_id": "80bff383-8ebd-4bde-bb42-37b6f67bc39f"
    +}
    +
    +

    + -- Example n�2 -- delete from database a + list of 2 blogs with unique identifier equals to 3 and 2 : +

    + JSON request :
    +
    +
    {
    +   "request_id": "38020cb7-d725-4c0e-80a0-63db7569155e",
    +   "action": "delete_by_id",
    +   "entity": "blog",
    +   "data": [
    +      {
    +         "blog_id": 3
    +      },
    +      {
    +         "blog_id": 2
    +      }
    +   ]
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": [
    +      {
    +         "blog_id": 3
    +      },
    +      {
    +         "blog_id": 2
    +      }
    +   ],
    +   "request_id": "38020cb7-d725-4c0e-80a0-63db7569155e"
    +}
    +
    +

    +
    +
    +

    Validate

    +
    + The validate action checks some properties of your instance (without triggering + any action to database). + The validate action calls the QxValidator module from + QxOrm library. +

    + -- Example n�1 -- a blog must contain + some text (blog_text property) to be saved in database. + With the following JSON request, we get a JSON response with an error message which means that + the blog instance is not valid : +

    + JSON request :
    +
    +
    {
    +   "request_id": "92043c2b-4ba8-4583-8fad-c828251734ba",
    +   "action": "validate",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 9999,
    +      "blog_text": ""
    +   }
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "invalid_values": [
    +         "blog",
    +         [
    +            {
    +               "message": "'blog_text' property cannot be empty",
    +               "path": "blog"
    +            }
    +         ]
    +      ]
    +   },
    +   "request_id": "92043c2b-4ba8-4583-8fad-c828251734ba"
    +}
    +
    +

    + -- Example n�2 -- we add a value to + blog_text property, so the blog instance becomes valid (the JSON response has an + invalid_values property with a null value) : +

    + JSON request :
    +
    +
    {
    +   "request_id": "92043c2b-4ba8-4583-8fad-c828251734ba",
    +   "action": "validate",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 9999,
    +      "blog_text": "my blog text !!!"
    +   }
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "invalid_values": null
    +   },
    +   "request_id": "92043c2b-4ba8-4583-8fad-c828251734ba"
    +}
    +
    +

    +
    +

    Custom SQL query or + stored procedure

    +
    + The call_custom_query action executes a custom SQL query or a stored procedure. +

    + -- Example n�1 -- insert in database a new + author with a custom SQL query : +

    + JSON request :
    +
    +
    {
    +   "request_id": "ff2a2256-041d-4c5f-bd86-3745ce46ead8",
    +   "action": "call_custom_query",
    +   "query": {
    +      "sql": "INSERT INTO author (author_id, name, birthdate, sex) VALUES (:author_id, :name, :birthdate, :sex)",
    +      "params": [
    +         {
    +            "key": ":author_id",
    +            "value": "author_id_custom_query"
    +         },
    +         {
    +            "key": ":name",
    +            "value": "new author inserted by custom query"
    +         },
    +         {
    +            "key": ":birthdate",
    +            "value": "20190215"
    +         },
    +         {
    +            "key": ":sex",
    +            "value": 2
    +         }
    +      ]
    +   }
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "query_output": {
    +         "distinct": false,
    +         "list_values": {
    +            ":author_id": [
    +               "author_id_custom_query",
    +               1
    +            ],
    +            ":birthdate": [
    +               "20190215",
    +               1
    +            ],
    +            ":name": [
    +               "new author inserted by custom query",
    +               1
    +            ],
    +            ":sex": [
    +               2,
    +               1
    +            ]
    +         },
    +         "parenthesis_count": 0,
    +         "query": [
    +            "INSERT INTO author (author_id, name, birthdate, sex) VALUES (:author_id, :name, :birthdate, :sex)"
    +         ],
    +         "response": "",
    +         "result_position_by_key": {},
    +         "result_values": [],
    +         "sql_element_index": 0,
    +         "sql_element_list": [],
    +         "sql_element_temp_type": 0,
    +         "type": ""
    +      }
    +   },
    +   "request_id": "ff2a2256-041d-4c5f-bd86-3745ce46ead8"
    +}
    +
    +

    +
    +

    Call C++ natives + functions

    +
    + The call_entity_function action executes C++ natives functions registered into + QxOrm context.
    + Prerequisites : the C++ native function must be a static method with this + signature : + static QJsonValue myNativeCppFct(const QJsonValue & + request); + +

    + Here is an example of C++ function registered into QxOrm context, this function can be called by + the JSON API engine (QxRestApi module) : +

    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<blog> & t)
    +{
    +   // Register 'helloWorld()' static function in QxOrm context (can be called by QxRestApi JSON API module)
    +   t.fctStatic_1<QJsonValue, const QJsonValue & >(& blog::helloWorld, "helloWorld");
    +}}
    +
    +// 'helloWorld()' static function implementation
    +QJsonValue blog::helloWorld(const QJsonValue & request)
    +{
    +   QJsonObject response;
    +   response.insert("request", request);
    +   response.insert("response", QString("Hello World !"));
    +   return response;
    +}
    +
    +

    + Here is how to execute the C++ helloWorld function from JSON API using + call_entity_function action : +

    + JSON request :
    +
    +
    {
    +   "request_id": "ab1ba7d3-9f98-4b18-a310-a9c34498d043",
    +   "action": "call_entity_function",
    +   "entity": "blog",
    +   "fct": "helloWorld",
    +   "data": {
    +      "param1": "test",
    +      "param2": "static fct call"
    +   }
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "request": {
    +         "param1": "test",
    +         "param2": "static fct call"
    +      },
    +      "response": "Hello World !"
    +   },
    +   "request_id": "ab1ba7d3-9f98-4b18-a310-a9c34498d043"
    +}
    +
    +

    +
    +

    Meta-data (C++ classes + registered into QxOrm context)

    +
    + The get_meta_data action fetches some meta-data from 1 or all entities registered + into QxOrm context (C++ classes structure with list of properties and relationships). +

    + -- Example n�1 -- get all meta-data of qxBlogRestApi example project : +

    + JSON request :
    +
    +
    {
    +   "request_id": "842ed7b5-9b94-455f-86dc-32992866b3d5",
    +   "action": "get_meta_data",
    +   "entity": "*"
    +}
    +
    + JSON response :
    +
    +
    {
    +   "data": {
    +      "entities": [
    +         {
    +            "base_entity": "",
    +            "description": "",
    +            "entity_id": {
    +               "description": "",
    +               "key": "author_id",
    +               "type": "QString"
    +            },
    +            "key": "author",
    +            "name": "author",
    +            "properties": [
    +               {
    +                  "description": "",
    +                  "key": "name",
    +                  "type": "QString"
    +               },
    +               {
    +                  "description": "",
    +                  "key": "birthdate",
    +                  "type": "QDate"
    +               },
    +               {
    +                  "description": "",
    +                  "key": "sex",
    +                  "type": "enum author::enum_sex *"
    +               }
    +            ],
    +            "relations": [
    +               {
    +                  "description": "",
    +                  "key": "list_blog",
    +                  "target": "blog",
    +                  "type": "std::vector<std::shared_ptr<blog>>",
    +                  "type_relation": "relation one-to-many"
    +               }
    +            ],
    +            "version": 0
    +         },
    +         {
    +            "base_entity": "",
    +            "description": "",
    +            "entity_id": {
    +               "description": "",
    +               "key": "blog_id",
    +               "type": "long"
    +            },
    +            "key": "blog",
    +            "name": "blog",
    +            "properties": [
    +               {
    +                  "description": "",
    +                  "key": "blog_text",
    +                  "type": "QString"
    +               },
    +               {
    +                  "description": "",
    +                  "key": "date_creation",
    +                  "type": "QDateTime"
    +               }
    +            ],
    +            "relations": [
    +               {
    +                  "description": "",
    +                  "key": "author_id",
    +                  "target": "author",
    +                  "type": "std::shared_ptr<author>",
    +                  "type_relation": "relation many-to-one"
    +               },
    +               {
    +                  "description": "",
    +                  "key": "list_comment",
    +                  "target": "comment",
    +                  "type": "QList<std::shared_ptr<comment>>",
    +                  "type_relation": "relation one-to-many"
    +               },
    +               {
    +                  "description": "",
    +                  "key": "list_category",
    +                  "target": "category",
    +                  "type": "qx::QxCollection<long, QSharedPointer<category>>",
    +                  "type_relation": "relation many-to-many"
    +               }
    +            ],
    +            "version": 0
    +         },
    +         {
    +            "base_entity": "",
    +            "description": "",
    +            "entity_id": {
    +               "description": "",
    +               "key": "comment_id",
    +               "type": "long"
    +            },
    +            "key": "comment",
    +            "name": "comment",
    +            "properties": [
    +               {
    +                  "description": "",
    +                  "key": "comment_text",
    +                  "type": "QString"
    +               },
    +               {
    +                  "description": "",
    +                  "key": "date_creation",
    +                  "type": "QDateTime"
    +               }
    +            ],
    +            "relations": [
    +               {
    +                  "description": "",
    +                  "key": "blog_id",
    +                  "target": "blog",
    +                  "type": "std::shared_ptr<blog>",
    +                  "type_relation": "relation many-to-one"
    +               }
    +            ],
    +            "version": 0
    +         },
    +         {
    +            "base_entity": "",
    +            "description": "",
    +            "entity_id": {
    +               "description": "",
    +               "key": "category_id",
    +               "type": "long"
    +            },
    +            "key": "category",
    +            "name": "category",
    +            "properties": [
    +               {
    +                  "description": "",
    +                  "key": "name",
    +                  "type": "QString"
    +               },
    +               {
    +                  "description": "",
    +                  "key": "description",
    +                  "type": "QString"
    +               }
    +            ],
    +            "relations": [
    +               {
    +                  "description": "",
    +                  "key": "list_blog",
    +                  "target": "blog",
    +                  "type": "qx::QxCollection<long, std::shared_ptr<blog>>",
    +                  "type_relation": "relation many-to-many"
    +               }
    +            ],
    +            "version": 0
    +         }
    +      ]
    +   },
    +   "request_id": "842ed7b5-9b94-455f-86dc-32992866b3d5"
    +}
    +
    +

    +
    +

    Send a list of JSON + requests

    +
    + To limit transactions count between client and server, it is possible to send a list of JSON + requests to QxRestApi module. + Each JSON request can contain its own unique identifier request_id (to match a JSON + response with the right JSON request). + When a list of JSON requests is sent to QxRestApi module, then a + transaction (commit/rollback) is automatically created (so if an error occurred, then all + actions in database are cancelled). +

    + -- Example n�1 -- send a list of 4 JSON + requests to QxRestApi module (1 request to fetch project + meta-data + 3 requests to fetch_all blogs with several ways + to get relationships) : +

    + JSON request :
    +
    +
    [
    +   {
    +      "request_id": "53c96a23-2566-4b3d-ae6c-bff634600e79",
    +      "action": "get_meta_data",
    +      "entity": "*"
    +   },
    +   {
    +      "request_id": "56e3ca99-5c12-4aca-aa6c-7d0e43c1e636",
    +      "action": "fetch_all",
    +      "entity": "blog"
    +   },
    +   {
    +      "request_id": "692968e4-8885-41ad-b918-6ce2791b3bb8",
    +      "action": "fetch_all",
    +      "entity": "blog",
    +      "data": [
    +         {
    +            "key": "",
    +            "value": ""
    +         }
    +      ]
    +   },
    +   {
    +      "request_id": "4ffe38a6-d642-44b0-8be1-198e84256321",
    +      "action": "fetch_all",
    +      "entity": "blog",
    +      "relations": [
    +         "*->*"
    +      ]
    +   }
    +]
    +
    + JSON response :
    +
    +
    [
    +   {
    +      "data": {
    +         "entities": [
    +            {
    +               "base_entity": "",
    +               "description": "",
    +               "entity_id": {
    +                  "description": "",
    +                  "key": "author_id",
    +                  "type": "QString"
    +               },
    +               "key": "author",
    +               "name": "author",
    +               "properties": [
    +                  {
    +                     "description": "",
    +                     "key": "name",
    +                     "type": "QString"
    +                  },
    +                  {
    +                     "description": "",
    +                     "key": "birthdate",
    +                     "type": "QDate"
    +                  },
    +                  {
    +                     "description": "",
    +                     "key": "sex",
    +                     "type": "enum author::enum_sex *"
    +                  }
    +               ],
    +               "relations": [
    +                  {
    +                     "description": "",
    +                     "key": "list_blog",
    +                     "target": "blog",
    +                     "type": "std::vector<std::shared_ptr<blog>>",
    +                     "type_relation": "relation one-to-many"
    +                  }
    +               ],
    +               "version": 0
    +            },
    +            {
    +               "base_entity": "",
    +               "description": "",
    +               "entity_id": {
    +                  "description": "",
    +                  "key": "blog_id",
    +                  "type": "long"
    +               },
    +               "key": "blog",
    +               "name": "blog",
    +               "properties": [
    +                  {
    +                     "description": "",
    +                     "key": "blog_text",
    +                     "type": "QString"
    +                  },
    +                  {
    +                     "description": "",
    +                     "key": "date_creation",
    +                     "type": "QDateTime"
    +                  }
    +               ],
    +               "relations": [
    +                  {
    +                     "description": "",
    +                     "key": "author_id",
    +                     "target": "author",
    +                     "type": "std::shared_ptr<author>",
    +                     "type_relation": "relation many-to-one"
    +                  },
    +                  {
    +                     "description": "",
    +                     "key": "list_comment",
    +                     "target": "comment",
    +                     "type": "QList<std::shared_ptr<comment>>",
    +                     "type_relation": "relation one-to-many"
    +                  },
    +                  {
    +                     "description": "",
    +                     "key": "list_category",
    +                     "target": "category",
    +                     "type": "qx::QxCollection<long, QSharedPointer<category>>",
    +                     "type_relation": "relation many-to-many"
    +                  }
    +               ],
    +               "version": 0
    +            },
    +            {
    +               "base_entity": "",
    +               "description": "",
    +               "entity_id": {
    +                  "description": "",
    +                  "key": "comment_id",
    +                  "type": "long"
    +               },
    +               "key": "comment",
    +               "name": "comment",
    +               "properties": [
    +                  {
    +                     "description": "",
    +                     "key": "comment_text",
    +                     "type": "QString"
    +                  },
    +                  {
    +                     "description": "",
    +                     "key": "date_creation",
    +                     "type": "QDateTime"
    +                  }
    +               ],
    +               "relations": [
    +                  {
    +                     "description": "",
    +                     "key": "blog_id",
    +                     "target": "blog",
    +                     "type": "std::shared_ptr<blog>",
    +                     "type_relation": "relation many-to-one"
    +                  }
    +               ],
    +               "version": 0
    +            },
    +            {
    +               "base_entity": "",
    +               "description": "",
    +               "entity_id": {
    +                  "description": "",
    +                  "key": "category_id",
    +                  "type": "long"
    +               },
    +               "key": "category",
    +               "name": "category",
    +               "properties": [
    +                  {
    +                     "description": "",
    +                     "key": "name",
    +                     "type": "QString"
    +                  },
    +                  {
    +                     "description": "",
    +                     "key": "description",
    +                     "type": "QString"
    +                  }
    +               ],
    +               "relations": [
    +                  {
    +                     "description": "",
    +                     "key": "list_blog",
    +                     "target": "blog",
    +                     "type": "qx::QxCollection<long, std::shared_ptr<blog>>",
    +                     "type_relation": "relation many-to-many"
    +                  }
    +               ],
    +               "version": 0
    +            }
    +         ]
    +      },
    +      "request_id": "53c96a23-2566-4b3d-ae6c-bff634600e79"
    +   },
    +   {
    +      "data": [
    +         {
    +            "author_id": {
    +               "author_id": "author_id_1",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 1,
    +            "blog_text": "save recursive blog from QxOrm REST API",
    +            "date_creation": "2013-11-25T09:56:33",
    +            "list_category": [],
    +            "list_comment": []
    +         },
    +         {
    +            "author_id": {
    +               "author_id": "author_id_1",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 5,
    +            "blog_text": "save another blog from QxOrm REST API !",
    +            "date_creation": "2016-06-12T08:33:12",
    +            "list_category": [],
    +            "list_comment": []
    +         },
    +         {
    +            "author_id": {
    +               "author_id": "author_id_save_recursive",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 6,
    +            "blog_text": "save recursive - new blog from QxOrm REST API",
    +            "date_creation": "2013-11-25T09:56:33",
    +            "list_category": [],
    +            "list_comment": []
    +         },
    +         {
    +            "author_id": {
    +               "author_id": "author_id_save_recursive",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 7,
    +            "blog_text": "save recursive - new blog from QxOrm REST API",
    +            "date_creation": "2013-11-25T09:56:33",
    +            "list_category": [],
    +            "list_comment": []
    +         }
    +      ],
    +      "request_id": "56e3ca99-5c12-4aca-aa6c-7d0e43c1e636"
    +   },
    +   {
    +      "data": [
    +         {
    +            "key": 1,
    +            "value": {
    +               "author_id": {
    +                  "author_id": "author_id_1",
    +                  "birthdate": null,
    +                  "list_blog": [],
    +                  "name": "",
    +                  "sex": 2
    +               },
    +               "blog_id": 1,
    +               "blog_text": "save recursive blog from QxOrm REST API",
    +               "date_creation": "2013-11-25T09:56:33",
    +               "list_category": [],
    +               "list_comment": []
    +            }
    +         },
    +         {
    +            "key": 5,
    +            "value": {
    +               "author_id": {
    +                  "author_id": "author_id_1",
    +                  "birthdate": null,
    +                  "list_blog": [],
    +                  "name": "",
    +                  "sex": 2
    +               },
    +               "blog_id": 5,
    +               "blog_text": "save another blog from QxOrm REST API !",
    +               "date_creation": "2016-06-12T08:33:12",
    +               "list_category": [],
    +               "list_comment": []
    +            }
    +         },
    +         {
    +            "key": 6,
    +            "value": {
    +               "author_id": {
    +                  "author_id": "author_id_save_recursive",
    +                  "birthdate": null,
    +                  "list_blog": [],
    +                  "name": "",
    +                  "sex": 2
    +               },
    +               "blog_id": 6,
    +               "blog_text": "save recursive - new blog from QxOrm REST API",
    +               "date_creation": "2013-11-25T09:56:33",
    +               "list_category": [],
    +               "list_comment": []
    +            }
    +         },
    +         {
    +            "key": 7,
    +            "value": {
    +               "author_id": {
    +                  "author_id": "author_id_save_recursive",
    +                  "birthdate": null,
    +                  "list_blog": [],
    +                  "name": "",
    +                  "sex": 2
    +               },
    +               "blog_id": 7,
    +               "blog_text": "save recursive - new blog from QxOrm REST API",
    +               "date_creation": "2013-11-25T09:56:33",
    +               "list_category": [],
    +               "list_comment": []
    +            }
    +         }
    +      ],
    +      "request_id": "692968e4-8885-41ad-b918-6ce2791b3bb8"
    +   },
    +   {
    +      "data": [
    +         {
    +            "author_id": {
    +               "author_id": "author_id_1",
    +               "birthdate": "2019-04-02",
    +               "list_blog": [
    +                  {
    +                     "author_id": {
    +                        "author_id": "author_id_1",
    +                        "birthdate": null,
    +                        "list_blog": [],
    +                        "name": "",
    +                        "sex": 2
    +                     },
    +                     "blog_id": 5,
    +                     "blog_text": "save another blog from QxOrm REST API !",
    +                     "date_creation": "2016-06-12T08:33:12",
    +                     "list_category": [],
    +                     "list_comment": []
    +                  },
    +                  {
    +                     "author_id": {
    +                        "author_id": "author_id_1",
    +                        "birthdate": null,
    +                        "list_blog": [],
    +                        "name": "",
    +                        "sex": 2
    +                     },
    +                     "blog_id": 1,
    +                     "blog_text": "save recursive blog from QxOrm REST API",
    +                     "date_creation": "2013-11-25T09:56:33",
    +                     "list_category": [],
    +                     "list_comment": []
    +                  }
    +               ],
    +               "name": "author_1",
    +               "sex": 0
    +            },
    +            "blog_id": 1,
    +            "blog_text": "save recursive blog from QxOrm REST API",
    +            "date_creation": "2013-11-25T09:56:33",
    +            "list_category": [],
    +            "list_comment": []
    +         },
    +         {
    +            "author_id": {
    +               "author_id": "author_id_1",
    +               "birthdate": "2019-04-02",
    +               "list_blog": [
    +                  {
    +                     "author_id": {
    +                        "author_id": "author_id_1",
    +                        "birthdate": null,
    +                        "list_blog": [],
    +                        "name": "",
    +                        "sex": 2
    +                     },
    +                     "blog_id": 5,
    +                     "blog_text": "save another blog from QxOrm REST API !",
    +                     "date_creation": "2016-06-12T08:33:12",
    +                     "list_category": [],
    +                     "list_comment": []
    +                  },
    +                  {
    +                     "author_id": {
    +                        "author_id": "author_id_1",
    +                        "birthdate": null,
    +                        "list_blog": [],
    +                        "name": "",
    +                        "sex": 2
    +                     },
    +                     "blog_id": 1,
    +                     "blog_text": "save recursive blog from QxOrm REST API",
    +                     "date_creation": "2013-11-25T09:56:33",
    +                     "list_category": [],
    +                     "list_comment": []
    +                  }
    +               ],
    +               "name": "author_1",
    +               "sex": 0
    +            },
    +            "blog_id": 5,
    +            "blog_text": "save another blog from QxOrm REST API !",
    +            "date_creation": "2016-06-12T08:33:12",
    +            "list_category": [],
    +            "list_comment": []
    +         },
    +         {
    +            "author_id": {
    +               "author_id": "author_id_save_recursive",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 6,
    +            "blog_text": "save recursive - new blog from QxOrm REST API",
    +            "date_creation": "2013-11-25T09:56:33",
    +            "list_category": [],
    +            "list_comment": []
    +         },
    +         {
    +            "author_id": {
    +               "author_id": "author_id_save_recursive",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 7,
    +            "blog_text": "save recursive - new blog from QxOrm REST API",
    +            "date_creation": "2013-11-25T09:56:33",
    +            "list_category": [],
    +            "list_comment": []
    +         }
    +      ],
    +      "request_id": "4ffe38a6-d642-44b0-8be1-198e84256321"
    +   }
    +]
    +
    +

    +
    +
    + +
    +
    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/doc/qxorm_en/manual_qxee.html b/doc/qxorm_en/manual_qxee.html new file mode 100644 index 0000000..d8e64c5 --- /dev/null +++ b/doc/qxorm_en/manual_qxee.html @@ -0,0 +1,5630 @@ + + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic + editor (data model designer and source code generator) + + + + + + + + + + + + + + + + + + + + + +
    QxOrm + + + Windows + Linux + Macintosh + C++
    +
    + + + + + + + + + + + + + + + + + + +
    HomeDownloadQuick sample + Tutorial (4) + + + + + + +
    + +
    +
    + Manual (2) + + + + + + +
    + +
    +
    ForumOur customers
    +
    + + + + + + + + + + + + + + + + + +
    + QxOrm >> Manual - QxEntityEditor application user guide + + + + + + + + + + + + + + + +
    + Current version :  + + QxOrm 1.5.0 - QxOrm library online class documentation - GitHub +
    + + + QxEntityEditor 1.2.8 +
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    + + + + + + + + + + +
    + Select a manual : + + QxOrm user guide + QxEntityEditor user guide +
    +
    +
    + + + + + + + + + +
    +

    QxEntityEditor application manual - Table of Contents

    +
    +
      +
    1. + Introduction +
        +
      1. + QxEntityEditor : graphic editor for QxOrm library +
      2. +
      3. + Download and installation +
      4. +
      5. + QxEntityEditor videos +
      6. +
      +
    2. +
    3. + Main features +
        +
      1. + Main screen views +
          +
        1. + Zoom on entities diagram +
        2. +
        3. + "Splitter" view mode +
        4. +
        +
      2. +
      3. + QxEntityEditor application settings +
      4. +
      5. + Register a license key +
      6. +
      7. + Create/open a *.qxee project +
          +
        1. + Architecture of a *.qxee project file (SQLite + database) +
        2. +
        +
      8. +
      9. + Project settings +
      10. +
      11. + Manage entities +
          +
        1. + Create a new entity +
        2. +
        3. + Modify an entity +
        4. +
        5. + Delete an entity +
        6. +
        7. + Clone an entity +
        8. +
        9. + Entity settings +
        10. +
        11. + Colors associated to an entity +
        12. +
        +
      12. +
      13. + Manage list of properties of an entity +
          +
        1. + Add/modify/delete a property +
        2. +
        3. + Property settings +
        4. +
        +
      14. +
      15. + Manage relationships between entities +
          +
        1. + Add/modify/delete a relationship +
        2. +
        3. + Relationship settings +
        4. +
        +
      16. +
      17. + Manage enumerations +
          +
        1. + Add/modify/delete/clone an enumeration +
        2. +
        3. + Enumeration settings +
        4. +
        5. + Colors associated to an enumeration +
        6. +
        +
      18. +
      19. + Manage namespaces +
          +
        1. + Rename a namespace +
        2. +
        3. + Manage colors of a namespace +
        4. +
        +
      20. +
      21. + Manage post-it/comments +
          +
        1. + Add/modify/delete/clone a post-it +
        2. +
        3. + Colors associated to a post-it +
        4. +
        +
      22. +
      23. + Organize diagram layout +
      24. +
      25. + Project historic (tag project state) +
      26. +
      27. + C++ source code preview +
      28. +
      29. + Naming convention (snake_case, camelCase) +
      30. +
      31. + List of available plugins +
      32. +
      +
    4. +
    5. + Import plugins +
        +
      1. + Import from a directory linked to a + Source Control manager (Git, Perforce, CVS, etc.) +
      2. +
      3. + Import project based on JSON file +
      4. +
      5. + Import from MySQL or MariaDB database +
      6. +
      7. + Import from PostgreSQL database +
      8. +
      9. + Import from SQLite database +
      10. +
      11. + Import from database using ODBC driver (Oracle, MS + SQL Server, etc.) +
      12. +
      +
    6. +
    7. + Export plugins +
        +
      1. + Export to C++ project +
          +
        1. + C++ export settings +
        2. +
        3. + Overview of generated C++ + project +
        4. +
        5. + Build generated C++ project + (using qmake or cmake) +
        6. +
        7. + Example : how to use generated + C++ project +
        8. +
        +
      2. +
      3. + Export to C++ model/view project +
          +
        1. + Export settings +
        2. +
        3. + Overview of + generated project +
        4. +
        +
      4. +
      5. + Export to C++ services project +
          +
        1. + Export settings +
        2. +
        3. + Overview of generated + project +
        4. +
        5. + Generic application + server to provide services +
        6. +
        +
      6. +
      7. + Export SQL DDL database schema +
      8. +
      9. + Print entities diagram +
      10. +
      11. + Export to a directory linked to a + Source Control manager (Git, Perforce, CVS, etc.) +
      12. +
      13. + Export project to XML or JSON format +
      14. +
      +
    8. +
    9. + Javascript engine to customize export +
        +
      1. + Architecture and workflow of Javascript + engine +
          +
        1. + Input parameters of Javascript + engine +
        2. +
        +
      2. +
      3. + Available functions in Javascript +
          +
        1. + Get entity details +
        2. +
        3. + Iterate over list of + properties of an entity +
        4. +
        5. + Get property details +
        6. +
        7. + Get enumeration details +
        8. +
        9. + Get meta-data of + entity/property/enumeration +
        10. +
        11. + Get environment variable +
        12. +
        13. + Read and write to files +
        14. +
        15. + Get list of entities/enumerations of + a project +
        16. +
        17. + Fetch all application settings (at + global level, project level and plugin level) +
        18. +
        +
      4. +
      5. + Add a custom action (placeholder) in C++ + export template +
      6. +
      7. + Script example : add automatically + Q_PROPERTY definition on all generated C++ properties +
      8. +
      9. + Enable Javascript debugger editor +
      10. +
      +
    10. +
    11. + Execute custom scripts (shell/bat) before/after plugin + execution +
    12. +
    13. + QxEntityEditor command-line interface +
    14. +
    +
    qt_ambassador
    + + QxOrm library has been accepted into the Qt + Ambassador Program + +
    +
    +
    +
    + +

    Introduction

    +
    + The goal of this documentation is to provide a user guide to learn how to work with + QxEntityEditor application features (the graphic editor for QxOrm library). + This manual is intended for developers and software architects who are looking for a solution to + manage a persistent data layer in C++/Qt. + Technical skills in C++ and databases are required to understand this document. +

    + Note : another documentation dedicated to QxOrm library is + available. +

    +

    QxEntityEditor : graphic + editor for QxOrm library

    +
    + QxEntityEditor is a graphic editor for QxOrm library : QxEntityEditor + provides a graphic way to manage the data model.
    + QxEntityEditor is multi-platform (available for Windows, Linux and Mac OS X) and + generates native code for all environments : desktop (Windows, Linux, Mac OS X), embedded and + mobile (Android, iOS, Windows Phone, Raspberry Pi, etc.).
    +
    + QxEntityEditor is based on plugins and provides many ways to import/export your data + model : + + + + + + + + + + + + + + + + + +
    QxEntityEditor on Windows + QxEntityEditor on Linux + QxEntityEditor on Mac OS X +
    QxEntityEditor on WindowsQxEntityEditor on LinuxQxEntityEditor on Mac OS X
    +

    + QxEntityEditor application is developed by XDL Team, a software development engineer + since 2003.
    +

    +
    +

    Download and + installation

    +
    + QxEntityEditor application can be downloaded on QxOrm web site, � + Download � web page. + Several QxEntityEditor versions are available : +
      +
    • Windows (from Windows XP to Windows 10) 32bits and 64bits mode ;
    • +
    • Linux (Ubuntu, Fedora, etc...) 32bits and 64bits mode ;
    • +
    • Mac OS X (64bits only).
    • +
    + For Windows, QxEntityEditor is available with an installation setup or a portable ZIP file.
    + For Linux and Mac OS X, QxEntityEditor is available only with a portable ZIP file.
    + No extra dependency is required, QxEntityEditor installation is very easy : you just have to + unzip downloaded file and run QxEntityEditor executable. +

    +
    +

    QxEntityEditor + videos

    +
    + Here is a quick overview of + QxEntityEditor features :
    +
    +
    +
    + Video presentation step by step : +
      +
    • Download and install QxOrm library (10s) ;
    • +
    • Download and install QxEntityEditor (56s) ;
    • +
    • Create a QxEntityEditor project (1m 46s) ;
    • +
    • Export entities to a C++/Qt project (8m 26s) ;
    • +
    • Export entities to a DDL SQL database script (10m 22s) ;
    • +
    • Create a client/server application to transfer entities over network (12m 31s) ; +
    • +
    • Export QxEntityEditor project to a XML or JSON file (17m 13s) ;
    • +
    • Execute QxEntityEditor with command line (no GUI) (18m 07s).
    • +
    +
    +
    +
    + Here is another video of + QxEntityEditor application to show how to import an existing database structure (MySQL + Workbench project) :
    +
    +
    +
    + Video step by step : +
      +
    • MySQL Workbench project - Sakila sample database (10s) ;
    • +
    • Create a DSN to connect to MySQL by ODBC (46s) ;
    • +
    • Import database structure in a QxEntityEditor project (1m 15s) ;
    • +
    • Export QxEntityEditor project to a C++ Qt project (3m 10s) ;
    • +
    • Build the generated C++ Qt project (4m 22s).
    • +
    +
    +
    +
    + +

    Main features

    +
    + QxEntityEditor is a graphic editor to manage : entities, properties, relationships between + entities, enumerations, namespaces. + To link database and C++ source code : +
      +
    • an entity is a table on database side, and a class on C++ source code side ;
    • +
    • a property is a column of a table on database side, and a data member of a class on C++ + source code side ;
    • +
    • a relationship (1-n, n-1, 1-1 or n-n) is a link between 2 tables + (foreign key) on database side, and a link between 2 classes on C++ source code side ;
    • +
    • an enumeration is a list of values available on C++ source code side (for now, this is just + a converted numeric value on database side) ;
    • +
    • a namespace is a database schema, and where classes are located on C++ source code side. +
    • +
    +
    +

    Main screen views

    +
    + Here is a screenshot which shows all areas of QxEntityEditor main view : +

    + All views +

    +
      +
    • Area 1 : main menu and toolbar of QxEntityEditor application ==> all actions are + available with these buttons ;
    • +
    • Area 2 : listbox to load quickly recent projects ;
    • +
    • Area 3 : treeview to display all project items (entities, properties, + relationships, enumerations, namespaces) ;
    • +
    • Area 4 : "Navigator" view to move quickly on entities diagram (useful for + large projects) ;
    • +
    • Area 5 : entities diagram to display all project items on a graphical view ;
    • +
    • Area 6 : settings screen (with tabs) for entities, properties, relationships, + enumerations + C++ source code preview ;
    • +
    • Area 7 : zoom on entities diagram.
    • +
    +
    +

    Zoom on entities + diagram

    +
    + QxEntityEditor application provides several ways to zoom on entities diagram : +
      +
    • using the main menu : View >> Zoom view to XX% ;
    • +
    • using the slider at the bottom-right of the screen ;
    • +
    • using a contextual menu, right-click on entities diagram, sub-menu : View >> + Zoom view to XX% ;
    • +
    • using left-click + mouse wheel on entities diagram.
    • +
    +
    + Zoom +

    +
    +

    "Splitter" view + mode

    +
    + View >> Show/hide splitter mode menu switches tabs settings (entities, + properties, relationships, enumerations + C++ source code preview) : +
      +
    • on the right part of the screen to provide a quick access on selected item settings ; +
    • +
    • hidden to display entities diagram on full screen (default value).
    • +
    +
    + Splitter mode +

    +
    +
    +

    QxEntityEditor + application settings

    +
    + QxEntityEditor application global parameters (for all projects) are accessible via main menu + Tools >> Global settings : +

    + Global settings +
    +
      +
    • Field � QxOrm library path � : define where QxOrm library is located. + This parameter is required to run a C++ export process to find QxOrm.pri (or + QxOrm.cmake) configuration file. + You can put an absolute path, or use an environment variable, for example : + $$(QXORM_DIR) ; +
    • +
    • Field � Auto load last opened project at startup � : run QxEntityEditor + application loading automatically the last opened project ;
    • +
    • Field � Display property type in entities viewer � : if option enabled, then each + property type is displayed on entities diagram ;
    • +
    • Field � Use old style to draw relationships � : simplify relationships display + (1-n, n-1, 1-1 or n-n) on entities diagram ;
    • +
    • Field � Disable undo/redo feature � : can be useful to optimize performance with + large projects ;
    • +
    • Fields � Entity/Enum/Comment width � : define default items width on entities + diagram.
    • +
    +
    +
    +

    Register a license + key

    +
    + Without a valid license key, a QxEntityEditor project is limited to 5 entities per project. + To remove this limitation, you can register a license key via main menu Help >> License + information : +

    + License +

    + Just copy/past your license key in the field License key, then push the Save + button. + A web access is required to register a license key. + If no error occurred, then the license key is registered and QxEntityEditor application can be + used without any limitation until Expiration date field. +

    + Note : if a web access error occurred during registration, you could try to enable � + If you are behind a proxy... � option. +

    + Other note : to get a valid license key, please contact us at : ic-east.com +

    +
    +

    Create/open a + *.qxee project

    +
    + A new QxEntityEditor project is created via main menu File >> New project : +

    + Project new +
    +
      +
    • Field � Project name � : the QxEntityEditor project name. Project file has the + same name with *.qxee extension ;
    • +
    • Field � Project location � : define where QxEntityEditor project is located. You + can use the "..." button to select a directory.
    • +
    +
    + An existing QxEntityEditor project can be opened via main menu File >> Open project + : then just select a project file with *.qxee extension. + Under the main toolbar of QxEntityEditor application, a list of recent projects is available to + switch quickly from a project to another : +

    + Project list +

    +

    Architecture of a + *.qxee project file (SQLite database)

    +
    + A *.qxee project file of QxEntityEditor application is a SQLite database.
    + A same QxEntityEditor project can be shared on all environments : Windows, Linux and Mac OS + X.
    + A *.qxee project file can be opened by a SQLite database manager tool : for example, + the free Firefox plugin SQLite Manager : +

    + Project database +

    + Note : using the Execute custom scripts (shell/bat) + before/after plugin execution feature, it is possible for example to apply a script + (.bat or .sh) to modify some imported data in the *.qxee SQLite database + file after an import process. +

    +
    +
    +

    Project + settings

    +
    + QxEntityEditor *.qxee project parameters are accessible via main menu Tools >> + Project settings : +

    + Project settings tab 1 +

    +
      +
    • Field � Default entity namespace � : default namespace when an entity is created + (it is possible to define another namespace in Entity + settings screen) ;
    • +
    • Field � Table name prefix � : prefix used to set table name associated to entity ; +
    • +
    • Field � Table name suffix � : suffix used to set table name associated to entity ; +
    • +
    • Field � Primary key prefix � : prefix used to set primary key name associated to + entity ;
    • +
    • Field � Primary key suffix � : suffix used to set primary key name associated to + entity ;
    • +
    • Field � Default primary key type � : default C++ type for primary keys ;
    • +
    • Field � Default property type � : default C++ type for properties.
    • +
    +
    + The second tab of project parameters screen provides a way to manage items colors in the + entities diagram at a project level : +

    + Project settings tab 2 +

    + Note : items colors in the entities diagram can be defined at several levels (from the + more global level to the more specific level) : + +
    +
    +

    Manage entities

    +
    + An entity in QxEntityEditor application is a table from a database point of view, and is a + persistent class (registered in QxOrm context) in C++ source code. + QxEntityEditor application provides features to create, modify, delete and clone entity. +

    +

    Create a new + entity

    +
    + A new entity can be created via : +
      +
    • Main menu : Actions >> New entity ;
    • +
    • Right-click contextual menu on entities diagram : Entity >> New entity ; +
    • +
    • The � Create a new entity � button on entity parameters screen when no entity + is selected.
    • +
    +
    + Entity new +

    + Note : these actions open the entity parameters screen in + creation context. + The entity will be really created and added to the *.qxee project after saving it in + the entity parameters screen. +

    +
    +

    Modify an + entity

    +
    + An entity can be modified via : +
      +
    • Main menu : Actions >> Modify entity ;
    • +
    • Right-click contextual menu on entities diagram : Entity >> Modify entity + ;
    • +
    • A double-click on entity name in the entities diagram.
    • +
    +
    + Entity modify +

    + Note : these actions open the entity parameters screen in + change context. + All entity changes will be really applied to the *.qxee project after saving it in the + entity parameters screen. +

    +
    +

    Delete an + entity

    +
    + An entity (or several entities) can be deleted via : +
      +
    • Main menu : Actions >> Delete entity ;
    • +
    • Right-click contextual menu on entities diagram : Entity >> Delete entity + ;
    • +
    • The "Del" keyboard shortcut when 1 or several entities are selected.
    • +
    +
    + Entity delete +

    +
    +

    Clone an + entity

    +
    + An entity can be cloned via : +
      +
    • Main menu : Actions >> Clone entity ;
    • +
    • Right-click contextual menu on entities diagram : Entity >> Clone entity + ;
    • +
    +
    + Entity clone +

    + Note : a cloned entity has all properties from source entity. Relationships are not + cloned. +

    +
    +

    Entity settings +

    +
    + Entity parameters are accessible via Entity settings tab : +

    + Entity settings +

    + This parameters screen is divided in several sections : +

    + -- Section Entity information (version XX) : entity version is incremented by + each project historic. +
      +
    • Field � Name � : entity name and generated C++ class name ;
    • +
    • Field � Table name � : database table name mapped to the entity (uses default prefix/suffix defined in project settings) ; +
    • +
    • Field � Namespace � : namespace where entity and generated C++ class are + located, each sub-namespace must be separated by :: characters, for example : + ns1::ns2::ns3 ; +
    • +
    • Field � Description � : entity description (displayed in generated C++ source + code) ;
    • +
    • Field � Inherit from � : parent entity (or C++ base class) : child entity gets + automatically primary key and all properties defined in parent entity ;
    • +
    • Field � Read only � : if enabled, then entity is in read-only mode : QxOrm + library only allows SQL queries of type SELECT (qx::dao fetch functions of QxOrm). + if enabled, then SQL queries of type INSERT, UPDATE and DELETE are + not available. + This parameter can be used for example to map an entity to a database view (read-only) + ;
    • +
    • Field � Abstract � : an abstract entity is not mapped to a database table, and + generated C++ class cannot be instantiated (abstract + class) : this parameter can be used for example to create parent entities which + contain a list of common properties to share with all other entities ;
    • +
    • Field � Soft delete column � : if empty, then remove an entity from database + means delete physically the row from database. + If not empty, this field defines the database + column used to process a soft delete behaviour (or logical delete) : the entity + is not physically removed in database.
    • +
    • Field � Validator method name � : validation function name (for example : + IsValid) used to validate an entity before inserting or updating it in database. + Validation method implementation must be written by the developper in its own + *.cpp file : see QxOrm library QxValidator + module for more details. +
    • +
    +
    + -- Section Entity triggers : +
      +
    • Checkbox � Before fetch, After fetch, Before insert, etc... � : + if checked, add C++ triggers methods definition onBeforeFetch(), + onAfterFetch(), onBeforeInsert(), etc... + Trigger method implementation must be written by the developper in its own *.cpp + file : see QxOrm library user manual for more + details. +
    • +
    +
    + -- Section Primary key property : fast definition of primary key (detailled settings of primary key is available). + +
    + -- Section List of properties : fast definition of list of properties (detailled settings for each property is available). + This list can be manually sorted using arrow up and arrow down buttons. +
      +
    • Column � Name � : property name, and data member name in C++ source code ;
    • +
    • Column � Type � : C++ property type (default type can be defined in project settings screen) ;
    • +
    • Column � Decoration � : C++ decoration type (for example boost::optional to manage database + NULL value) ;
    • +
    • Column � Collection � : if property is a list of, then define C++ list type ; +
    • +
    • Column � Default value � : property default value ;
    • +
    • Column � Index � : define a database index to optimize SELECT SQL + queries ;
    • +
    • Column � Transient � : if enabled, then C++ data member is not mapped to a + database column (property is registered in QxOrm context but is not persistent) ;
    • +
    • Button � Delete � : remove property from the list.
    • +
    +
    + -- Section List of relationships : fast definition of list of relationships (detailled settings for each relationship is available). + This list can be manually sorted using arrow up and arrow down buttons. +
      +
    • Column � Name � : relationship name, and data member name in C++ source code ; +
    • +
    • Column � Target � : target entity ;
    • +
    • Column � n-1 � : relationship of type many-to-one ;
    • +
    • Column � 1-n � : relationship of type one-to-many ;
    • +
    • Column � n-n � : relationship of type many-to-many ;
    • +
    • Column � 1-1 � : relationship of type one-to-one ;
    • +
    • Column � Decoration � : C++ decoration type (for relationships, QxOrm library + recommends smart-pointer : boost::shared_ptr, std::shared_ptr or + QSharedPointer) ; +
    • +
    • Column � Collection � : if relationship is a list of (1-n and + n-n), then define C++ list type ; +
    • +
    • Column � Foreign key � : relationship foreign key : property name of target + entity ;
    • +
    • Button � Delete � : remove relationship from the list.
    • +
    +
    + -- Section List of meta-data : meta-data provides a way to add and manage + extra-parameters and are available in C++ source code (QxOrm introspection engine) and Javascript engine to customize QxEntityEditor export + process. +
      +
    • Column � Key � : meta-data key ;
    • +
    • Column � Value � : meta-data value ;
    • +
    • Button � Delete � : remove meta-data from the list.
    • +
    +
    +
    +

    Colors + associated to an entity

    +
    + Entity colors can be defined via right-click contextual menu Define entity colors : +

    + Entity colors +

    + Note : items colors in the entities diagram can be defined at several levels (from the + more global level to the more specific level) : + +
    +
    +
    +

    Manage list of properties + of an entity

    +
    + A property in QxEntityEditor application is a column (of a table) from a database point of view, + and is a data member in a C++ class (registered in QxOrm context). + QxEntityEditor application provides features to create, modify, delete and sort properties of an + entity. +

    +

    Add/modify/delete a property

    +
    + Add and delete a property is done via entity parameters screen : + the List of properties section of this settings screen provides a way to add/remove + properties. + It is also possible to sort manually this list of properties. +

    + Modify a property is accessible via a button which is displayed on entities diagram when the + mouse is closed to a property : +

    + Property settings access +

    +
    +

    Property + settings

    +
    + Detailled property parameters are accessible via Property settings tab : +

    + Property settings +

    + This parameters screen is divided in several sections : +

    + -- Section Property information (version XX) : property version depends on project historic. +
      +
    • Field � Name � : property name, and data member name in C++ source code ;
    • +
    • Field � Column name � : database column name (if empty, then Column name + == Name) ;
    • +
    • Field � Description � : property description (displayed in generated C++ source + code) ;
    • +
    • Field � Index � : define a database index to optimize SELECT SQL queries + ;
    • +
    • Field � Transient � : if enabled, then C++ data member is not mapped to a + database column (property is registered in QxOrm context but is not persistent) ;
    • +
    • Field � Serializable � : if enabled, property is managed by serialization engine of QxOrm library ;
    • +
    • Field � Obsolete � : enable this option to remove a property without breaking ascendant compatibility of + serialization engine of QxOrm library : property becomes private without + Get/Set accessors. +
    • +
    +
    + -- Section Property type : +
      +
    • Field � Type � : C++ property type (default type can be defined in project settings screen) ;
    • +
    • Field � Decoration � : C++ decoration type (for example boost::optional to manage database + NULL value) ;
    • +
    • Field � Default value � : property default value ;
    • +
    • Field � Collection � : if property is a list of, then define C++ list type. +
    • +
    +
    + -- Section Property validation : validate a property before inserting/updating + in database using QxValidator module of QxOrm library. +
      +
    • Field � Min value � : minimal value allowed for a property (numeric type) ; +
    • +
    • Field � Max value � : maximal value allowed for a property (numeric type) ; +
    • +
    • Field � Min length � : minimal length allowed for a property (string type) ; +
    • +
    • Field � Max length � : maximal length allowed for a property (string type) ; +
    • +
    • Field � Regular expression � : regular expression used to validate a property + value (for example, an e-mail regular expression to check if property contains a valid + e-mail address) ;
    • +
    • Field � Not NULL � : check if property value + is not NULL ;
    • +
    • Field � Unique � : unique value in database, 2 rows in a same table cannot have + the same value in the column mapped to current property.
    • +
    +
    + -- Section Advanced : +
      +
    • Field � Accessibility �, values � public, protected or + private � : a public property is accessible without Get/Set + methods, a protected property is only accessible by inherited classes, a + private property is only accessible via Get/Set methods ; +
    • +
    • Field � SQL type � : QxOrm library provides a default mapping SQL type / C++ type, it is + possible to override default SQL type with this parameter ;
    • +
    • Field � SQL alias � : force a SQL alias to database column used by QxOrm + library to build SQL queries ;
    • +
    • Field � Format � : define property format (printf syntax) for database + storage (can be useful to manage dates and times for example) ;
    • +
    • Field � Get/Set accessors � : if enabled, Get/Set accessors are + generated in C++ source code to access and modify property value ;
    • +
    • Field � Read only � : if enabled, only Get accessor is generated in C++ + source code, so property becomes read-only ;
    • +
    • Field � Get method implementation � : provides a way to override default + Get method implementation in C++ source code to access property value ; +
    • +
    • Field � Set method implementation � : provides a way to override default + Set method implementation in C++ source code to modify property value. +
    • +
    +
    + -- Section List of meta-data : meta-data provides a way to add and manage + extra-parameters and are available in C++ source code (QxOrm introspection engine) and Javascript engine to customize QxEntityEditor export + process. +
      +
    • Column � Key � : meta-data key ;
    • +
    • Column � Value � : meta-data value ;
    • +
    • Button � Delete � : remove meta-data from the list.
    • +
    +
    +
    +
    +

    Manage relationships + between entities

    +
    + A relationship between 2 entities (1-n, n-1, 1-1 or n-n) in + QxEntityEditor application links 2 tables in database, and links 2 classes in C++ source code. + QxEntityEditor application provides features to create, modify, delete and sort relationships of + an entity. +

    +

    Add/modify/delete a relationship

    +
    + Add and delete a relationship is done via entity parameters + screen : the List of relationships section of this settings screen provides a + way to add/remove relationships. + It is also possible to sort manually this list of relationships. +

    + Modify a relationship is accessible via a button which is displayed on entities diagram when + the mouse is closed to a relationship : +

    + Relation settings access +

    +
    +

    Relationship + settings

    +
    + Detailled relationship parameters are accessible via Relationship settings tab : +

    + Relation settings +

    + This parameters screen is divided in several sections : +

    + -- Section Relationship information (version XX) : relationship version depends + on project historic. +
      +
    • Field � Name � : relationship name, and data member name in C++ source code ; +
    • +
    • Field � Target � : target entity ;
    • +
    • Field � Description � : relationship description (displayed in generated C++ + source code) ;
    • +
    • Fields � 1-n, n-1, 1-1 or n-n � : relationship type, depending on selected option, + some other fields are enabled or disabled ;
    • +
    • Field � Column name � : enabled only for relationships of type n-1, + column name in database (if empty, then Column name == Name) ;
    • +
    • Field � Decoration � : C++ decoration type (for relationships, QxOrm library + recommends smart-pointer : boost::shared_ptr, std::shared_ptr or + QSharedPointer) ; +
    • +
    • Field � Collection � : if relationship is a list of (1-n and + n-n), then define C++ list type ; +
    • +
    • Field � Foreign key � : relationship foreign key : property name of target + entity ;
    • +
    • Field � FK owner � : used only by n-n relationships ;
    • +
    • Field � Extra-table � : used only by n-n relationships, extra-table name + which links source and target entities ;
    • +
    • Field � Index � : define a database index to optimize SELECT SQL queries + ;
    • +
    • Field � Serializable � : if enabled, relationship is managed by serialization engine of QxOrm library ;
    • +
    • Field � Obsolete � : enable this option to remove a relationship without breaking ascendant compatibility of + serialization engine of QxOrm library : relationship becomes private without + Get/Set accessors. +
    • +
    +
    + -- Section Advanced : +
      +
    • Field � Accessibility �, values � public, protected or + private � : a public relationship is accessible without Get/Set + methods, a protected relationship is only accessible by inherited classes, a + private relationship is only accessible via Get/Set methods ; +
    • +
    • Field � Get/Set accessors � : if enabled, Get/Set accessors are + generated in C++ source code to access and modify relationship value ;
    • +
    • Field � Read only � : if enabled, only Get accessor is generated in C++ + source code, so relationship becomes read-only ;
    • +
    • Field � Get method implementation � : provides a way to override default + Get method implementation in C++ source code to access relationship value ; +
    • +
    • Field � Set method implementation � : provides a way to override default + Set method implementation in C++ source code to modify relationship value. +
    • +
    +
    + -- Section List of meta-data : meta-data provides a way to add and manage + extra-parameters and are available in C++ source code (QxOrm introspection engine) and Javascript engine to customize QxEntityEditor export + process. +
      +
    • Column � Key � : meta-data key ;
    • +
    • Column � Value � : meta-data value ;
    • +
    • Button � Delete � : remove meta-data from the list.
    • +
    +
    +
    +
    +

    Manage enumerations

    +
    + An enumeration in QxEntityEditor application is a list of values available in C++ source code + (converted to a numeric value in database). + QxEntityEditor application provides features to create, modify, delete and clone enumeration. +

    +

    Add/modify/delete/clone an enumeration

    +
    + Add, modify, delete and clone an enumeration is done via equivalent menu for an entity (just replace entity word by enumeration).
    + So QxEntityEditor application provides exactly same menus as an + entity to manage enumeration. +

    +
    +

    Enumeration + settings

    +
    + Enumeration parameters are accessible via Enumeration settings tab : +

    + Enumeration settings +

    + This parameters screen is divided in several sections : +

    + -- Section Enumeration information (version XX) : enumeration version is + incremented by each project historic. +
      +
    • Field � Name � : enumeration name and generated C++ class name ;
    • +
    • Field � Description � : enumeration description (displayed in generated C++ + source code) ;
    • +
    • Field � Namespace � : namespace where enumeration and generated C++ class are + located, each sub-namespace must be separated by :: characters, for example : + ns1::ns2::ns3 ; +
    • +
    • Field � Use Qt macro Q_ENUM � : uses Q_ENUM macro + provided by Qt framework to define an enumeration compatible with Qt + introspection engine ;
    • +
    +
    + -- Section List of values : +
      +
    • Column � Key � : value key ;
    • +
    • Column � Value � : value ;
    • +
    • Button � Delete � : remove value from the list.
    • +
    +
    + -- Section List of meta-data : meta-data provides a way to add and manage + extra-parameters and are available in C++ source code (QxOrm introspection engine) and Javascript engine to customize QxEntityEditor export + process. +
      +
    • Column � Key � : meta-data key ;
    • +
    • Column � Value � : meta-data value ;
    • +
    • Button � Delete � : remove meta-data from the list.
    • +
    +
    +
    +

    Colors associated to + an enumeration

    +
    + Enumeration colors can be defined via right-click contextual menu Define enumeration + colors : +

    + Enumeration colors +

    + Note : items colors in the entities diagram can be defined at several levels (from the + more global level to the more specific level) : + +
    +
    +
    +

    Manage namespaces

    +
    + A namespace in QxEntityEditor application is used to group several items (entities and + enumerations) in a same area. + From a database point of view, a namespace is generally a schema. + In C++ source code, a namespace defines where classes and functions are located (to group them). +

    + Namespaces are used by the following QxEntityEditor feature : Organize diagram layout (to group items automatically). +

    + The QxEntityEditor application treeview (which displays all loaded items in a project) provides + some menus to manage namespaces : +

    + Namespace menu +

    +

    Rename a + namespace

    +
    + To rename a namespace, just do a right-click on project treeview, then contextual menu + Move entities to another namespace : +

    + Namespace rename +

    +
    +

    Manage colors of a namespace

    +
    + To define items colors at a namespace level, just do a right-click on project treeview, then + contextual menu Define colors by namespace : +

    + Namespace colors +

    + The left part of this screen displays all namespaces available in *.qxee + project.
    + The right part of this screen provides a way to manage colors for all items located in the + selected namespace. + It is also possible to define a background color (generally a light color) to group all items + in entities diagram. +

    + Note : items colors in the entities diagram can be defined at several levels (from the + more global level to the more specific level) : + +
    +
    +
    +

    Manage + post-it/comments

    +
    + A comment (or post-it) in QxEntityEditor application is a tag displayed on entities diagram to + provide some details about data model or *.qxee project. + A post-it can be moved everywhere in the entities diagram. + A post-it has a title and a free text area. + It is possible to write some text using HTML format to add colors, put text in bold, italic, + etc... +

    + QxEntityEditor application provides features to create, modify, delete and clone + comment/post-it. +

    + Post-it / comments +

    +

    Add/modify/delete/clone a post-it

    +
    + Add, modify, delete and clone a post-it is done via equivalent menu for an entity (just replace entity word by comment).
    + So QxEntityEditor application provides exactly same menus as an + entity to manage post-it. +

    +
    +

    Colors associated + to a post-it

    +
    + Post-it colors can be defined via right-click contextual menu Define comment colors : +

    + Post-it colors +

    + Note : items colors in the entities diagram can be defined at several levels (from the + more global level to the more specific level) : + +
    +
    +
    +

    Organize + diagram layout

    +
    + A feature is available in QxEntityEditor application to group automatically all items in + entities diagram depending on namespace, and depending on relationships between entities : main + menu View >> Organize diagram layout. + This feature can be useful for example to organize entities diagram after executing an import process. +

    + Organize diagram layout +

    +
    +

    Project historic + (tag project state)

    +
    + A QxEntityEditor project can have several versions/historics : main menu Actions >> Tag + project state. +

    + Each project historic changes project items version number (entities, properties, relationships, + etc...). + Version number is used to manage ascendant compatibility with + serialization engine of QxOrm library. + Project historic is also used by database DDL SQL export + plugin : manage database schema evolution (add a table, remove a column, add an index, + etc...). +

    + Tag project state tab 1 +

    + The first tab of this historic screen provides a way to create a new historic title (for example + : date/time, project version number, etc...) and a comment in free text. +

    + Tag project state tab 2 +

    + The second tab of this historic screen displays the list of all project historics already saved. + This list of historics provides buttons to restore a project state in several formats (XML, JSON + or QxEntityEditor *.qxee project file). +

    +
    +

    C++ source code + preview

    +
    + It is possible to display a preview of generated C++ source code (header *.h and source + *.cpp files) for the selected item in entities diagram (entity or enumeration). + This C++ source code can be generated by C++ export plugin of + QxEntityEditor. + C++ preview is accessible after selecting an item in entities diagram, then go to C++ + preview tab : +

    + C++ preview +

    +
    +

    Naming convention + (snake_case, camelCase)

    +
    + QxEntityEditor application provides a feature to apply quickly a naming convention on all + project items (entities, properties, relationships, etc...). + This feature keeps (doesn't break) mapping with database. This feature is available via main + menu : Naming convention. + 3 styles of naming convention are provided : snake_case, camelCase and + PascalCase. +

    + Naming convention +

    + Note : this feature can be useful for example after executing an import process to harmonize generated C++ source code. +

    +
    +

    List of available + plugins

    +
    + QxEntityEditor application is based on plugins to manage import/export process. + A list of available plugins with version number and description is available via main menu : + Help >> About plugins. +

    + List of plugins +

    +
    +
    + +

    Import plugins

    +
    + All import process in QxEntityEditor application are accessible via main menu Import : +

    + Import plugins +

    +

    Import + from a directory linked to a Source Control manager (Git, Perforce, CVS, etc.)

    +
    + A QxEntityEditor project (*.qxee file) can be managed by a developers team : the source + code of a QxEntityEditor project can be exported/imported (manually or using command line) with + QxEESourceControlExport and QxEESourceControlImport plugins.
    +
    + Associated with a Source Control manager (Git, Perforce, CVS, etc.), these 2 plugins provide : +
      +
    • several people can work simultaneously on the same project ;
    • +
    • a project can easily be versioned in the source code manager ;
    • +
    • each element of a project can be compared (difference between 2 versions of the same + entity for example).
    • +
    + Here is a use case of QxEESourceControlExport and QxEESourceControlImport plugins, with 2 + developers named 'dev A' and 'dev B' (this example can be extended to X developers) : +
      +
    • dev A and dev B work on a same QxEntityEditor project (*.qxee file) ;
    • +
    • dev A creates/changes/deletes some entities in QxEntityEditor application ;
    • +
    • dev B creates/changes/deletes other entities in QxEntityEditor application ;
    • +
    • dev A and dev B export the *.qxee project file with the QxEESourceControlExport plugin + (manually or using command line) ;
    • +
    • once the project is exported to a directory, dev A and dev B check-in (or submit) + all generated JSON files to the Source Control manager (Git, Perforce, CVS, etc.) on their + own branch ;
    • +
    • from the Source Control manager, dev A and dev B merge (or integrate) their own + branch to the development branch DEV (or MAIN or MASTER or LATEST); Note : even if + there are conflicts to resolve, they will be easy to correct because the JSON format is + easily readable ;
    • +
    • dev A and dev B can now get latest files from Source Control manager from DEV branch (or + MAIN or MASTER or LATEST) : these JSON files contain both modifications from dev A and + dev B ;
    • +
    • dev A and dev B can import these JSON files into the QxEntityEditor application using the + import plugin QxEESourceControlImport + (manually or using command line).
    • +
    + Import plugin settings are accessible from the main menu Import >> Import from Source + Control repository (Git, Perforce, CVS, etc.) : +

    + Source Control import plugin +

    + The parameter to enter corresponds to the JSON file present at the root of the directory where + the *.qxee project was exported.
    + This JSON file is named <project_name>.qxee.export.json, for example with + the qxBlog sample project : qxBlog.qxee.export.json. +

    + Source Control output directory +

    + Warning : an import deletes all elements of the current project (entities, properties, + relationships, etc.). +

    + Note : It is possible to start QxEntityEditor from the command line to automatically load + a directory associated with a source code manager when the application starts. +

    + Command line example :
    +
    + +
    QxEntityEditor --project="<path_to_your_qxee_project_file>" --plugin=QxEESourceControlImport --QxEESourceControlImport_path="<path_to_the_root_source_control_json_file>"
    +
    +
    +

    +
    +

    Import project based + on JSON file

    +
    + + Note : + to work with a source code manager (Git, Perforce, CVS, etc.), it is recommended to use QxEESourceControlExport and QxEESourceControlImport plugins (instead of + creating a single file).
    +
    + A QxEntityEditor project can be managed with a text file in JSON format : main menu Import + >> Import from JSON file.
    + To know the JSON structure of a *.qxee project, it is recommended to do a first export process to JSON file. +

    + Import JSON +

    + The JSON import screen is simple and has only 1 field to define : the JSON file to import. +

    + Warning : a JSON import process removes all items from current project (entities, + properties, relationships, etc...). +

    + Note : QxEntityEditor application can be executed with a command-line to load + automatically a JSON file at startup. +

    + Here is a command-line example :
    +
    + +
    QxEntityEditor --project="<path_to_your_qxee_project_file>" --plugin=QxEEJsonImport --QxEEJsonImport_file="<path_to_your_json_file>"
    +
    +
    +
    + Here is a JSON file example (deployed in ./samples/qxBlog.json directory of + QxEntityEditor package) :
    +
    +
    +{
    +    "app_version": 0,
    +    "description": "",
    +    "dt_creation": "20131206221737",
    +    "dt_modification": "20131206221737",
    +    "id": 1,
    +    "list_all_entities": [
    +        {
    +            "app_version": 0,
    +            "description": "",
    +            "dt_creation": "20131206221810",
    +            "dt_modification": "20140617214144",
    +            "id": 1,
    +            "is_abstract": false,
    +            "is_read_only": false,
    +            "key": "",
    +            "list_functions": null,
    +            "list_functions_static": null,
    +            "list_meta_data": null,
    +            "list_properties": [
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": false,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221810",
    +                    "dt_modification": "20140617214144",
    +                    "entity_id": {
    +                        "id": 1
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 1,
    +                    "is_index": true,
    +                    "is_obsolete": false,
    +                    "is_primary_key": true,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "author_id",
    +                    "order_level": 0,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221810",
    +                        "dt_modification": "20140617214144",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 1,
    +                        "list_meta_data": null,
    +                        "primitive_type": "long",
    +                        "property_id": {
    +                            "id": 1
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221810",
    +                    "dt_modification": "20140617214144",
    +                    "entity_id": {
    +                        "id": 1
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 2,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "firstname",
    +                    "order_level": 1,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221810",
    +                        "dt_modification": "20140617214144",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 2,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QString",
    +                        "property_id": {
    +                            "id": 2
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221810",
    +                    "dt_modification": "20140617214144",
    +                    "entity_id": {
    +                        "id": 1
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 3,
    +                    "is_index": true,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "lastname",
    +                    "order_level": 2,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221810",
    +                        "dt_modification": "20140617214144",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 3,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QString",
    +                        "property_id": {
    +                            "id": 3
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221810",
    +                    "dt_modification": "20140617214144",
    +                    "entity_id": {
    +                        "id": 1
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 4,
    +                    "is_index": true,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "birthdate",
    +                    "order_level": 3,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221810",
    +                        "dt_modification": "20140617214144",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 4,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QDateTime",
    +                        "property_id": {
    +                            "id": 4
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221810",
    +                    "dt_modification": "20140617214144",
    +                    "entity_id": {
    +                        "id": 1
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 5,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "sex",
    +                    "order_level": 4,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221810",
    +                        "dt_modification": "20140617214144",
    +                        "entity_id": null,
    +                        "enumeration_id": {
    +                            "id": 1
    +                        },
    +                        "id": 5,
    +                        "list_meta_data": null,
    +                        "primitive_type": "sex::enum_sex",
    +                        "property_id": {
    +                            "id": 5
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20140617214144",
    +                    "entity_id": {
    +                        "id": 1
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 18,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "list_of_blog",
    +                    "order_level": 17,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "qx::QxCollection",
    +                        "decoration": "boost::shared_ptr",
    +                        "default_value": "",
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214144",
    +                        "entity_id": {
    +                            "id": 4
    +                        },
    +                        "enumeration_id": null,
    +                        "id": 18,
    +                        "list_meta_data": null,
    +                        "primitive_type": "",
    +                        "property_id": {
    +                            "id": 18
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": {
    +                        "app_version": 0,
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214144",
    +                        "extra_table": "",
    +                        "foreign_key": "author",
    +                        "foreign_key_owner": "",
    +                        "id": 2,
    +                        "inverse_property_id": {
    +                            "id": 15
    +                        },
    +                        "list_meta_data": null,
    +                        "property_id": {
    +                            "id": 18
    +                        },
    +                        "type_relation": "one-to-many",
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                }
    +            ],
    +            "name": "author",
    +            "namespace": "",
    +            "parent_id": null,
    +            "project_id": {
    +                "id": 1
    +            },
    +            "project_version": 0,
    +            "property_id": {
    +                "accessibility": "protected",
    +                "allow_null": false,
    +                "app_version": 0,
    +                "column_name": "",
    +                "description": "",
    +                "dt_creation": "20131206221810",
    +                "dt_modification": "20140617214144",
    +                "entity_id": {
    +                    "id": 1
    +                },
    +                "force_sql_alias": "",
    +                "force_sql_type": "",
    +                "format": "",
    +                "get_method": "",
    +                "id": 1,
    +                "is_index": true,
    +                "is_obsolete": false,
    +                "is_primary_key": true,
    +                "is_read_only": false,
    +                "is_serializable": true,
    +                "is_transient": false,
    +                "is_unique": false,
    +                "key": "",
    +                "list_meta_data": null,
    +                "max_length": "",
    +                "max_value": "",
    +                "min_length": "",
    +                "min_value": "",
    +                "name": "author_id",
    +                "order_level": 0,
    +                "project_version": 0,
    +                "property_type_id": {
    +                    "app_version": 0,
    +                    "collection": "",
    +                    "decoration": "",
    +                    "default_value": "",
    +                    "dt_creation": "20131206221810",
    +                    "dt_modification": "20140617214144",
    +                    "entity_id": null,
    +                    "enumeration_id": null,
    +                    "id": 1,
    +                    "list_meta_data": null,
    +                    "primitive_type": "long",
    +                    "property_id": {
    +                        "id": 1
    +                    },
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0
    +                },
    +                "qt_property": "",
    +                "reg_exp": "",
    +                "relation_id": null,
    +                "set_method": "",
    +                "user_id_creation": 0,
    +                "user_id_modification": 0,
    +                "version": 0,
    +                "write_accessors": true
    +            },
    +            "soft_delete_column": "",
    +            "table_name": "t_author",
    +            "trigger_after_delete": false,
    +            "trigger_after_fetch": false,
    +            "trigger_after_insert": false,
    +            "trigger_after_update": false,
    +            "trigger_before_delete": false,
    +            "trigger_before_fetch": false,
    +            "trigger_before_insert": false,
    +            "trigger_before_update": false,
    +            "user_id_creation": 0,
    +            "user_id_modification": 0,
    +            "validator_method": "",
    +            "version": 0
    +        },
    +        {
    +            "app_version": 0,
    +            "description": "",
    +            "dt_creation": "20131206222031",
    +            "dt_modification": "20140617214043",
    +            "id": 4,
    +            "is_abstract": false,
    +            "is_read_only": false,
    +            "key": "",
    +            "list_functions": null,
    +            "list_functions_static": null,
    +            "list_meta_data": null,
    +            "list_properties": [
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": false,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20140617214043",
    +                    "entity_id": {
    +                        "id": 4
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 12,
    +                    "is_index": true,
    +                    "is_obsolete": false,
    +                    "is_primary_key": true,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "blog_id",
    +                    "order_level": 11,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214043",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 12,
    +                        "list_meta_data": null,
    +                        "primitive_type": "long",
    +                        "property_id": {
    +                            "id": 12
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": false,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20140617214043",
    +                    "entity_id": {
    +                        "id": 4
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 13,
    +                    "is_index": true,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "title",
    +                    "order_level": 12,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214043",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 13,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QString",
    +                        "property_id": {
    +                            "id": 13
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20140617214043",
    +                    "entity_id": {
    +                        "id": 4
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 14,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "text",
    +                    "order_level": 13,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214043",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 14,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QString",
    +                        "property_id": {
    +                            "id": 14
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20140617214043",
    +                    "entity_id": {
    +                        "id": 4
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 15,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "author",
    +                    "order_level": 14,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "qx::QxCollection",
    +                        "decoration": "boost::shared_ptr",
    +                        "default_value": "",
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214043",
    +                        "entity_id": {
    +                            "id": 1
    +                        },
    +                        "enumeration_id": null,
    +                        "id": 15,
    +                        "list_meta_data": null,
    +                        "primitive_type": "",
    +                        "property_id": {
    +                            "id": 15
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": {
    +                        "app_version": 0,
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214043",
    +                        "extra_table": "",
    +                        "foreign_key": "",
    +                        "foreign_key_owner": "",
    +                        "id": 1,
    +                        "inverse_property_id": {
    +                            "id": 18
    +                        },
    +                        "list_meta_data": null,
    +                        "property_id": {
    +                            "id": 15
    +                        },
    +                        "type_relation": "many-to-one",
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20140617214043",
    +                    "entity_id": {
    +                        "id": 4
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 16,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "list_of_comment",
    +                    "order_level": 15,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "qx::QxCollection",
    +                        "decoration": "boost::shared_ptr",
    +                        "default_value": "",
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214043",
    +                        "entity_id": {
    +                            "id": 3
    +                        },
    +                        "enumeration_id": null,
    +                        "id": 16,
    +                        "list_meta_data": null,
    +                        "primitive_type": "",
    +                        "property_id": {
    +                            "id": 16
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": {
    +                        "app_version": 0,
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214043",
    +                        "extra_table": "",
    +                        "foreign_key": "blog_id",
    +                        "foreign_key_owner": "",
    +                        "id": 3,
    +                        "inverse_property_id": {
    +                            "id": 19
    +                        },
    +                        "list_meta_data": null,
    +                        "property_id": {
    +                            "id": 16
    +                        },
    +                        "type_relation": "one-to-many",
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20140617214043",
    +                    "entity_id": {
    +                        "id": 4
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 17,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "list_of_category",
    +                    "order_level": 16,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "qx::QxCollection",
    +                        "decoration": "boost::shared_ptr",
    +                        "default_value": "",
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214043",
    +                        "entity_id": {
    +                            "id": 2
    +                        },
    +                        "enumeration_id": null,
    +                        "id": 17,
    +                        "list_meta_data": null,
    +                        "primitive_type": "",
    +                        "property_id": {
    +                            "id": 17
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": {
    +                        "app_version": 0,
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214043",
    +                        "extra_table": "t_qxee_blog_category",
    +                        "foreign_key": "blog_id",
    +                        "foreign_key_owner": "category_id",
    +                        "id": 5,
    +                        "inverse_property_id": {
    +                            "id": 20
    +                        },
    +                        "list_meta_data": null,
    +                        "property_id": {
    +                            "id": 17
    +                        },
    +                        "type_relation": "many-to-many",
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                }
    +            ],
    +            "name": "blog",
    +            "namespace": "",
    +            "parent_id": null,
    +            "project_id": {
    +                "id": 1
    +            },
    +            "project_version": 0,
    +            "property_id": {
    +                "accessibility": "protected",
    +                "allow_null": false,
    +                "app_version": 0,
    +                "column_name": "",
    +                "description": "",
    +                "dt_creation": "20131206222031",
    +                "dt_modification": "20140617214043",
    +                "entity_id": {
    +                    "id": 4
    +                },
    +                "force_sql_alias": "",
    +                "force_sql_type": "",
    +                "format": "",
    +                "get_method": "",
    +                "id": 12,
    +                "is_index": true,
    +                "is_obsolete": false,
    +                "is_primary_key": true,
    +                "is_read_only": false,
    +                "is_serializable": true,
    +                "is_transient": false,
    +                "is_unique": false,
    +                "key": "",
    +                "list_meta_data": null,
    +                "max_length": "",
    +                "max_value": "",
    +                "min_length": "",
    +                "min_value": "",
    +                "name": "blog_id",
    +                "order_level": 11,
    +                "project_version": 0,
    +                "property_type_id": {
    +                    "app_version": 0,
    +                    "collection": "",
    +                    "decoration": "",
    +                    "default_value": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20140617214043",
    +                    "entity_id": null,
    +                    "enumeration_id": null,
    +                    "id": 12,
    +                    "list_meta_data": null,
    +                    "primitive_type": "long",
    +                    "property_id": {
    +                        "id": 12
    +                    },
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0
    +                },
    +                "qt_property": "",
    +                "reg_exp": "",
    +                "relation_id": null,
    +                "set_method": "",
    +                "user_id_creation": 0,
    +                "user_id_modification": 0,
    +                "version": 0,
    +                "write_accessors": true
    +            },
    +            "soft_delete_column": "",
    +            "table_name": "t_blog",
    +            "trigger_after_delete": false,
    +            "trigger_after_fetch": false,
    +            "trigger_after_insert": false,
    +            "trigger_after_update": false,
    +            "trigger_before_delete": false,
    +            "trigger_before_fetch": false,
    +            "trigger_before_insert": false,
    +            "trigger_before_update": false,
    +            "user_id_creation": 0,
    +            "user_id_modification": 0,
    +            "validator_method": "",
    +            "version": 0
    +        },
    +        {
    +            "app_version": 0,
    +            "description": "",
    +            "dt_creation": "20131206221933",
    +            "dt_modification": "20140617214115",
    +            "id": 2,
    +            "is_abstract": false,
    +            "is_read_only": false,
    +            "key": "",
    +            "list_functions": null,
    +            "list_functions_static": null,
    +            "list_meta_data": null,
    +            "list_properties": [
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": false,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221933",
    +                    "dt_modification": "20140617214115",
    +                    "entity_id": {
    +                        "id": 2
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 6,
    +                    "is_index": true,
    +                    "is_obsolete": false,
    +                    "is_primary_key": true,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "category_id",
    +                    "order_level": 5,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221933",
    +                        "dt_modification": "20140617214115",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 6,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QString",
    +                        "property_id": {
    +                            "id": 6
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221933",
    +                    "dt_modification": "20140617214115",
    +                    "entity_id": {
    +                        "id": 2
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 7,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "name",
    +                    "order_level": 6,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221933",
    +                        "dt_modification": "20140617214115",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 7,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QString",
    +                        "property_id": {
    +                            "id": 7
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221933",
    +                    "dt_modification": "20140617214115",
    +                    "entity_id": {
    +                        "id": 2
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 8,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "description",
    +                    "order_level": 7,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221933",
    +                        "dt_modification": "20140617214115",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 8,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QString",
    +                        "property_id": {
    +                            "id": 8
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20140617214115",
    +                    "entity_id": {
    +                        "id": 2
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 20,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "list_of_blog",
    +                    "order_level": 19,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "qx::QxCollection",
    +                        "decoration": "boost::shared_ptr",
    +                        "default_value": "",
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214115",
    +                        "entity_id": {
    +                            "id": 4
    +                        },
    +                        "enumeration_id": null,
    +                        "id": 20,
    +                        "list_meta_data": null,
    +                        "primitive_type": "",
    +                        "property_id": {
    +                            "id": 20
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": {
    +                        "app_version": 0,
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214115",
    +                        "extra_table": "t_qxee_blog_category",
    +                        "foreign_key": "category_id",
    +                        "foreign_key_owner": "blog_id",
    +                        "id": 6,
    +                        "inverse_property_id": {
    +                            "id": 17
    +                        },
    +                        "list_meta_data": null,
    +                        "property_id": {
    +                            "id": 20
    +                        },
    +                        "type_relation": "many-to-many",
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                }
    +            ],
    +            "name": "category",
    +            "namespace": "",
    +            "parent_id": null,
    +            "project_id": {
    +                "id": 1
    +            },
    +            "project_version": 0,
    +            "property_id": {
    +                "accessibility": "protected",
    +                "allow_null": false,
    +                "app_version": 0,
    +                "column_name": "",
    +                "description": "",
    +                "dt_creation": "20131206221933",
    +                "dt_modification": "20140617214115",
    +                "entity_id": {
    +                    "id": 2
    +                },
    +                "force_sql_alias": "",
    +                "force_sql_type": "",
    +                "format": "",
    +                "get_method": "",
    +                "id": 6,
    +                "is_index": true,
    +                "is_obsolete": false,
    +                "is_primary_key": true,
    +                "is_read_only": false,
    +                "is_serializable": true,
    +                "is_transient": false,
    +                "is_unique": false,
    +                "key": "",
    +                "list_meta_data": null,
    +                "max_length": "",
    +                "max_value": "",
    +                "min_length": "",
    +                "min_value": "",
    +                "name": "category_id",
    +                "order_level": 5,
    +                "project_version": 0,
    +                "property_type_id": {
    +                    "app_version": 0,
    +                    "collection": "",
    +                    "decoration": "",
    +                    "default_value": "",
    +                    "dt_creation": "20131206221933",
    +                    "dt_modification": "20140617214115",
    +                    "entity_id": null,
    +                    "enumeration_id": null,
    +                    "id": 6,
    +                    "list_meta_data": null,
    +                    "primitive_type": "QString",
    +                    "property_id": {
    +                        "id": 6
    +                    },
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0
    +                },
    +                "qt_property": "",
    +                "reg_exp": "",
    +                "relation_id": null,
    +                "set_method": "",
    +                "user_id_creation": 0,
    +                "user_id_modification": 0,
    +                "version": 0,
    +                "write_accessors": true
    +            },
    +            "soft_delete_column": "",
    +            "table_name": "t_category",
    +            "trigger_after_delete": false,
    +            "trigger_after_fetch": false,
    +            "trigger_after_insert": false,
    +            "trigger_after_update": false,
    +            "trigger_before_delete": false,
    +            "trigger_before_fetch": false,
    +            "trigger_before_insert": false,
    +            "trigger_before_update": false,
    +            "user_id_creation": 0,
    +            "user_id_modification": 0,
    +            "validator_method": "",
    +            "version": 0
    +        },
    +        {
    +            "app_version": 0,
    +            "description": "",
    +            "dt_creation": "20131206221951",
    +            "dt_modification": "20131206221951",
    +            "id": 3,
    +            "is_abstract": false,
    +            "is_read_only": false,
    +            "key": "",
    +            "list_functions": null,
    +            "list_functions_static": null,
    +            "list_meta_data": null,
    +            "list_properties": [
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": false,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221951",
    +                    "dt_modification": "20131206221951",
    +                    "entity_id": {
    +                        "id": 3
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 9,
    +                    "is_index": true,
    +                    "is_obsolete": false,
    +                    "is_primary_key": true,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "comment_id",
    +                    "order_level": 8,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221951",
    +                        "dt_modification": "20131206221951",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 9,
    +                        "list_meta_data": null,
    +                        "primitive_type": "long",
    +                        "property_id": {
    +                            "id": 9
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221951",
    +                    "dt_modification": "20131206221951",
    +                    "entity_id": {
    +                        "id": 3
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 10,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "title",
    +                    "order_level": 9,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221951",
    +                        "dt_modification": "20131206221951",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 10,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QString",
    +                        "property_id": {
    +                            "id": 10
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221951",
    +                    "dt_modification": "20131206221951",
    +                    "entity_id": {
    +                        "id": 3
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 11,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "text",
    +                    "order_level": 10,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221951",
    +                        "dt_modification": "20131206221951",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 11,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QString",
    +                        "property_id": {
    +                            "id": 11
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20131206222031",
    +                    "entity_id": {
    +                        "id": 3
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 19,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "blog_id",
    +                    "order_level": 18,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "qx::QxCollection",
    +                        "decoration": "boost::shared_ptr",
    +                        "default_value": "",
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20131206222031",
    +                        "entity_id": {
    +                            "id": 4
    +                        },
    +                        "enumeration_id": null,
    +                        "id": 19,
    +                        "list_meta_data": null,
    +                        "primitive_type": "",
    +                        "property_id": {
    +                            "id": 19
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": {
    +                        "app_version": 0,
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20131206222031",
    +                        "extra_table": "",
    +                        "foreign_key": "",
    +                        "foreign_key_owner": "",
    +                        "id": 4,
    +                        "inverse_property_id": {
    +                            "id": 16
    +                        },
    +                        "list_meta_data": null,
    +                        "property_id": {
    +                            "id": 19
    +                        },
    +                        "type_relation": "many-to-one",
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                }
    +            ],
    +            "name": "comment",
    +            "namespace": "",
    +            "parent_id": null,
    +            "project_id": {
    +                "id": 1
    +            },
    +            "project_version": 0,
    +            "property_id": {
    +                "accessibility": "protected",
    +                "allow_null": false,
    +                "app_version": 0,
    +                "column_name": "",
    +                "description": "",
    +                "dt_creation": "20131206221951",
    +                "dt_modification": "20131206221951",
    +                "entity_id": {
    +                    "id": 3
    +                },
    +                "force_sql_alias": "",
    +                "force_sql_type": "",
    +                "format": "",
    +                "get_method": "",
    +                "id": 9,
    +                "is_index": true,
    +                "is_obsolete": false,
    +                "is_primary_key": true,
    +                "is_read_only": false,
    +                "is_serializable": true,
    +                "is_transient": false,
    +                "is_unique": false,
    +                "key": "",
    +                "list_meta_data": null,
    +                "max_length": "",
    +                "max_value": "",
    +                "min_length": "",
    +                "min_value": "",
    +                "name": "comment_id",
    +                "order_level": 8,
    +                "project_version": 0,
    +                "property_type_id": {
    +                    "app_version": 0,
    +                    "collection": "",
    +                    "decoration": "",
    +                    "default_value": "",
    +                    "dt_creation": "20131206221951",
    +                    "dt_modification": "20131206221951",
    +                    "entity_id": null,
    +                    "enumeration_id": null,
    +                    "id": 9,
    +                    "list_meta_data": null,
    +                    "primitive_type": "long",
    +                    "property_id": {
    +                        "id": 9
    +                    },
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0
    +                },
    +                "qt_property": "",
    +                "reg_exp": "",
    +                "relation_id": null,
    +                "set_method": "",
    +                "user_id_creation": 0,
    +                "user_id_modification": 0,
    +                "version": 0,
    +                "write_accessors": true
    +            },
    +            "soft_delete_column": "",
    +            "table_name": "t_comment",
    +            "trigger_after_delete": false,
    +            "trigger_after_fetch": false,
    +            "trigger_after_insert": false,
    +            "trigger_after_update": false,
    +            "trigger_before_delete": false,
    +            "trigger_before_fetch": false,
    +            "trigger_before_insert": false,
    +            "trigger_before_update": false,
    +            "user_id_creation": 0,
    +            "user_id_modification": 0,
    +            "validator_method": "",
    +            "version": 0
    +        }
    +    ],
    +    "list_comments": [
    +        {
    +            "app_version": 0,
    +            "comment_text": "- Design the entity model in few minutes\n\n- Generate C++ persistent classes automatically\n\n- Generate DDL SQL script automatically\n\n- Manage schema evolution for each project version\n\n- Compile C++ native code everywhere : Windows, Linux, MacOSX, Android, iOS, etc...\n\n- Transfer your data model over network and create quickly client/server applications",
    +            "comment_text_html": false,
    +            "comment_title": "qxBlog project",
    +            "dt_creation": "20131206222130",
    +            "dt_modification": "20140107093142",
    +            "id": 1,
    +            "list_meta_data": null,
    +            "user_id_creation": 0,
    +            "user_id_modification": 0
    +        }
    +    ],
    +    "list_enumerations": [
    +        {
    +            "app_version": 0,
    +            "description": "",
    +            "dt_creation": "20131206221840",
    +            "dt_modification": "20131210205552",
    +            "id": 1,
    +            "key": "",
    +            "list_meta_data": null,
    +            "list_of_key_value": {
    +                "female": 2,
    +                "male": 1,
    +                "unknown": 3
    +            },
    +            "name": "sex",
    +            "namespace": "",
    +            "project_id": {
    +                "id": 1
    +            },
    +            "project_version": 0,
    +            "qt_enum": false,
    +            "user_id_creation": 0,
    +            "user_id_modification": 0,
    +            "version": 0
    +        }
    +    ],
    +    "list_groups": null,
    +    "list_meta_data": null,
    +    "list_namespaces": null,
    +    "location": "C:/Temp/qxee",
    +    "name": "qxBlog",
    +    "project_guid": "{16335d56-73ac-48cf-8fcd-f74cc7d97201}",
    +    "project_parameters": {
    +        "app_version": 0,
    +        "default_entity_namespace": "",
    +        "default_primary_key_type": "long",
    +        "default_property_type": "QString",
    +        "dt_creation": "20131206221737",
    +        "dt_modification": "20131206221737",
    +        "id": 1,
    +        "list_meta_data": null,
    +        "lst_plugin_script": {
    +        },
    +        "primary_key_prefix": "",
    +        "primary_key_suffix": "_id",
    +        "project_id": {
    +            "id": 1
    +        },
    +        "table_name_prefix": "t_",
    +        "table_name_suffix": "",
    +        "user_id_creation": 0,
    +        "user_id_modification": 0
    +    },
    +    "user_id_creation": 0,
    +    "user_id_modification": 0,
    +    "version": 0
    +}
    +
    +
    +

    +
    +

    Import from MySQL + or MariaDB database

    +
    + A MySQL (or MariaDB) database structure can be imported in QxEntityEditor application via main + menu : Import >> Import from MySQL (or MariaDB) database : +

    + Import MySQL or MariaDB +

    + This import parameters screen is divided in several sections : +

    + -- Section MySQL (or MariaDB) database connection : +
      +
    • Field � Database server address (IP) � : the MySQL server name or IP address ; +
    • +
    • Field � Database port � : MySQL server port number to connect to database ;
    • +
    • Field � Database name � : database name to import ;
    • +
    • Field � Database login � : user name to connect to database ;
    • +
    • Field � Database password � : password to connect to database ;
    • +
    • Button � Connect to database � : if an error occurred, a warning message is + displayed, otherwise all database tables and columns are displayed in � Database items + to import to QxEntityEditor project � section (treeview).
    • +
    +
    + -- Section Import settings : +
      +
    • Field � Namespace � : namespace used to group all imported + items in current project ;
    • +
    • Field � Delete all entities in the namespace before importing � : remove all + entities from above namespace before running import from database process ;
    • +
    • Field � Import tables/columns comment to entities/properties description � : + import tables and columns description defined in database ;
    • +
    • Field � Add boost::optional<T> decoration if a column definition allows NULL + value � : add automatically boost::optional + decoration to manage NULL value ;
    • +
    • Field � Import columns default value � : import default values defined in database + columns ;
    • +
    • Field � Organize diagram layout after import process � : run automatically the organize entities diagram feature after executing + import process.
    • +
    +
    + -- Section Relationship import settings : +
      +
    • Field � Decoration � : default relationship C++ type decoration (for + relationships, QxOrm library recommends smart-pointer : boost::shared_ptr, + std::shared_ptr or QSharedPointer) ; +
    • +
    • Field � Collection � : if relationship is a list of (1-n and n-n), + then define C++ list type ;
    • +
    +
    + -- Section Mapping database SQL type to C++ type : QxEntityEditor provides a + default mapping SQL type / C++ type. + You can use this list to override default behaviour : +
      +
    • Column � SQL type � : SQL type to map. SQL type search is case insensitive, and is + applied on first characters (STARTS WITH behaviour) : for example, varchar + matches VARCHAR(255) ;
    • +
    • Column � C++ type � : C++ type to use when SQL type is found ;
    • +
    • Button � Delete � : remove SQL type / C++ type mapping.
    • +
    +
    + -- Section Database items to import to QxEntityEditor project : when + QxEntityEditor is connected to database, all items (tables, columns, primary keys, + relationships, views, etc...) are displayed in this section. + By default, there is no item selected. + You can select all items to import with 1 click on the treeview root item. + It is also possible to select quickly items from a specific schema. + Once items to import are selected, the � Ok � button becomes enabled to run import + process. +


    + Note : all import parameters (database connection, import settings and relationships, SQL + type / C++ type mapping, etc...) are automatically saved when closing the import screen. + All import parameters are automatically re-loaded when opening the import screen. +

    + Other note : a QxEntityEditor video is available to show how + to import the MySQL database sample named sakila. +

    +
    +

    Import from + PostgreSQL database

    +
    + A PostgreSQL database structure can be imported in QxEntityEditor application via main menu : + Import >> Import from PostgreSQL database : +

    + Import PostgreSQL +

    + This import parameters screen is divided in several sections : +

    + -- Section PostgreSQL database connection : +
      +
    • Field � Database server address (IP) � : the PostgreSQL server name or IP address + ;
    • +
    • Field � Database port � : PostgreSQL server port number to connect to database ; +
    • +
    • Field � Database name � : database name to import ;
    • +
    • Field � Database login � : user name to connect to database ;
    • +
    • Field � Database password � : password to connect to database ;
    • +
    • Button � Connect to database � : if an error occurred, a warning message is + displayed, otherwise all database tables and columns are displayed in � Database items + to import to QxEntityEditor project � section (treeview).
    • +
    +
    + -- Section Import settings : +
      +
    • Field � Namespace � : namespace used to group all imported + items in current project ;
    • +
    • Field � Delete all entities in the namespace before importing � : remove all + entities from above namespace before running import from database process ;
    • +
    • Field � Import tables/columns comment to entities/properties description � : + import tables and columns description defined in database ;
    • +
    • Field � Add boost::optional<T> decoration if a column definition allows NULL + value � : add automatically boost::optional + decoration to manage NULL value ;
    • +
    • Field � Import columns default value � : import default values defined in database + columns ;
    • +
    • Field � Organize diagram layout after import process � : run automatically the organize entities diagram feature after executing + import process.
    • +
    +
    + -- Section Relationship import settings : +
      +
    • Field � Decoration � : default relationship C++ type decoration (for + relationships, QxOrm library recommends smart-pointer : boost::shared_ptr, + std::shared_ptr or QSharedPointer) ; +
    • +
    • Field � Collection � : if relationship is a list of (1-n and n-n), + then define C++ list type ;
    • +
    +
    + -- Section Mapping database SQL type to C++ type : QxEntityEditor provides a + default mapping SQL type / C++ type. + You can use this list to override default behaviour : +
      +
    • Column � SQL type � : SQL type to map. SQL type search is case insensitive, and is + applied on first characters (STARTS WITH behaviour) : for example, varchar + matches VARCHAR(255) ;
    • +
    • Column � C++ type � : C++ type to use when SQL type is found ;
    • +
    • Button � Delete � : remove SQL type / C++ type mapping.
    • +
    +
    + -- Section Database items to import to QxEntityEditor project : when + QxEntityEditor is connected to database, all items (tables, columns, primary keys, + relationships, views, etc...) are displayed in this section. + By default, there is no item selected. + You can select all items to import with 1 click on the treeview root item. + It is also possible to select quickly items from a specific schema. + Once items to import are selected, the � Ok � button becomes enabled to run import + process. +


    + Note : all import parameters (database connection, import settings and relationships, SQL + type / C++ type mapping, etc...) are automatically saved when closing the import screen. + All import parameters are automatically re-loaded when opening the import screen. +

    + Other note : a QxEntityEditor video is available to show how + to import the MySQL database sample named sakila. +

    +
    +

    Import from SQLite + database

    +
    + A SQLite database structure can be imported in QxEntityEditor application via main menu : + Import >> Import from SQLite database : +

    + Import SQLite +

    + This import parameters screen is divided in several sections : +

    + -- Section Database connection : +
      +
    • Field � Database path � : SQLite database file path ;
    • +
    • Button � Connect to database � : if an error occurred, a warning message is + displayed, otherwise all database tables and columns are displayed in � Database items + to import to QxEntityEditor project � section (treeview).
    • +
    +
    + -- Section Import settings : +
      +
    • Field � Namespace � : namespace used to group all imported + items in current project ;
    • +
    • Field � Delete all entities in the namespace before importing � : remove all + entities from above namespace before running import from database process ;
    • +
    • Field � Import tables/columns comment to entities/properties description � : + import tables and columns description defined in database ;
    • +
    • Field � Add boost::optional<T> decoration if a column definition allows NULL + value � : add automatically boost::optional + decoration to manage NULL value ;
    • +
    • Field � Import columns default value � : import default values defined in database + columns ;
    • +
    • Field � Organize diagram layout after import process � : run automatically the organize entities diagram feature after executing + import process.
    • +
    +
    + -- Section Relationship import settings : +
      +
    • Field � Decoration � : default relationship C++ type decoration (for + relationships, QxOrm library recommends smart-pointer : boost::shared_ptr, + std::shared_ptr or QSharedPointer) ; +
    • +
    • Field � Collection � : if relationship is a list of (1-n and n-n), + then define C++ list type ;
    • +
    +
    + -- Section Mapping database SQL type to C++ type : QxEntityEditor provides a + default mapping SQL type / C++ type. + You can use this list to override default behaviour : +
      +
    • Column � SQL type � : SQL type to map. SQL type search is case insensitive, and is + applied on first characters (STARTS WITH behaviour) : for example, varchar + matches VARCHAR(255) ;
    • +
    • Column � C++ type � : C++ type to use when SQL type is found ;
    • +
    • Button � Delete � : remove SQL type / C++ type mapping.
    • +
    +
    + -- Section Database items to import to QxEntityEditor project : when + QxEntityEditor is connected to database, all items (tables, columns, primary keys, + relationships, views, etc...) are displayed in this section. + By default, there is no item selected. + You can select all items to import with 1 click on the treeview root item. + It is also possible to select quickly items from a specific schema. + Once items to import are selected, the � Ok � button becomes enabled to run import + process. +


    + Note : all import parameters (database connection, import settings and relationships, SQL + type / C++ type mapping, etc...) are automatically saved when closing the import screen. + All import parameters are automatically re-loaded when opening the import screen. +

    + Other note : a QxEntityEditor video is available to show how + to import the MySQL database sample named sakila. +

    +
    +

    Import from database + using ODBC driver (Oracle, MS SQL Server, etc.)

    +
    + QxEntityEditor application provides a generic database import using ODBC drivers : main menu + Import >> Import from database using ODBC driver. + This kind of import process requires a DSN (Data Source Name) defined on the environment. + Import by ODBC process is able to manage following databases : Oracle, Microsoft SQL Server, + SQLite, MySQL/MariaDB, PostgreSQL, etc... +

    + Import ODBC +

    + This import parameters screen is divided in several sections : +

    + -- Section Database connection : +
      +
    • Field � DSN or connection string � : DSN (Data Source + Name) defined on the environment to be able to connect to database using an ODBC + driver ;
    • +
    • Field � Login � : user name to connect to database (optional depending on DSN) ; +
    • +
    • Field � Password � : password to connect to database (optional depending on DSN) ; +
    • +
    • Field � Database type � : Generic type is able to connect to all databases + but with some limitations (no relationship, no composite key). + So you should select the real database type before connecting to database (Oracle, + Microsoft SQL Server, SQLite, MySQL/MariaDB, PostgreSQL) ;
    • +
    • Button � Connect to database � : if an error occurred, a warning message is + displayed, otherwise all database tables and columns are displayed in � Database items + to import to QxEntityEditor project � section (treeview).
    • +
    +
    + -- Section Import settings : +
      +
    • Field � Namespace � : namespace used to group all imported + items in current project ;
    • +
    • Field � Delete all entities in the namespace before importing � : remove all + entities from above namespace before running import from database process ;
    • +
    • Field � Import tables/columns comment to entities/properties description � : + import tables and columns description defined in database ;
    • +
    • Field � Add boost::optional<T> decoration if a column definition allows NULL + value � : add automatically boost::optional + decoration to manage NULL value ;
    • +
    • Field � Import columns default value � : import default values defined in database + columns ;
    • +
    • Field � Organize diagram layout after import process � : run automatically the organize entities diagram feature after executing + import process.
    • +
    +
    + -- Section Relationship import settings : +
      +
    • Field � Decoration � : default relationship C++ type decoration (for + relationships, QxOrm library recommends smart-pointer : boost::shared_ptr, + std::shared_ptr or QSharedPointer) ; +
    • +
    • Field � Collection � : if relationship is a list of (1-n and n-n), + then define C++ list type ;
    • +
    +
    + -- Section Mapping database SQL type to C++ type : QxEntityEditor provides a + default mapping SQL type / C++ type. + You can use this list to override default behaviour : +
      +
    • Column � SQL type � : SQL type to map. SQL type search is case insensitive, and is + applied on first characters (STARTS WITH behaviour) : for example, varchar + matches VARCHAR(255) ;
    • +
    • Column � C++ type � : C++ type to use when SQL type is found ;
    • +
    • Button � Delete � : remove SQL type / C++ type mapping.
    • +
    +
    + -- Section Database items to import to QxEntityEditor project : when + QxEntityEditor is connected to database, all items (tables, columns, primary keys, + relationships, views, etc...) are displayed in this section. + By default, there is no item selected. + You can select all items to import with 1 click on the treeview root item. + It is also possible to select quickly items from a specific schema. + Once items to import are selected, the � Ok � button becomes enabled to run import + process. +


    + Note : all import parameters (database connection, import settings and relationships, SQL + type / C++ type mapping, etc...) are automatically saved when closing the import screen. + All import parameters are automatically re-loaded when opening the import screen. +

    + Other note : a QxEntityEditor video is available to show how + to import the MySQL database sample named sakila. +

    +
    +
    + +

    Export plugins

    +
    + All export process in QxEntityEditor application are accessible via main menu Export : +

    + Export plugins +

    +

    Export to C++ + project

    +
    + C++ export generates a full C++ project ready to be built (using qmake and/or + cmake) which contains all entities/properties/relationships/enumerations registered in + QxOrm context. + All QxOrm library features (persistence, serialization, introspection, etc...) are available for all items inside + generated C++ project. +

    +

    C++ export settings

    +
    + C++ export parameters are accessible via main menu : Tools >> Export to C++ project + (settings). + Export parameters screen is displayed automatically when the first export is launched if no + setting is saved in *.qxee project. + When a C++ export process has already been executed, then export parameters screen is only + accessible via : Tools >> Export to C++ project (settings) : +

    + Export C++ +

    + This export parameters screen is divided in several sections : +

    + -- Section C++ export settings : +
      +
    • Field � C++ project location � : define where generated C++ project is located (output + directory). + You can define in this field a relative path to your *.qxee project file ;
    • +
    • Field � Relative path to QxOrm library � : if enabled, QxOrm library dependency + is managed using a relative path, otherwise it is managed using an absolute path. + You should define an environment variable in QxEntityEditor + application settings (for example : $$(QXORM_DIR)) instead of using this + option ;
    • +
    • Field � Generate a custom directory with custom files for each entity � : if + enabled, generated C++ project contains a custom file per entity. + By default, these custom files are empty and will never be removed or erased by another + export. + These custom files can be used for example to implement validation methods, or trigger functions.
    • +
    • Field � Generate all persistent classes using the PIMPL (Private Implementation) + idiom � : if enabled, all generated C++ persistent classes registered into QxOrm + context will use the C++ PIMPL pattern (or + d-pointer).
    • +
    +
    + -- Section Custom script (Javascript file) to change the default behaviour of the + export process : settings for export custom engine. +
      +
    • Field � Custom script file � : Javascript file location to customize C++ export process. + If empty, no customization is applied (default behaviour of export process). + You can define in this field a relative path to *.qxee project file, for example + ./my_script.js if my_script.js file is in the same directory as + *.qxee file ; +
    • +
    • Button � Debug custom Javascript file � : if a Javascript file is defined, a + click on this button will enable Javascript debugging + mode for the next C++ export process. + Instead of using this button, you can also enable Javascript + debugging mode pressing SHIFT keyboard shortcut when starting an export + process.
    • +
    +
    + -- Section C++ template files : this section defines entities C++ source code + for Header *.h and Source *.cpp files. + Export engine is based on placeholders (@@ACTION@@ format) to replace code by + calculated value. + Placeholder is the most important part to understand how Javascript customization engine works. + Please note that you can define your own custom placeholder + with @@CUSTOM_ prefix, for example : @@CUSTOM_MY_ACTION@@. +
      +
    • Option � No inheritance � : if enabled (default option), C++ class doesn't have + any inheritance (no base class) ;
    • +
    • Option � qx::IxPersistable � : if enabled, each generated C++ class inherit + from qx::IxPersistable interface (which provides a + common interface for persistence actions to database). + Please note that this option increases compilation times to build your C++ project ; +
    • +
    • Option � qx::IxPersistable + QObject � : if enabled, each generated C++ class + inherit from qx::IxPersistable interface and from + QObject Qt class. + Please note that this option increases compilation times to build your C++ project ; +
    • +
    • Option � Custom � : if enabled, you can provide your own C++ template files + (you should begin to copy/past Header *.h and Source *.cpp + from another option to get a list of placeholders to use).
    • +
    +
    +
    +

    Overview of generated C++ project

    +
    + Output directory of generated C++ project looks like : +

    + Export C++ output +

    +
      +
    • � bin � folder is empty by default, and will contain output binary (shared + library *.dll on Windows, *.so on Linux, etc...) ;
    • +
    • � build � folder is empty by default, it can be used to manage CMake configuration files ;
    • +
    • � custom � folder is filled if the � Generate a custom directory with custom + files for each entity � option is enabled. + These custom files can be used for example to implement validation methods, or trigger functions ;
    • +
    • � include � folder contains all classes definitions for entities and + enumerations ;
    • +
    • � src � folder contains all classes implementations for entities and + enumerations ;
    • +
    • � CMakeLists.txt � file can be used to build project with CMake ;
    • +
    • � *.gen.pri � and � *.gen.pro � files can be used to build project with + qmake ; +
    • +
    +
    +
    +

    Build generated C++ project (using qmake or + cmake)

    +
    + Generated C++ project can be built using CMake or qmake.
    + QxOrm library provides some documented compilation options + in QxOrm.pri (and QxOrm.cmake) configuration files (you can keep default compilation + options).
    + QxOrm library user manual has a topic about how to build a + QxOrm project.
    + A tutorial is also available to explain how to setup an + development environment on Windows with MSVC.
    + Once C++ project is built, then output binary is available in the � bin � directory of + exported project (*.dll on Windows, *.so on Linux, etc...). +

    +
    +

    Example : how to use generated C++ project

    +
    + The ./samples/ directory of QxEntityEditor package provides a sample project : + qxBlog.qxee. + This qxBlog.qxee project can be opened in QxEntityEditor application without any valid + license key. + Once C++ project is exported by QxEntityEditor, you can build the project to get a + qxBlog.dll shared library (or qxBlog.so on Linux). + This shared library registers all entities, properties and enumerations in QxOrm context : so all QxOrm library features are available. +

    + The ./samples/ directory of QxEntityEditor package also provides a C++ sample project + which depends on qxBlog.qxee exported project : qxBlogExec.zip. + Once qxBlogExec.zip is unzipped, the readme.txt file in the root folder + contains instructions to build this sample project : +

    +
    +
    +--------------------------------------------
    +-- To build qxBlogExec project with qmake --
    +--------------------------------------------
    +
    +1- Export the 'qxBlog.qxee' project as a C++ project with QxEntityEditor and build it
    +2- Open the 'qxBlogExec.pro' file
    +3- Replace '$$(QXORM_DIR)' by your QxOrm library installation path
    +4- Replace 'C:/Temp/qxee/cpp/' by the path where you have exported the 'qxBlog.qxee' project as a C++ project
    +5- run command line : qmake
    +6- run command line : make debug (or make release)
    +
    +
    +--------------------------------------------
    +-- To build qxBlogExec project with CMake --
    +--------------------------------------------
    +
    +1- Export the 'qxBlog.qxee' project as a C++ project with QxEntityEditor and build it
    +2- Open the 'CMakeLists.txt' file
    +3- Replace '$ENV{QXORM_DIR}' by your QxOrm library installation path
    +4- Replace 'C:/Temp/qxee/cpp/' by the path where you have exported the 'qxBlog.qxee' project as a C++ project
    +5- run cmake or cmake-gui to configure and generate your make files
    +
    +
    +

    + Here is the commented content of main() function (close to qxBlog tutorial) : +

    +
    +
    +int main(int argc, char * argv[])
    +{
    +   // Qt application
    +   QCoreApplication app(argc, argv);
    +   QFile::remove("./qxBlog.sqlite");
    +
    +   // Parameters to connect to database
    +   qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE");
    +   qx::QxSqlDatabase::getSingleton()->setDatabaseName("./qxBlog.sqlite");
    +   qx::QxSqlDatabase::getSingleton()->setHostName("localhost");
    +   qx::QxSqlDatabase::getSingleton()->setUserName("root");
    +   qx::QxSqlDatabase::getSingleton()->setPassword("");
    +
    +   // Only for debug purpose : assert if invalid offset detected fetching a relation
    +   qx::QxSqlDatabase::getSingleton()->setVerifyOffsetRelation(true);
    +
    +   // !!! TO CREATE TABLES, PLEASE USE THE C++ DDL SQL EXPORT PLUGIN OF QXENTITYEDITOR !!!
    +   // Create all tables in database (you should not use 'qx::dao::create_table<T>()' function in a production software, use it just for prototypes or samples)
    +   QSqlError daoError = qx::dao::create_table<author>();
    +   daoError = qx::dao::create_table<comment>();
    +   daoError = qx::dao::create_table<category>();
    +   daoError = qx::dao::create_table<blog>();
    +
    +   // Create a list of 3 author
    +   author_ptr author_1; author_1.reset(new author());
    +   author_ptr author_2; author_2.reset(new author());
    +   author_ptr author_3; author_3.reset(new author());
    +
    +   author_1->setlastname("author_1");
    +   author_1->setsex(sex::male);
    +   author_1->setbirthdate(QDateTime::currentDateTime());
    +   author_2->setlastname("author_2");
    +   author_2->setsex(sex::female);
    +   author_2->setbirthdate(QDateTime::currentDateTime());
    +   author_3->setlastname("author_3");
    +   author_3->setsex(sex::female);
    +   author_3->setbirthdate(QDateTime::currentDateTime());
    +
    +   list_of_author authorX;
    +   authorX.insert(1, author_1);
    +   authorX.insert(2, author_2);
    +   authorX.insert(3, author_3);
    +
    +   // Insert list of 3 author into database
    +   daoError = qx::dao::insert(authorX);
    +   qAssert(qx::dao::count<author>() == 3);
    +
    +   // Clone author n�2 : 'author_id_2'
    +   author_ptr author_clone = qx::clone(* author_2);
    +   qAssert(author_clone->getauthor_id() == author_2->getauthor_id());
    +   qAssert(author_clone->getsex() == sex::female);
    +
    +   // Create a query to fetch only female author : 'author_id_2' and 'author_id_3'
    +   qx::QxSqlQuery query("WHERE t_author.sex = :sex");
    +   query.bind(":sex", sex::female);
    +
    +   list_of_author list_of_female_author;
    +   daoError = qx::dao::fetch_by_query(query, list_of_female_author);
    +   qAssert(list_of_female_author.count() == 2);
    +
    +   // Dump list of female author (xml serialization)
    +   qx::dump(list_of_female_author);
    +
    +   // Create 3 categories
    +   category_ptr category_1 = category_ptr(new category("cat_1"));
    +   category_ptr category_2 = category_ptr(new category("cat_2"));
    +   category_ptr category_3 = category_ptr(new category("cat_3"));
    +
    +   category_1->setname("category_1"); category_1->setdescription("desc_1");
    +   category_2->setname("category_2"); category_2->setdescription("desc_2");
    +   category_3->setname("category_3"); category_3->setdescription("desc_3");
    +
    +   { // Create a scope to destroy temporary connexion to database
    +
    +   // Open a transaction to database
    +   QSqlDatabase db = qx::QxSqlDatabase::getDatabase();
    +   bool bCommit = db.transaction();
    +
    +   // Insert 3 categories into database, use 'db' parameter for the transaction
    +   daoError = qx::dao::insert(category_1, (& db));    bCommit = (bCommit && ! daoError.isValid());
    +   daoError = qx::dao::insert(category_2, (& db));    bCommit = (bCommit && ! daoError.isValid());
    +   daoError = qx::dao::insert(category_3, (& db));    bCommit = (bCommit && ! daoError.isValid());
    +
    +   qAssert(bCommit);
    +   qAssert(category_1->getcategory_id() != "");
    +   qAssert(category_2->getcategory_id() != "");
    +   qAssert(category_3->getcategory_id() != "");
    +
    +   // Terminate transaction => commit or rollback if there is error
    +   if (bCommit) { db.commit(); }
    +   else { db.rollback(); }
    +
    +   } // End of scope : 'db' is destroyed
    +
    +   // Create a blog with the class name (factory)
    +   boost::any blog_any = qx::create("blog");
    +   blog_ptr blog_1;
    +   try { blog_1 = boost::any_cast<blog_ptr>(blog_any); }
    +   catch (...) { blog_1.reset(new blog()); }
    +   blog_1->settext("blog_text_1");
    +   blog_1->settitle("blog_title_1");
    +   blog_1->setauthor(author_1);
    +
    +   // Insert 'blog_1' into database with 'save()' method
    +   daoError = qx::dao::save(blog_1);
    +
    +   // Modify 'blog_1' properties and save into database
    +   blog_1->settext("update blog_text_1");
    +   blog_1->setauthor(author_2);
    +   daoError = qx::dao::save(blog_1);
    +
    +   // Add 2 comments to 'blog_1'
    +   comment_ptr comment_1; comment_1.reset(new comment());
    +   comment_ptr comment_2; comment_2.reset(new comment());
    +
    +   comment_1->settext("comment_1 text");
    +   comment_1->setblog_id(blog_1);
    +   comment_2->settext("comment_2 text");
    +   comment_2->setblog_id(blog_1);
    +
    +   daoError = qx::dao::insert(comment_1);
    +   daoError = qx::dao::insert(comment_2);
    +   qAssert(qx::dao::count<comment>() == 2);
    +
    +   // Add 2 categories to 'blog_1' => must insert into extra-table 'category_blog'
    +   blog::type_list_of_category lst_category;
    +   lst_category.insert(category_1->getcategory_id(), category_1);
    +   lst_category.insert(category_3->getcategory_id(), category_3);
    +   blog_1->setlist_of_category(lst_category);
    +   daoError = qx::dao::save_with_relation("list_of_category", blog_1);
    +
    +   // Fetch blog into a new variable with all relation : 'author', 'comment' and 'category'
    +   blog_ptr blog_tmp; blog_tmp.reset(new blog());
    +   blog_tmp->setblog_id(blog_1->getblog_id());
    +   daoError = qx::dao::fetch_by_id_with_all_relation(blog_tmp);
    +
    +   qAssert(blog_tmp->list_of_comment().count() == 2);
    +   qAssert(blog_tmp->list_of_category().count() == 2);
    +   qAssert(blog_tmp->gettext() == "update blog_text_1");
    +   qAssert(blog_tmp->getauthor() && blog_tmp->getauthor()->getauthor_id() == author_2->getauthor_id());
    +
    +   // Fetch blog into a new variable with many relations using "*->*->*->*" (4 levels of relationships)
    +   blog_tmp.reset(new blog());
    +   blog_tmp->setblog_id(blog_1->getblog_id());
    +   daoError = qx::dao::fetch_by_id_with_relation("*->*->*->*", blog_tmp);
    +
    +   qAssert(blog_tmp->list_of_comment().count() == 2);
    +   qAssert(blog_tmp->list_of_category().count() == 2);
    +   qAssert(blog_tmp->gettext() == "update blog_text_1");
    +   qAssert(blog_tmp->getauthor() && blog_tmp->getauthor()->getauthor_id() == author_2->getauthor_id());
    +
    +   // Dump 'blog_tmp' result from database (xml serialization)
    +   qx::dump(blog_tmp);
    +
    +   // Check qx::dao::save_with_relation_recursive() function
    +   daoError = qx::dao::save_with_relation_recursive(blog_tmp);
    +   qAssert(! daoError.isValid());
    +   daoError = qx::dao::save_with_relation_recursive(blog_tmp, qx::dao::save_mode::e_update_only);
    +   qAssert(! daoError.isValid());
    +
    +   // Call 'age()' method with class name and method name (reflexion)
    +   //qx_bool bInvokeOk = qx::QxClassX::invoke("author", "age", author_1);
    +   //qAssert(bInvokeOk);
    +
    +   // Test 'isDirty()' method
    +   qx::dao::ptr<blog> blog_isdirty = qx::dao::ptr<blog>(new blog());
    +   blog_isdirty->setblog_id(blog_1->getblog_id());
    +   daoError = qx::dao::fetch_by_id(blog_isdirty);
    +   qAssert(! daoError.isValid() && ! blog_isdirty.isDirty());
    +
    +   blog_isdirty->settext("blog property 'text' modified => blog is dirty !!!");
    +   QStringList lstDiff; bool bDirty = blog_isdirty.isDirty(lstDiff);
    +   qAssert(bDirty && (lstDiff.count() == 1) && (lstDiff.at(0) == "text"));
    +   if (bDirty) { qDebug("[QxOrm] test dirty 1 : blog is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
    +
    +   // Update only property 'm_text' of 'blog_isdirty'
    +   daoError = qx::dao::update_optimized(blog_isdirty);
    +   qAssert(! daoError.isValid() && ! blog_isdirty.isDirty());
    +   qx::dump(blog_isdirty);
    +
    +   // Test 'isDirty()' method with a container
    +   typedef qx::dao::ptr< QList<author_ptr> > type_lst_author_test_is_dirty;
    +   type_lst_author_test_is_dirty container_isdirty = type_lst_author_test_is_dirty(new QList<author_ptr>());
    +   daoError = qx::dao::fetch_all(container_isdirty);
    +   qAssert(! daoError.isValid() && ! container_isdirty.isDirty() && (container_isdirty->count() == 3));
    +
    +   author_ptr author_ptr_dirty = container_isdirty->at(1);
    +   author_ptr_dirty->setlastname("author name modified at index 1 => container is dirty !!!");
    +   bDirty = container_isdirty.isDirty(lstDiff);
    +   qAssert(bDirty && (lstDiff.count() == 1));
    +   if (bDirty) { qDebug("[QxOrm] test dirty 2 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
    +
    +   author_ptr_dirty = container_isdirty->at(2);
    +   author_ptr_dirty->setfirstname("firstname changed");
    +   bDirty = container_isdirty.isDirty(lstDiff);
    +   qAssert(bDirty && (lstDiff.count() == 2));
    +   if (bDirty) { qDebug("[QxOrm] test dirty 3 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
    +
    +   // Update only property 'm_name' at position 1, only property 'm_birthdate' at position 2 and nothing at position 0
    +   daoError = qx::dao::update_optimized(container_isdirty);
    +   qAssert(! daoError.isValid() && ! container_isdirty.isDirty());
    +   qx::dump(container_isdirty);
    +
    +   // Fetch only property 'm_text' of blog
    +   QStringList lstColumns = QStringList() << "text";
    +   QList<blog_ptr> lst_blog_with_only_text;
    +   daoError = qx::dao::fetch_all(lst_blog_with_only_text, NULL, lstColumns);
    +   qAssert(! daoError.isValid() && (lst_blog_with_only_text.size() > 0));
    +   if ((lst_blog_with_only_text.size() > 0) && (lst_blog_with_only_text[0].get() != NULL))
    +   { qAssert(lst_blog_with_only_text[0]->gettitle().isEmpty()); }
    +   qx::dump(lst_blog_with_only_text);
    +
    +   // Dump all registered classes into QxOrm context (introspection engine)
    +   qx::QxClassX::dumpAllClasses();
    +
    +   // Call a custom SQL query or a stored procedure
    +   qx_query testStoredProc("SELECT * FROM t_author");
    +   daoError = qx::dao::call_query(testStoredProc);
    +   qAssert(! daoError.isValid());
    +   testStoredProc.dumpSqlResult();
    +
    +   // Call a custom SQL query or a stored procedure and fetch automatically properties (with a collection of items)
    +   qx_query testStoredProcBis("SELECT * FROM t_author");
    +   authorX.clear();
    +   daoError = qx::dao::execute_query(testStoredProcBis, authorX);
    +   qAssert(! daoError.isValid()); qAssert(authorX.count() > 0);
    +   qx::dump(authorX);
    +
    +   // Call a custom SQL query or a stored procedure and fetch automatically properties
    +   qx_query testStoredProcThird("SELECT name, category_id FROM t_category");
    +   category_ptr category_tmp = category_ptr(new category());
    +   daoError = qx::dao::execute_query(testStoredProcThird, category_tmp);
    +   qAssert(! daoError.isValid()); qAssert(category_tmp->getcategory_id() != "");
    +   qx::dump(category_tmp);
    +
    +   return 0;
    +}
    +
    +
    +

    +
    +
    +

    Export to C++ model/view project

    +
    + QxOrm library provides QxModelView module. + A documentation about QxModelView module is available in the QxOrm library user manual. + QxModelView module + provides a way to work with Qt + model/view engine with all classes registered in QxOrm context : +
      +
    • QML : each property defined in QxOrm context is + exposed to QML engine : QxModelView module makes easier integration between QML and + databases ;
    • +
    • Qt widgets : QTableView or QListView + for example to display/modify a database table content.
    • +
    +
    + QxEntityEditor application is able to generate a full C++ model/view project ready to be built : + so all entities and their relationships are accessible in QML views. +

    +

    Export settings

    +
    + C++ model/view export parameters are accessible via main menu : Tools >> Export to + C++ model/view project (settings). + Export parameters screen is displayed automatically when the first export is launched if no + setting is saved in *.qxee project. + When a C++ model/view export process has already been executed, then export parameters screen + is only accessible via : Tools >> Export to C++ model/view project (settings) : +

    + Export C++ model/view +

    + This export parameters screen is divided in several sections : +

    + -- Section C++ model/view export settings : +
      +
    • Field � C++ model/view project location � : output directory which contains C++ model/view project files. + You can define in this field a relative path to your *.qxee project file ;
    • +
    • Field � C++ entities project location � : read-only, displays the C++ project location (entities registered in QxOrm + context) whose depends C++ model/view project ;
    • +
    • Field � Namespace for model/view � : namespace used to group all C++ model/view + classes (these classes implement qx::IxModel interface) ;
    • +
    • Field � Generate custom files � : if enabled, generated C++ project contains a + custom file per entity. + By default, these custom files are empty and will never be removed or erased by another + export.
    • +
    +
    + -- Section Custom script (Javascript file) to change the default behaviour of the + export process : settings for export custom engine. +
      +
    • Field � Custom script file � : Javascript file location to customize C++ export process. + If empty, no customization is applied (default behaviour of export process). + You can define in this field a relative path to *.qxee project file, for example + ./my_script.js if my_script.js file is in the same directory as + *.qxee file ; +
    • +
    • Button � Debug custom Javascript file � : if a Javascript file is defined, a + click on this button will enable Javascript debugging + mode for the next C++ export process. + Instead of using this button, you can also enable Javascript + debugging mode pressing SHIFT keyboard shortcut when starting an export + process.
    • +
    +
    + -- Section C++ model/view template files : this section defines model/view C++ + source code for Header *.h and Source *.cpp files. + Export engine is based on placeholders (@@ACTION@@ format) to replace code by + calculated value. + Placeholder is the most important part to understand how Javascript customization engine works. + Please note that you can define your own custom placeholder + with @@CUSTOM_ prefix, for example : @@CUSTOM_MY_ACTION@@. + +
    +
    +

    Overview of generated project

    +
    + Output directory of generated C++ model/view project looks like : +

    + Export C++ model/view output +

    +
      +
    • � bin � folder is empty by default, and will contain output binary (shared + library *.dll on Windows, *.so on Linux, etc...) ;
    • +
    • � build � folder is empty by default, it can be used to manage CMake configuration files ;
    • +
    • � custom � folder is filled if the � Generate custom files � + option is enabled ;
    • +
    • � include � folder contains all models definitions for entities and their + relationships ;
    • +
    • � src � folder contains all models implementations for entities and their + relationships ;
    • +
    • � CMakeLists.txt � file can be used to build model/view project with CMake ;
    • +
    • � *.gen.pri � and � *.gen.pro � files can be used to build model/view + project with qmake ;
    • +
    +
    +
    +
    +

    Export + to C++ services project

    +
    + QxOrm library provides QxService module. + A documentation about QxService module is available in QxOrm + library user manual. + QxService module of QxOrm + library provides a way to create quickly a powerful C++ applications server (based on + services with request from client and response from server). + A tutorial is available on QxOrm website to explain how to work + with QxService module. +

    + QxEntityEditor application is able to generate a full C++ services project ready to be built (a + C++ project to manage client layer and a C++ project to publish services on server side) : so + all entities and their relationships can be transfered over network (client/server application). +

    +

    Export settings

    +
    + C++ services export parameters are accessible via main menu : Tools >> Export to C++ + services project (settings). + Export parameters screen is displayed automatically when the first export is launched if no + setting is saved in *.qxee project. + When a C++ services export process has already been executed, then export parameters screen + is only accessible via : Tools >> Export to C++ services project (settings) : +

    + Export C++ services +

    + This export parameters screen is divided in several sections : +

    + -- Section C++ services export settings : +
      +
    • Field � C++ services project location � : output directory which contains all C++ services project files. + You can define in this field a relative path to your *.qxee project file ;
    • +
    • Field � C++ entities project location � : read-only, displays the C++ project location (entities registered in QxOrm + context) whose depends C++ services project ;
    • +
    • Field � Namespace for services � : namespace used to group all C++ services + classes (these classes provide a way to transfer + entities over network, client layer and server layer) ;
    • +
    • Field � Generate custom files � : if enabled, generated C++ project contains a + custom file per entity. + By default, these custom files are empty and will never be removed or erased by another + export. + These custom files can be used for example to implement and publish new services ;
    • +
    • Field � Generate server application � : if enabled, a sample C++ applications server + project is generated and is able to load and publish services on server side + based on plugins ;
    • +
    • Field � Server application location � : if � Generate server application + � option is enabled, then define output directory of sample C++ applications server + project.
    • +
    +
    + -- Section Custom script (Javascript file) to change the default behaviour of the + export process : settings for export custom engine. +
      +
    • Field � Custom script file � : Javascript file location to customize C++ export process. + If empty, no customization is applied (default behaviour of export process). + You can define in this field a relative path to *.qxee project file, for example + ./my_script.js if my_script.js file is in the same directory as + *.qxee file ; +
    • +
    • Button � Debug custom Javascript file � : if a Javascript file is defined, a + click on this button will enable Javascript debugging + mode for the next C++ export process. + Instead of using this button, you can also enable Javascript + debugging mode pressing SHIFT keyboard shortcut when starting an export + process.
    • +
    +
    + -- Section C++ services template files : this section defines services C++ + source code for Header *.h and Source *.cpp files (client and + server). + Export engine is based on placeholders (@@ACTION@@ format) to replace code by + calculated value. + Placeholder is the most important part to understand how Javascript customization engine works. + Please note that you can define your own custom placeholder + with @@CUSTOM_ prefix, for example : @@CUSTOM_MY_ACTION@@. +
      +
    • Option � Default � : if enabled (default option), generate default services + (client layer and server layer) : entities and their relationships can be transfered + over network and CRUD operations on database are supported ;
    • +
    • Option � Custom � : if enabled, you can provide your own C++ services template + files (you should begin to copy/past Header *.h and Source + *.cpp from another option to get a list of placeholders to use). +
    • +
    +
    +
    +

    Overview of generated project

    +
    + Output directory of generated C++ services project (client and server) looks like : +

    + Export C++ services output +

    +
      +
    • � bin � folder is empty by default, and will contain output client and server + binaries (shared library *.dll on Windows, *.so on Linux, etc...) ;
    • +
    • � build � folder is empty by default, it can be used to manage CMake configuration files ;
    • +
    • � client � folder contains qmake and CMake + files to build project (client layer) ;
    • +
    • � custom � folder is filled if the � Generate custom files � + option is enabled ;
    • +
    • � include � folder contains all services definitions for entities and their + relationships ;
    • +
    • � server � folder contains qmake and CMake + files to build project (server layer) ;
    • +
    • � src � folder contains all services implementations for entities and their + relationships ;
    • +
    • � CMakeLists.txt � file can be used to build services project (client and + server) with CMake ;
    • +
    • � *.gen.pri � and � *.gen.pro � files can be used to build services + project (client and server) with qmake ;
    • +
    • � *.gen.json � file is used by the server project to build a plugin which can + be loaded by the applications + server to publish services.
    • +
    +
    +
    +

    Generic application server to provide + services

    +
    + If � Generate server + application � option is enabled, then C++ services export process generates + also a generic applications server. + This sample applications server is able to publish services loading plugins. + Like all other C++ projects generated by QxEntityEditor, the generic applications server can + be built with qmake and + CMake. + Output directory of the generic applications server project looks like : +

    + Export C++ services server app output +

    + Once project is built, an executable is available in the � bin � directory. + Open the executable will launch following window : +

    + Export C++ services server app +

    + This screen is divided in several sections : +

    + -- Section Plugins services : +
      +
    • Field � Plugins path � : required to define where to find services plugins. + These plugins are built using C++ + services server project. + Once loaded, all plugins publish their services which can be called by clients + applications. + If no plugin loaded after selecting the plugins path, then it is possible to define QT_DEBUG_PLUGINS environment variable to log some loading + errors and help you to understand why plugins are not loaded (generally, a plugin + dependency not found).
    • +
    +
    + -- Section Database connection parameters : +
      +
    • This section contains all required parameters to initialize a database connection used + by services (see qx::QxSqlDatabase class for + more details).
    • +
    +
    + -- Section Server parameters : +
      +
    • Field � Port number � : define port number opened by the applications server to + publish services. + Clients applications have to connect to this port number to send requests to server ; +
    • +
    • Field � Thread count � : number of threads used by server to manage client + requests ;
    • +
    • Field � Serialization type � : serialization type used to send responses from + server to client ;
    • +
    • Field � Compress data � : if enabled, all responses from server to client are + compressed ;
    • +
    • Field � Encrypt data � : if enabled, all responses from server to client are + encrypted (possibility to define your own encryption key) ;
    • +
    • Button � Start server � : button to start and stop server.
    • +
    +
    + -- Section Log last client-server reply-request transaction : +
      +
    • Display in XML or JSON format all details about the last client-server transaction + managed by the applications server.
    • +
    +
    + -- Section Log last server error : +
      +
    • If an error occurred on applications server, it is automatically logged in this + section.
    • +
    +
    +
    +
    +

    Export SQL DDL + database schema

    +
    + QxEntityEditor application is able to generate a database schema (structure) in a DDL SQL + format. + QxEntityEditor application support most commonly used databases : SQLite, MySQL, MariaDB, + PostgreSQL, Oracle and Microsoft SQL Server. + Generated DDL SQL script can be imported in database to create automatically all tables, + columns, primary keys, relationships, index, etc... + DDL SQL parameters are accessible via main menu : Tools >> Export to DDL SQL script + file (settings) : +

    + Export SQL DDL +

    + This export parameters screen is divided in several sections : +

    + -- Section DDL export settings : +
      +
    • Field � File location � : output directory which will contain the generated DDL + SQL script ;
    • +
    • Field � Database type � : select DDL SQL script format (SQLite, MySQL, MariaDB, + PostgreSQL, Oracle or Microsoft SQL Server) ;
    • +
    • Field � Relationships � : if enabled, then foreign keys are created to manage + relationships between entities ;
    • +
    • Field � Schema � : define DDL export type : 1 script which contains all + *.qxee items, or 1 script per *.qxee project + version (database schema evolution). +
    • +
    +
    + -- Section Mapping C++ type to database SQL type : +
      +
    • List to map C++ type (defined in all *.qxee project properties) to SQL type.
    • +
    +
    + -- Section Custom script (Javascript file) to change the default behaviour of the export + process : settings for export custom engine. +
      +
    • Field � Custom script file � : Javascript file location to customize export process. + If empty, no customization is applied (default behaviour of export process). + You can define in this field a relative path to *.qxee project file, for example + ./my_script.js if my_script.js file is in the same directory as + *.qxee file ; +
    • +
    • Button � Debug custom Javascript file � : if a Javascript file is defined, a click + on this button will enable Javascript debugging mode for + the next export process. + Instead of using this button, you can also enable Javascript + debugging mode pressing SHIFT keyboard shortcut when starting an export + process.
    • +
    +
    +
    +

    Print entities + diagram

    +
    + QxEntityEditor application is able to print entities diagram to PNG and/or PDF format. + Print parameters are accessible via main menu : Tools >> Print the entities diagram + (settings) : +

    + Export print PNG/PDF +

    +
    +

    Export to a directory linked to a Source Control + manager (Git, Perforce, CVS, etc.)

    +
    + A QxEntityEditor project (*.qxee file) can be managed by a developers team : the source + code of a QxEntityEditor project can be exported/imported (manually or using command line) with + QxEESourceControlExport and QxEESourceControlImport plugins.
    +
    + Associated with a Source Control manager (Git, Perforce, CVS, etc.), these 2 plugins provide : +
      +
    • several people can work simultaneously on the same project ;
    • +
    • a project can easily be versioned in the source code manager ;
    • +
    • each element of a project can be compared (difference between 2 versions of the same + entity for example).
    • +
    + Here is a use case of QxEESourceControlExport and QxEESourceControlImport plugins, with 2 + developers named 'dev A' and 'dev B' (this example can be extended to X developers) : +
      +
    • dev A and dev B work on a same QxEntityEditor project (*.qxee file) ;
    • +
    • dev A creates/changes/deletes some entities in QxEntityEditor application ;
    • +
    • dev B creates/changes/deletes other entities in QxEntityEditor application ;
    • +
    • dev A and dev B export the *.qxee project file with the QxEESourceControlExport plugin + (manually or using command line) ;
    • +
    • once the project is exported to a directory, dev A and dev B check-in (or submit) + all generated JSON files to the Source Control manager (Git, Perforce, CVS, etc.) on their + own branch ;
    • +
    • from the Source Control manager, dev A and dev B merge (or integrate) their own + branch to the development branch DEV (or MAIN or MASTER or LATEST); Note : even if + there are conflicts to resolve, they will be easy to correct because the JSON format is + easily readable ;
    • +
    • dev A and dev B can now get latest files from Source Control manager from DEV branch (or + MAIN or MASTER or LATEST) : these JSON files contain both modifications from dev A and + dev B ;
    • +
    • dev A and dev B can import these JSON files into the QxEntityEditor application using the + import plugin QxEESourceControlImport + (manually or using command line).
    • +
    + The export plugin settings are accessible from the main menu Tools >> Export to Source + Control repository (Git, Perforce, CVS, etc.) as JSON files (settings) : +

    + Source Control export plugin settings +

    + The parameter to enter corresponds to the directory in which the JSON files will be + generated.
    + Each *.qxee project item (entities, properties, relationships, comments, etc.) is + exported to a specific JSON file.
    + The directory where the JSON files are exported has the following structure : +

    + Source Control output directory +

    + Note : it is possible to export a project from command line without starting the + QxEntityEditor application UI.
    + Command line example :
    +
    + +
    QxEntityEditor --no_gui --project="<path_to_your_qxee_project_file>" --plugin=QxEESourceControlExport --QxEESourceControlExport_path="<path_to_your_export_output_directory>"
    +
    +
    +

    +
    +

    Export project + to XML or JSON format

    +
    + + Note : + to work with a source code manager (Git, Perforce, CVS, etc.), it is recommended to use QxEESourceControlExport and QxEESourceControlImport plugins (instead of + creating a single file).
    +
    + QxEntityEditor application can export a *.qxee project to XML or JSON format. + XML/JSON export parameters are accessible via main menu : Tools >> Export to XML or + JSON file (settings) : +

    + Export XML/JSON +

    + Note : a XML file cannot be used to import items in a *.qxee project. + On the other hand, a JSON file can be used to import items in a *.qxee project using the + import plugin : Import project based on JSON file. +

    +
    +
    + +

    Javascript engine to + customize export

    +
    + QxEntityEditor application provides a powerful export customization engine. + This engine is based on Javascript language to write scripts to customize QxEntityEditor exports. + These Javascript scripts can + access to all parameters and items of a *.qxee project, and a QxEntityEditor native debugger can be enabled to help you to develop + and fix your scripts. + It is also possible to write your own files during export + process. +

    + Note : the ./samples/ directory of QxEntityEditor package provides 2 documented + samples scripts : custom_script.js and q_property.js. +

    +

    Architecture + and workflow of Javascript engine

    +
    + All QxEntityEditor export plugins provide following settings : Custom script (Javascript file) to change the + default behaviour of the export process. + This is where you can define a Javascript file to use to customize your exports. + Export engine is based on placeholders (@@ACTION@@ format) to replace code by + calculated value. + Placeholder is the most important part to understand how Javascript + customization engine works. + Please note that you can define your own custom placeholder + with @@CUSTOM_ prefix, for example : @@CUSTOM_MY_ACTION@@. + During an export process, QxEntityEditor application iterates over all entities and enumerations + defined in *.qxee project. + Each time a placeholder is read by export engine (@@ACTION@@ format), the Javascript script is + called with all input parameters required to customize output files. +

    + Here is a minimal Javascript file example. + This script does nothing, it just shows what is a valid QxEntityEditor Javascript file : + +
    +
    +
    ({
    +
    +// Here is the entry point of the QxEntityEditor javascript engine
    +// This function is called for each placeholder defined in the C++ template section
    +customProcess : function(params)
    +{
    +   try
    +   {
    +      // quit with 'params[0]' means : doesn't change the default export behaviour
    +      return params[0];
    +   }
    +   catch (err)
    +   { return ("[CustomScriptError] an unexpected error occurred : " + err); }
    +}
    +
    +});
    +
    +

    +

    Input + parameters of Javascript engine

    +
    + customProcess() function input parameter (named params) is an array which + contain a list of values (this is the call context of the script). + Here is a sample function which can be used in your own scripts to list and to give meaning + to all input values : +

    +
    +
    function printParams(params)
    +{
    +   var log = "";
    +   log = log + "\n - default_value = " + params[0];
    +   log = log + "\n - project_name = " + params[1];
    +   log = log + "\n - project_file = " + params[2];
    +   log = log + "\n - plugin_name = " + params[3];
    +   log = log + "\n - current_file = " + params[4];
    +   log = log + "\n - action (or placeholder) = " + params[5];
    +   log = log + "\n - entity_name = " + params[6];
    +   log = log + "\n - entity_table_name = " + params[7];
    +   log = log + "\n - property_name = " + params[8];
    +   log = log + "\n - property_type = " + params[9];
    +   log = log + "\n - property_column_name = " + params[10];
    +   log = log + "\n - property_is_primary_key = " + params[11];
    +   log = log + "\n - enumeration_name = " + params[12];
    +   log = log + "\n - entity_id = " + params[13];
    +   log = log + "\n - property_id = " + params[14];
    +   log = log + "\n - enumeration_id = " + params[15];
    +   log = log + "\n - output_location = " + params[16];
    +
    +   print(log); // print value to the custom script debugger window
    +   helper.print(log); // print value to the standard output (for example, on Windows, use the 'DebugView' application to see all logs)
    +   return log;
    +}
    +
    +

    +
    +
    +

    Available + functions in Javascript

    +
    + QxEntityEditor export customization engine support all Javascript language + features (ECMAScript 5). + QxEntityEditor application provides also a global instance of object named helper + which can be used in your own scripts to access to more features. + We will list in this chapter the most commonly used features to customize output files. +

    +

    Get + entity details

    +
    + Here is an example to get all current entity parameters, + helper.getEntityDetails(entity_id) : +

    +
    +
    var entity_id = params[13];
    +printEntityDetails(entity_id);
    +
    +//...
    +
    +function printEntityDetails(entity_id)
    +{
    +   var details = helper.getEntityDetails(entity_id);
    +   if (details.length == 0) { return details; }
    +
    +   var log = "";
    +   log = log + "\n - entity_id = " + details[0];
    +   log = log + "\n - entity_key = " + details[1];
    +   log = log + "\n - entity_name = " + details[2];
    +   log = log + "\n - entity_namespace = " + details[3];
    +   log = log + "\n - entity_tablename = " + details[4];
    +   log = log + "\n - entity_description = " + details[5];
    +   log = log + "\n - entity_is_read_only = " + details[6];
    +   log = log + "\n - entity_is_abstract = " + details[7];
    +   log = log + "\n - entity_version = " + details[8];
    +   log = log + "\n - entity_primary_key_property_id = " + details[9];
    +   log = log + "\n - entity_list_of_properties_id = " + details[10];
    +   log = log + "\n - entity_has_triggers = " + details[11];
    +   log = log + "\n - entity_trigger_on_before_fetch = " + details[12];
    +   log = log + "\n - entity_trigger_on_after_fetch = " + details[13];
    +   log = log + "\n - entity_trigger_on_before_insert = " + details[14];
    +   log = log + "\n - entity_trigger_on_after_insert = " + details[15];
    +   log = log + "\n - entity_trigger_on_before_update = " + details[16];
    +   log = log + "\n - entity_trigger_on_after_update = " + details[17];
    +   log = log + "\n - entity_trigger_on_before_delete = " + details[18];
    +   log = log + "\n - entity_trigger_on_after_delete = " + details[19];
    +   log = log + "\n - entity_parent_id = " + details[20];
    +   log = log + "\n - entity_soft_delete_column = " + details[21];
    +   log = log + "\n - entity_validator_method = " + details[22];
    +
    +   print(log); // print value to the custom script debugger window
    +   helper.print(log); // print value to the standard output (for example, on Windows, use the 'DebugView' application to see all logs)
    +   return details;
    +}
    +
    +

    +
    +

    Iterate over list of properties of an + entity

    +
    + Here is an example to iterate over all properties defined in current entity : +

    +
    +
    var entity_id = params[13];
    +var entity_details = helper.getEntityDetails(entity_id);
    +var entity_list_of_properties_id = ((entity_details.length > 0) ? entity_details[10] : "");
    +var entity_list_of_properties_array = entity_list_of_properties_id.split("|");
    +for (var idx = 0; idx < entity_list_of_properties_array.length; idx++)
    +{
    +   var property_id = entity_list_of_properties_array[idx];
    +   var property_details = helper.getPropertyDetails(property_id);
    +   // ...
    +}
    +
    +

    +
    +

    Get + property details

    +
    + Here is an example to get all property parameters, + helper.getPropertyDetails(property_id) : +

    +
    +
    function printPropertyDetails(property_id)
    +{
    +   var details = helper.getPropertyDetails(property_id);
    +   if (details.length == 0) { return details; }
    +
    +   var log = "";
    +   log = log + "\n - property_id = " + details[0];
    +   log = log + "\n - property_key = " + details[1];
    +   log = log + "\n - property_name = " + details[2];
    +   log = log + "\n - property_column_name = " + details[3];
    +   log = log + "\n - property_description = " + details[4];
    +   log = log + "\n - property_type = " + details[5];
    +   log = log + "\n - property_version = " + details[6];
    +   log = log + "\n - property_entity_id = " + details[7];
    +   log = log + "\n - property_is_read_only = " + details[8];
    +   log = log + "\n - property_is_primary_key = " + details[9];
    +   log = log + "\n - property_is_serializable = " + details[10];
    +   log = log + "\n - property_is_transient = " + details[11];
    +   log = log + "\n - property_is_obsolete = " + details[12];
    +   log = log + "\n - property_is_index = " + details[13];
    +   log = log + "\n - property_is_unique = " + details[14];
    +   log = log + "\n - property_allow_null = " + details[15];
    +   log = log + "\n - property_order_level = " + details[16];
    +   log = log + "\n - property_default_value = " + details[17];
    +   log = log + "\n - property_format = " + details[18];
    +   log = log + "\n - property_force_sql_type = " + details[19];
    +   log = log + "\n - property_force_sql_alias = " + details[20];
    +   log = log + "\n - property_min_value = " + details[21];
    +   log = log + "\n - property_max_value = " + details[22];
    +   log = log + "\n - property_min_length = " + details[23];
    +   log = log + "\n - property_max_length = " + details[24];
    +   log = log + "\n - property_reg_exp = " + details[25];
    +   log = log + "\n - property_accessibility = " + details[26];
    +   log = log + "\n - property_is_relationship = " + details[27];
    +   log = log + "\n - property_relation_type = " + details[28];
    +   log = log + "\n - property_relation_entity_target_id = " + details[29];
    +   log = log + "\n - property_relation_inverse_property_id = " + details[30];
    +   log = log + "\n - property_relation_foreign_key = " + details[31];
    +   log = log + "\n - property_relation_foreign_key_owner = " + details[32];
    +   log = log + "\n - property_relation_extra_table = " + details[33];
    +   log = log + "\n - property_relation_type_desc = " + details[34];
    +
    +   print(log); // print value to the custom script debugger window
    +   helper.print(log); // print value to the standard output (for example, on Windows, use the 'DebugView' application to see all logs)
    +   return details;
    +}
    +
    +

    +
    +

    Get + enumeration details

    +
    + Here is an example to get all current enumeration parameters, + helper.getEnumerationDetails(enumeration_id) : +

    +
    +
    var enumeration_id = params[15];
    +printEnumerationDetails(enumeration_id);
    +
    +//...
    +
    +function printEnumerationDetails(enumeration_id)
    +{
    +   var details = helper.getEnumerationDetails(enumeration_id);
    +   if (details.length == 0) { return details; }
    +
    +   var log = "";
    +   log = log + "\n - enumeration_id = " + details[0];
    +   log = log + "\n - enumeration_key = " + details[1];
    +   log = log + "\n - enumeration_name = " + details[2];
    +   log = log + "\n - enumeration_namespace = " + details[3];
    +   log = log + "\n - enumeration_description = " + details[4];
    +   log = log + "\n - enumeration_version = " + details[5];
    +   log = log + "\n - enumeration_use_qt_enum_macro = " + details[6];
    +   log = log + "\n - enumeration_list_of_keys = " + details[7];
    +   log = log + "\n - enumeration_list_of_values = " + details[8];
    +
    +   print(log); // print value to the custom script debugger window
    +   helper.print(log); // print value to the standard output (for example, on Windows, use the 'DebugView' application to see all logs)
    +   return details;
    +}
    +
    +

    +
    +

    Get + meta-data of entity/property/enumeration

    +
    + QxEntityEditor application provides a feature to define some meta-data linked to entity, property or enumeration. + These meta-data can be used for example to define extra-parameters not provided by default by + QxEntityEditor application. + These meta-data are available in generated C++ code (introspection engine of QxOrm library), and are also + available in QxEntityEditor Javascript engine. + Here is an example to get meta-data values in Javascript : +

    +
    +
    var entity_id = params[13];
    +var entity_meta_data = helper.getEntityMetaData(entity_id, "MY_ENTITY_META_DATA_KEY");
    +
    +// ...
    +
    +var property_meta_data = helper.getPropertyMetaData(property_id, "MY_PROPERTY_META_DATA_KEY");
    +
    +// ...
    +
    +var enumeration_id = params[15];
    +var enumeration_meta_data = helper.getEnumerationMetaData(enumeration_id, "MY_ENUM_META_DATA_KEY");
    +
    +

    +
    +

    Get + environment variable

    +
    + Here is an example to get/set environment variables in Javascript, + helper.getEnvironmentVariable() and + helper.setEnvironmentVariable() : +

    +
    +
    var env_var = helper.getEnvironmentVariable("QT_DIR");
    +var set_env_var_ok = helper.setEnvironmentVariable("MY_ENV_VAR", "my_value");
    +
    +

    +
    +

    Read + and write to files

    +
    + Javascript engine of QxEntityEditor application is able to read and write to files (whatever + files on your system, not only exported files). + Javascript classes file and dir are available to instantiate + Javascript objects and to provide functions to manage files and directories. +

    + The Javascript file class provides the same definition as QFile Qt class : +

    +
    +
    /*
    +   --- 'file' class methods available by script (QFile wrapper : http://doc.qt.io/qt-5/qfile.html) ---
    +
    +   bool copy(string fileName, string newName);
    +   bool exists(string fileName);
    +   bool link(string fileName, string linkName);
    +   bool remove(string fileName);
    +   bool rename(string oldName, string newName);
    +   string readAll(string fileName);
    +
    +   void setFileName(string name);
    +   string fileName();
    +   bool open(int mode); // enum QIODevice::OpenMode, for example : 26 = (QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text) = (2 + 8 + 16)
    +   int error(); // enum QFile::FileError
    +   void close();
    +   bool atEnd();
    +   string readLine();
    +   void write(string text);
    +*/
    +
    +

    + The Javascript dir class provides the same definition as QDir Qt class : +

    +
    +
    /*
    +   --- 'dir' class methods available by script (QDir wrapper : http://doc.qt.io/qt-5/qdir.html) ---
    +
    +   void setPath(string path);
    +   string path();
    +   string appPath();
    +   string homePath();
    +   string rootPath();
    +   string tempPath();
    +
    +   string fromNativeSeparators(string pathName);
    +   string toNativeSeparators(string pathName);
    +
    +   string cleanPath(string path);
    +   bool isAbsolutePath(string path);
    +   bool isRelativePath(string path);
    +   bool match(string filter, string fileName);
    +
    +   bool mkdir(string dirName);
    +   bool mkpath(string dirPath);
    +   bool rmdir(string dirName);
    +   bool rmpath(string dirPath);
    +   bool exists(string name);
    +
    +   bool cdUp();
    +   bool cd(string dirName);
    +   string absoluteFilePath(string fileName);
    +   string absolutePath();
    +   string canonicalPath();
    +   string dirName();
    +   string filePath(string fileName);
    +   void refresh();
    +   string relativeFilePath(string fileName);
    +
    +   bool isAbsolute();
    +   bool isReadable();
    +   bool isRelative();
    +   bool isRoot();
    +*/
    +
    +

    + Example : read a file content : +

    +
    +
    var f1 = new file();
    +var f1_content = f1.readAll("C:\\Temp\\my_file.txt");
    +
    +

    + Other example : write to a file : +

    +
    +
    var f2 = new file();
    +f2.setFileName("C:\\Temp\\file_generated_by_script.txt");
    +f2.open(26); // (QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text) = (2 + 8 + 16)
    +f2.write("aaa");
    +f2.write("bbb");
    +f2.close();
    +
    +

    +
    +

    Get list + of entities/enumerations of a project

    +
    + Here is how to get all entities and enumerations of a *.qxee project : +

    +
    +
    var listOfAllEntities = helper.getListOfAllEntities(); // 'listOfAllEntities' variable is an array, each item of this array contains : <entity_id>|<entity_name>
    +var listOfAllEnums = helper.getListOfAllEnums(); // 'listOfAllEnums' variable is an array, each item of this array contains : <enum_id>|<enum_name>
    +
    +

    +
    +

    Fetch all + application settings (at global level, project level and plugin level)

    +
    + Here is how to fetch all application settings (JSON format) : +
      +
    • at global level calling function : + helper.getQxEEGlobalSettingsJson() ; +
    • +
    • at project level calling function : + helper.getQxEEProjectSettingsJson() ; +
    • +
    • at plugin level calling function : + helper.getQxEEPluginSetingsJson(). +
    • +
    +
    +
    +
    var globalSettings = helper.getQxEEGlobalSettingsJson();    // the result is a string in JSON format ==> so just use JSON.parse() function to get a javascript object instance
    +var projectSettings = helper.getQxEEProjectSettingsJson();  // the result is a string in JSON format ==> so just use JSON.parse() function to get a javascript object instance
    +var pluginSettings = helper.getQxEEPluginSetingsJson();     // the result is a string in JSON format ==> so just use JSON.parse() function to get a javascript object instance
    +
    +print(globalSettings); helper.print(globalSettings);
    +print(projectSettings); helper.print(projectSettings);
    +print(pluginSettings); helper.print(pluginSettings);
    +
    +globalSettings = JSON.parse(globalSettings);    // Now 'globalSettings' is a javascript object instance
    +projectSettings = JSON.parse(projectSettings);  // Now 'projectSettings' is a javascript object instance
    +pluginSettings = JSON.parse(pluginSettings);    // Now 'pluginSettings' is a javascript object instance
    +
    +

    +
    +
    +

    Add a custom + action (placeholder) in C++ export template

    +
    + C++ export plugins provide a setting to define template files to + use during export : Header *.h and Source *.cpp. + QxEntityEditor application provides several C++ templates by default, and it is possible to + define your own C++ template (option � Custom + �). + Header *.h and Source *.cpp C++ templates contain some + placeholders (@@ACTION@@ format) to replace code by calculated value. + Placeholder is the most important part to understand how Javascript + customization engine works. + Please note that you can define your own custom placeholder + with @@CUSTOM_ prefix, for example : @@CUSTOM_MY_ACTION@@. +

    + Here is an example to test current action code (so current placeholder inside call context) with + the Javascript engine : +

    +
    + /* you can define your own placeholder in the template, it must start + with @@CUSTOM_, for example : @@CUSTOM_MY_ACTION@@ + ==> then, in the custom script, check if you are processing your custom action with this + code : */ +
    var action = params[5];
    +if (action == "CUSTOM_MY_ACTION")
    +{
    +   return "quit with my custom code here";
    +}
    +
    +

    +
    +

    Script + example : add automatically Q_PROPERTY definition on all generated C++ properties

    +
    + Here is a documented script (deployed with QxEntityEditor package in + ./samples/q_property.js file) to add automatically Q_PROPERTY macro definition + to all properties of a *.qxee project : +

    +
    +
    ({
    +
    +/* ----------------------------------------------------------------------------------------
    +   ----------------------------------------------------------------------------------------
    +   'q_property.js' : custom javascript file to customize QxEntityEditor C++ export process.
    +   This script is an example to show how to use QxEntityEditor javascript engine to add a Q_PROPERTY definition for each property generated by QxEntityEditor.
    +   More details about Q_PROPERTY macro on Qt web site : http://doc.qt.io/qt-5/properties.html
    +
    +   To use this javascript file :
    +      1- go to the main menu of QxEntityEditor 'Tools >> Export to C++ project (settings)' ;
    +      2- select the C++ template 'qx::IxPersistable + QObject' : now generated entities will inherit from QObject, which is required to use the Qt Q_PROPERTY macro ;
    +      3- in the field 'Custom script file' : put the location of this 'q_property.js' custom javascript file ;
    +      4- save the settings and start the C++ export process ;
    +      5- check generated files : Q_PROPERTY should be added automatically by the export process.
    +   ----------------------------------------------------------------------------------------
    +   ---------------------------------------------------------------------------------------- */
    +
    +// Here is the entry point of the QxEntityEditor javascript engine
    +// This function is called for each placeholder defined in the C++ template section
    +customProcess : function(params)
    +{
    +   try
    +   {
    +      // We use here the @@MACRO_QX_PERSISTABLE_HPP@@ placeholder which is present in the default C++ template 'qx::IxPersistable + QObject'
    +      // You could also create your own custom C++ template (based on 'qx::IxPersistable + QObject' template), and create your own placeholder (which must be prefixed by @@CUSTOM_), for example : @@CUSTOM_Q_PROPERTY@@
    +      var action = params[5];
    +      if (action != "MACRO_QX_PERSISTABLE_HPP") { return params[0]; } // quit with 'params[0]' means : doesn't change the default export behaviour
    +
    +      // Check if we have an entity
    +      var entity_id = params[13];
    +      if ((entity_id == "") || (entity_id == "0")) { return params[0]; } // quit with 'params[0]' means : doesn't change the default export behaviour
    +
    +      // Get the list of properties
    +      var entity_details = helper.getEntityDetails(entity_id);
    +      var entity_list_of_properties_id = ((entity_details.length > 0) ? entity_details[10] : "");
    +      var entity_list_of_properties_array = entity_list_of_properties_id.split("|");
    +      if (entity_list_of_properties_array.length <= 0) { return params[0]; } // quit with 'params[0]' means : doesn't change the default export behaviour
    +
    +      // Prepare output string
    +      var output = params[0] + "\n";
    +
    +      // Iterate over each property
    +      for (var idx = 0; idx < entity_list_of_properties_array.length; idx++)
    +      {
    +         // Get property details
    +         var property_id = entity_list_of_properties_array[idx];
    +         var property_details = helper.getPropertyDetails(property_id);
    +
    +         // Here you could also get property meta-data that you can define in QxEntityEditor, property parameters screen, section 'List of meta-data'
    +         // This is a way to manage your own property parameters which are not a part of QxEntityEditor
    +         // It could be useful for example to manage some Q_PROPERTY settings like : RESET, NOTIFY, REVISION, DESIGNABLE, SCRIPTABLE, FINAL, etc...
    +         // To get a property meta-data value, just write this code : var my_meta_data = helper.getPropertyMetaData(property_id, "MY_META_DATA_KEY");
    +
    +         // Get property type and name
    +         var property_type = property_details[5];
    +         var property_name = property_details[2];
    +
    +         // Check if property type can be used with Q_PROPERTY macro
    +         if (list_of_compatible_property_type.indexOf(property_type) == -1) { continue; }
    +
    +         // Create Q_PROPERTY definition
    +         output += "\n   Q_PROPERTY(" + property_type + " " + property_name + " READ get" + property_name + " WRITE set" + property_name + ")";
    +      }
    +
    +      return output;
    +   }
    +   catch (err)
    +   { return ("[CustomScriptError] an unexpected error occurred : " + err); }
    +}
    +
    +});
    +
    +// Here is a list of C++ types compatible with Qt Q_PROPERTY macro (C++ type can be converted to/from QVariant)
    +// You can of course register your own C++ types to be able to use them with Q_PROPERTY macro (using Qt Q_DECLARE_METATYPE() macro)
    +// So the following array is not fixed : you can add all C++ types you want...
    +var list_of_compatible_property_type = [ "QBitArray", "QBitmap", "bool", "QBrush", "QByteArray", "QChar", "QColor", "QDate", "QDateTime", "double", 
    +                                         "QUuid", "QFont", "QVariantHash", "QIcon", "QImage", "int", "QLine", "QLineF", "QVariantList", "qlonglong", 
    +                                         "QVariantMap", "QMatrix", "QMatrix4x4", "QPixmap", "QPoint", "QPointF", "QPolygon", "QPolygonF", "QRect", "QRectF", 
    +                                         "QRegExp", "QRegion", "QSize", "QSizeF", "QString", "QStringList", "QTime", "uint", "qulonglong", "QUrl", 
    +                                         "QVector2D", "QVector3D", "QVector4D" ];
    +
    +

    +
    +

    Enable Javascript + debugger editor

    +
    + Javascript engine of QxEntityEditor application provides a native Javascript debugger. + This debug environment provides all required features to help you to develop and fix your custom + scripts : +
      +
    • define breakpoints ;
    • +
    • step-by-step mode to go from a code line to another code line during export execution ; +
    • +
    • display all variables and their values during export execution ;
    • +
    • display logs window.
    • +
    +
    + By default, QxEntityEditor Javascript debugger is disabled during an export. + To enable it and show the debug editor, you can : +
      +
    • go to export settings, define a script to + use, then click on � Debug custom Javascript file � button ;
    • +
    • or press the SHIFT keyboard shortcut before starting an export process.
    • +
    +
    + JS debug +

    +
    +
    + +

    Execute custom scripts + (shell/bat) before/after plugin execution

    +
    + QxEntityEditor application provides a way to define some custom scripts *.bat (Windows), + *.sh (Linux) or even executables to run before and/or after a QxEntityEditor plugin + execution. + Each script (or process) is called with 1 input parameter : the current *.qxee project + location. + This feature can be used for example to : +
      +
    • put generated files to a source control (Git, Perforce, CVS, etc...) ;
    • +
    • run compilation process for a generated C++ project ;
    • +
    • execute unitary tests or start an automation server like Jenkins for example ;
    • +
    • modify some values in the *.qxee SQLite database project + file after importing from a database structure.
    • +
    +
    + To define these scripts or executables to run before and/or after a QxEntityEditor plugin + execution, just go to the main menu : Tools >> Plugins scripts. +

    + Plugin scripts +

    + Note : you can put in this list an absolute path to your script, or a relative path to + *.qxee project file. + For example, ./my_script.sh means that my_script.sh file must be located in the same + directory as *.qxee project file. +

    +
    + +

    QxEntityEditor + command-line interface

    +
    + QxEntityEditor provides a command-line interface to run the application with some parameters. + We will detail in this chapter some QxEntityEditor calling examples : +


    + -- Example n�1 : run QxEntityEditor defining a *.qxee project to load at startup + (--project parameter) : +
    +
    + +
    QxEntityEditor --project="c:\test\qxBlog.qxee"
    +
    +
    +

    + -- Example n�2 : run QxEntityEditor without displaying the user interface (--no_gui + parameter), defining a *.qxee project to load (--project parameter) and executing + automatically a C++ export process (--plugin parameter) : +
    +
    + +
    QxEntityEditor --no_gui --project="c:\test\qxBlog.qxee" --plugin=QxEECppExport
    +
    +
    +

    + -- Example n�3 : run QxEntityEditor in read-only mode (--viewer_mode parameter), this + parameter can be used to open large *.qxee projects without any registered license key : +
    +
    + +
    QxEntityEditor --viewer_mode
    +
    +
    +

    + -- Example n�4 : run QxEntityEditor tracing all SQL logs (--log_sql parameter), this + parameter logs all SQL queries executed to *.qxee SQLite + database : +
    +
    + +
    QxEntityEditor --log_sql
    +
    +
    +

    + -- Example n�5 : run QxEntityEditor loading a *.qxee project from a JSON file : +
    +
    + +
    QxEntityEditor --project="<path_to_your_qxee_project_file>" --plugin=QxEEJsonImport --QxEEJsonImport_file="<path_to_your_json_file>"
    +
    +
    +

    + -- Example n�6 : display documentation about all QxEntityEditor command line parameters : +
    +
    + +
    QxEntityEditor --?
    +
    +
    +
    + Note : --?, --help or --h will log following output to console : +
    +
    + +
    *** QxEntityEditor 1.2.5 application global command line parameters ***
    +
    +  --project="<full path to *.qxee project file>" : run QxEntityEditor defining a *.qxee project to load at startup
    +  --no_gui : run QxEntityEditor without displaying the user interface
    +  --viewer_mode : run QxEntityEditor in read-only mode, this parameter can be used to open large *.qxee projects without any registered license key
    +  --log_sql : run QxEntityEditor tracing all SQL logs, this parameter logs all SQL queries executed to *.qxee SQLite database
    +  --font : define application font (due to issues since macOS Catalina 10.15) with syntax <family>||<pointSize>||<weight>||<italic> (only <family> is required), for example : Courier New||14
    +  --style_sheet : define application style sheet (more details here : https://doc.qt.io/qt-5/stylesheet-reference.html), for example : QWidget { background-color: black }
    +  --plugin=<plugin name> : run QxEntityEditor and execute automatically a plugin process (see below for specific parameters per plugin)
    +
    +*** Import plugin QxEEJsonImport ***
    +
    +  --QxEEJsonImport_file="<full path to your json file>" : [Required] run QxEntityEditor loading a *.qxee project from a JSON file
    +
    +*** Import plugin QxEEMySQLImport ***
    +
    +  --QxEEMySQLImport_db_ip="<DB IP>" : [Required] Database server address (IP)
    +  --QxEEMySQLImport_db_port="<DB port>" : [Required] Port number to connect to database
    +  --QxEEMySQLImport_db_name="<DB name>" : [Required] Database name
    +  --QxEEMySQLImport_filter_regexp="<Regular Expression>" : [Optional] Filter to select tables from database to import
    +  --QxEEMySQLImport_login="<DB Login>" : [Optional] Login to connect to database
    +  --QxEEMySQLImport_pwd="<DB Password>" : [Optional] Password to connect to database
    +  --QxEEMySQLImport_namespace="<Namespace>" : [Optional] C++ namespace where imported classes will be located
    +  --QxEEMySQLImport_delete_namespace=0/1 : [Optional] Delete all entities in the namespace before importing
    +  --QxEEMySQLImport_import_comment=0/1 : [Optional] Import tables/columns comment to entities/properties description
    +  --QxEEMySQLImport_import_default_value=0/1 : [Optional] Import columns default value
    +  --QxEEMySQLImport_boost_optional=0/1 : [Optional] Add boost::optional<T> decoration if a column definition allows NULL value
    +  --QxEEMySQLImport_organize_diagram=0/1 : [Optional] Organize diagram layout after import process
    +  --QxEEMySQLImport_relation_decoration=<numeric value> : [Optional] Decoration used for relationships (0=no decoration, 1=boost::shared_ptr, 2=QSharedPointer, 5=std::shared_ptr)
    +  --QxEEMySQLImport_relation_collection=<numeric value> : [Optional] Collection used for relationships (1=qx::QxCollection, 2=std::vector, 3=std::list, 8=QHash, 10=QList)
    +  --QxEEMySQLImport_mapping_sql_to_cpp="sql1~cpp1;sql2~cpp2;etc..." : [Optional] List of mappings from SQL type to C++ type
    +  --QxEEMySQLImport_verbose=0/1 : [Optional] Display more details during the import process
    +
    +*** Import plugin QxEEOdbcImport ***
    +
    +  --QxEEOdbcImport_dsn="<ODBC DSN>" : [Required] ODBC DSN to connect to database
    +  --QxEEOdbcImport_db_type=<numeric value> : [Required] Database engine type (0=generic, 1=postgresql, 2=mysql, 3=oracle, 4=mssqlserver, 5=sqlite)
    +  --QxEEOdbcImport_filter_regexp="<Regular Expression>" : [Optional] Filter to select tables from database to import
    +  --QxEEOdbcImport_login="<DB Login>" : [Optional] Login to connect to database
    +  --QxEEOdbcImport_pwd="<DB Password>" : [Optional] Password to connect to database
    +  --QxEEOdbcImport_namespace="<Namespace>" : [Optional] C++ namespace where imported classes will be located
    +  --QxEEOdbcImport_delete_namespace=0/1 : [Optional] Delete all entities in the namespace before importing
    +  --QxEEOdbcImport_import_comment=0/1 : [Optional] Import tables/columns comment to entities/properties description
    +  --QxEEOdbcImport_import_default_value=0/1 : [Optional] Import columns default value
    +  --QxEEOdbcImport_boost_optional=0/1 : [Optional] Add boost::optional<T> decoration if a column definition allows NULL value
    +  --QxEEOdbcImport_organize_diagram=0/1 : [Optional] Organize diagram layout after import process
    +  --QxEEOdbcImport_relation_decoration=<numeric value> : [Optional] Decoration used for relationships (0=no decoration, 1=boost::shared_ptr, 2=QSharedPointer, 5=std::shared_ptr)
    +  --QxEEOdbcImport_relation_collection=<numeric value> : [Optional] Collection used for relationships (1=qx::QxCollection, 2=std::vector, 3=std::list, 8=QHash, 10=QList)
    +  --QxEEOdbcImport_mapping_sql_to_cpp="sql1~cpp1;sql2~cpp2;etc..." : [Optional] List of mappings from SQL type to C++ type
    +  --QxEEOdbcImport_verbose=0/1 : [Optional] Display more details during the import process
    +
    +*** Import plugin QxEEPostgreSQLImport ***
    +
    +  --QxEEPostgreSQLImport_db_ip="<DB IP>" : [Required] Database server address (IP)
    +  --QxEEPostgreSQLImport_db_port="<DB port>" : [Required] Port number to connect to database
    +  --QxEEPostgreSQLImport_db_name="<DB name>" : [Required] Database name
    +  --QxEEPostgreSQLImport_filter_regexp="<Regular Expression>" : [Optional] Filter to select tables from database to import
    +  --QxEEPostgreSQLImport_login="<DB Login>" : [Optional] Login to connect to database
    +  --QxEEPostgreSQLImport_pwd="<DB Password>" : [Optional] Password to connect to database
    +  --QxEEPostgreSQLImport_namespace="<Namespace>" : [Optional] C++ namespace where imported classes will be located
    +  --QxEEPostgreSQLImport_delete_namespace=0/1 : [Optional] Delete all entities in the namespace before importing
    +  --QxEEPostgreSQLImport_import_comment=0/1 : [Optional] Import tables/columns comment to entities/properties description
    +  --QxEEPostgreSQLImport_import_default_value=0/1 : [Optional] Import columns default value
    +  --QxEEPostgreSQLImport_boost_optional=0/1 : [Optional] Add boost::optional<T> decoration if a column definition allows NULL value
    +  --QxEEPostgreSQLImport_organize_diagram=0/1 : [Optional] Organize diagram layout after import process
    +  --QxEEPostgreSQLImport_relation_decoration=<numeric value> : [Optional] Decoration used for relationships (0=no decoration, 1=boost::shared_ptr, 2=QSharedPointer, 5=std::shared_ptr)
    +  --QxEEPostgreSQLImport_relation_collection=<numeric value> : [Optional] Collection used for relationships (1=qx::QxCollection, 2=std::vector, 3=std::list, 8=QHash, 10=QList)
    +  --QxEEPostgreSQLImport_mapping_sql_to_cpp="sql1~cpp1;sql2~cpp2;etc..." : [Optional] List of mappings from SQL type to C++ type
    +  --QxEEPostgreSQLImport_verbose=0/1 : [Optional] Display more details during the import process
    +
    +*** Import plugin QxEESQLiteImport ***
    +
    +  --QxEESQLiteImport_db_path="<DB File Path>" : [Required] SQLite database file path
    +  --QxEESQLiteImport_filter_regexp="<Regular Expression>" : [Optional] Filter to select tables from database to import
    +  --QxEESQLiteImport_namespace="<Namespace>" : [Optional] C++ namespace where imported classes will be located
    +  --QxEESQLiteImport_delete_namespace=0/1 : [Optional] Delete all entities in the namespace before importing
    +  --QxEESQLiteImport_import_comment=0/1 : [Optional] Import tables/columns comment to entities/properties description
    +  --QxEESQLiteImport_import_default_value=0/1 : [Optional] Import columns default value
    +  --QxEESQLiteImport_boost_optional=0/1 : [Optional] Add boost::optional<T> decoration if a column definition allows NULL value
    +  --QxEESQLiteImport_organize_diagram=0/1 : [Optional] Organize diagram layout after import process
    +  --QxEESQLiteImport_relation_decoration=<numeric value> : [Optional] Decoration used for relationships (0=no decoration, 1=boost::shared_ptr, 2=QSharedPointer, 5=std::shared_ptr)
    +  --QxEESQLiteImport_relation_collection=<numeric value> : [Optional] Collection used for relationships (1=qx::QxCollection, 2=std::vector, 3=std::list, 8=QHash, 10=QList)
    +  --QxEESQLiteImport_mapping_sql_to_cpp="sql1~cpp1;sql2~cpp2;etc..." : [Optional] List of mappings from SQL type to C++ type
    +  --QxEESQLiteImport_verbose=0/1 : [Optional] Display more details during the import process
    +
    +*** Export plugin QxEECppExport ***
    +
    +  !!! Note : this plugin loads automatically previous parameters values before starting the export process, but you can use following parameters to override them !!!
    +  --QxEECppExport_path="<Export Path>" : [Optional] C++ export path parameter
    +  --QxEECppExport_template_type=<numeric value> : [Optional] C++ template type selected to build C++ files (0=no_inheritance, 1=ix_persistable, 2=qx_persistable, 3=ix_persistable_and_q_object, 4=custom)
    +  --QxEECppExport_template_header="<Template Header>" : [Optional] Custom C++ template header file path (QxEECppExport_template_type parameter must be equal to 4, which means custom)
    +  --QxEECppExport_template_source="<Template Source>" : [Optional] Custom C++ template source file path (QxEECppExport_template_type parameter must be equal to 4, which means custom)
    +  --QxEECppExport_qxorm_relative_path=0/1 : [Optional] Relative path to QxOrm library
    +  --QxEECppExport_generate_custom_files=0/1 : [Optional] Generate a custom directory with custom files for each entity
    +  --QxEECppExport_custom_javacript="<Custom JS>" : [Optional] Custom script (javascript file) to change the default behaviour of the export process
    +
    +*** Export plugin QxEECppModelViewExport ***
    +
    +  !!! Note : this plugin loads automatically previous parameters values before starting the export process, but you can use following parameters to override them !!!
    +  --QxEECppModelViewExport_path="<Export Path>" : [Optional] C++ model/view export path parameter
    +  --QxEECppModelViewExport_namespace="<Namespace>" : [Optional] Namespace where to put all model/view classes
    +  --QxEECppModelViewExport_template_type=<numeric value> : [Optional] C++ model/view template type selected to build C++ files (0=default, 1=custom, 2=qx_model_service)
    +  --QxEECppModelViewExport_template_header="<Template Header>" : [Optional] Custom C++ template header file path (QxEECppModelViewExport_template_type parameter must be equal to 1, which means custom)
    +  --QxEECppModelViewExport_template_source="<Template Source>" : [Optional] Custom C++ template source file path (QxEECppModelViewExport_template_type parameter must be equal to 1, which means custom)
    +  --QxEECppModelViewExport_generate_custom_files=0/1 : [Optional] Generate a custom directory with custom files for each entity
    +  --QxEECppModelViewExport_custom_javacript="<Custom JS>" : [Optional] Custom script (javascript file) to change the default behaviour of the export process
    +
    +*** Export plugin QxEECppServicesExport ***
    +
    +  !!! Note : this plugin loads automatically previous parameters values before starting the export process, but you can use following parameters to override them !!!
    +  --QxEECppServicesExport_path="<Export Path>" : [Optional] C++ services export path parameter
    +  --QxEECppServicesExport_namespace="<Namespace>" : [Optional] Namespace where to put all services classes
    +  --QxEECppServicesExport_template_type=<numeric value> : [Optional] C++ services template type selected to build C++ files (0=default, 1=custom)
    +  --QxEECppServicesExport_template_header="<Template Header>" : [Optional] Custom C++ template header file path (QxEECppServicesExport_template_type parameter must be equal to 1, which means custom)
    +  --QxEECppServicesExport_template_source="<Template Source>" : [Optional] Custom C++ template source file path (QxEECppServicesExport_template_type parameter must be equal to 1, which means custom)
    +  --QxEECppServicesExport_generate_custom_files=0/1 : [Optional] Generate a custom directory with custom files for each entity
    +  --QxEECppServicesExport_custom_javacript="<Custom JS>" : [Optional] Custom script (javascript file) to change the default behaviour of the export process
    +  --QxEECppServicesExport_generate_server_app=0/1 : [Optional] Generate a sample server application
    +  --QxEECppServicesExport_server_app_path="<Server App Path>" : [Optional] Server application location
    +
    +*** Export plugin QxEEGenericDDLExport ***
    +
    +  !!! Note : this plugin loads automatically previous parameters values before starting the export process, but you can use following parameters to override them !!!
    +  --QxEEGenericDDLExport_path="<Export Path>" : [Optional] DDL export path parameter
    +  --QxEEGenericDDLExport_db_type=<numeric value> : [Optional] Database type (0=default, 1=sqlite, 2=mysql, 3=postgresql, 4=oracle, 5=mssqlserver)
    +  --QxEEGenericDDLExport_relation_as_fk=0/1 : [Optional] Export relationships as foreign keys in database
    +  --QxEEGenericDDLExport_schema_type=<numeric value> : [Optional] Way to export database schema (0=full, 1=evolution, 2=full_and_evolution)
    +  --QxEEGenericDDLExport_custom_javacript="<Custom JS>" : [Optional] Custom script (javascript file) to change the default behaviour of the export process
    +
    +*** Export plugin QxEEPrinter ***
    +
    +  !!! Note : this plugin loads automatically previous parameters values before starting the export process !!!
    +
    +*** Export plugin QxEEXmlExport ***
    +
    +  !!! Note : this plugin loads automatically previous parameters values before starting the export process, but you can use following parameters to override them !!!
    +  --QxEEXmlExport_path="<Export Path>" : [Optional] XML or JSON export path parameter
    +  --QxEEXmlExport_as_json=0/1 : [Optional] Export project as JSON format
    +
    +
    +
    +
    + Other note : to see logs generated by QxEntityEditor application : +
      +
    • Windows : use an external tool, for example DebugView ;
    • +
    • Linux : execute QxEntityEditor from a terminal ;
    • +
    • Mac OS X : execute QxEntityEditor from a terminal inside QxEntityEditor.app + sub-directory, for example : cd QxEntityEditor.app/Content/MacOS/, then : + ./QxEntityEditor +
    • +
    +

    + -- Example n�7 : import by ODBC (plugin QxEEOdbcImport) a database schema : +
    +
    + +
    QxEntityEditor --no_gui --project="<project_path>" --plugin=QxEEOdbcImport --QxEEOdbcImport_db_type=1 --QxEEOdbcImport_dsn="<your_dsn>" --QxEEOdbcImport_login="<your_login>" --QxEEOdbcImport_pwd="<your_password>" --QxEEOdbcImport_delete_namespace=1
    +
    +
    +

    + -- Example n�8 : import a PostgreSQL database schema (plugin QxEEPostgreSQLImport) : +
    +
    + +
    QxEntityEditor --no_gui --project="<project_path>" --plugin=QxEEPostgreSQLImport --QxEEPostgreSQLImport_db_ip="<ip>" --QxEEPostgreSQLImport_db_port=5432 --QxEEPostgreSQLImport_db_name="<dbname>" --QxEEPostgreSQLImport_login="<your_login>" --QxEEPostgreSQLImport_pwd="<your_password>" --QxEEPostgreSQLImport_delete_namespace=1
    +
    +
    +

    + -- Example n�9 : export a QxEntityEditor project to a Source Control manager/directory Git, + Perforce, CVS, etc. (QxEESourceControlExport plugin) : +
    +
    + +
    QxEntityEditor --no_gui --project="<path_to_your_qxee_project_file>" --plugin=QxEESourceControlExport --QxEESourceControlExport_path="<path_to_your_export_output_directory>"
    +
    +
    +

    + -- Example n�10 : import from a Source Control manager/directory Git, Perforce, CVS, etc. + (QxEESourceControlImport plugin) : +
    +
    + +
    QxEntityEditor --project="<path_to_your_qxee_project_file>" --plugin=QxEESourceControlImport --QxEESourceControlImport_path="<path_to_the_root_source_control_json_file>"
    +
    +
    +

    +
    + +
    +
    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/doc/qxorm_en/model.html b/doc/qxorm_en/model.html new file mode 100644 index 0000000..9d1602b --- /dev/null +++ b/doc/qxorm_en/model.html @@ -0,0 +1,177 @@ + + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic editor + (data model designer and source code generator) + + + + + + + + + + + + + + + + + + + + + +
    QxOrm + + + + Windows + Linux + Macintosh + C++
    +
    + + + + + + + + + + + + + + + + + + +
    HomeDownloadQuick sample + Tutorial (4) + + + + + + +
    + +
    +
    + Manual (2) + + + + + + +
    + +
    +
    ForumOur customers
    +
    + + + + + + + + + + + + + + + + + +
    + QxOrm >> [model] + + + + + + + + + + + + + + +
    + Current version :  + + QxOrm 1.5.0 - QxOrm library online class documentation - GitHub +
    + + + QxEntityEditor 1.2.8 +
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    +
    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/doc/qxorm_en/quick_sample.html b/doc/qxorm_en/quick_sample.html new file mode 100644 index 0000000..094df39 --- /dev/null +++ b/doc/qxorm_en/quick_sample.html @@ -0,0 +1,449 @@ + + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic + editor (data model designer and source code generator) + + + + + + + + + + + + + + + + + + + + + +
    QxOrm + + + Windows + Linux + Macintosh + C++
    +
    + + + + + + + + + + + + + + + + + + +
    HomeDownloadQuick sample + Tutorial (4) + + + + + + +
    + +
    +
    + Manual (2) + + + + + + +
    + +
    +
    ForumOur customers
    +
    + + + + + + + + + + + + + + + + + +
    + QxOrm >> Quick sample + + + + + + + + + + + + + + +
    + Current version :  + + QxOrm 1.5.0 - QxOrm library online class documentation - GitHub +
    + + + QxEntityEditor 1.2.8 +
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    + + + + + + + + + +
    In this chapter, we will see a quick sample with basic functionalities of QxOrm + library.
    +
    + Note : QxOrm library uses the following syntax and naming convention for C++ + source code : +
      +
    • all classes, functions, properties, etc... are defined under namespace qx ; +
    • +
    • all macros of QxOrm library are prefixed by QX_... ; +
    • +
    • all abstracts classes (or interfaces) start with prefix Ix (for example + IxFactory is an interface to create an instance of object) ; +
    • +
    • other classes start with prefix Qx (for example QxDataMember) ; +
    • +
    • containers of objects end with suffix X (for example QxDataMemberX is a + list of QxDataMember) ; +
    • +
    • functions to interact with databases are under namespace qx::dao (for + example qx::dao::fetch_by_id()) ; +
    • +
    • functions to serialize are under namespace + qx::serialization (for example qx::serialization::xml::to_file()) + ; +
    • +
    • the reflection (or introspection) engine can be used with + qx::QxClassX (for example qx::QxClassX::invoke() to call a class + method) ; +
    • +
    • all traits classes are under namespace qx::trait (for + example qx::trait::is_smart_ptr<T>). +
    • +
    +
    qt_ambassador
    + + QxOrm library has been accepted into the Qt + Ambassador Program + +
    + Additional note : you can find a more complex sample (with inheritance, polymorphism, + relationships, collections, shared libraries, + memory leak, etc...) in the folder ./test/qxDllSample/ of your QxOrm package. + The ./test/qxDllSample/ folder contains 2 projects of + DLL type (shared libraries) and 1 project of executable type : ./dll1/, + ./dll2/ and ./exe/.
    + This solution can be built with Visual C++ 2008, 2010 or 2012 on Windows (open the file + ./test/qxDllSample/test.sln).
    + This solution can also be compiled with GCC on Linux, Clang on Mac OS X and MinGW on + Windows with qmake command line.
    +
    + Quick sample step by step : + +
    + * + -----------------------------------------------------------------------------------------------------
    + * 1- drug.h file : drug class definition with 3 properties : id, name + and description
    * + -----------------------------------------------------------------------------------------------------
    +

    + + + + + + + +
    +
    #ifndef _CLASS_DRUG_H_
    +#define _CLASS_DRUG_H_
    +
    +class drug
    +{
    +public:
    +   long id;
    +   QString name;
    +   QString description;
    +
    +   drug() : id(0) { ; }
    +   virtual ~drug() { ; }
    +};
    +
    +QX_REGISTER_HPP_MY_TEST_EXE(drug, qx::trait::no_base_class_defined, 1)
    +
    +/* This macro is necessary to register 'drug' class in QxOrm context */
    +/* param 1 : the current class to register => 'drug' */
    +/* param 2 : the base class, if no base class, use the qx trait => 'qx::trait::no_base_class_defined' */
    +/* param 3 : the class version used by serialization to provide 'ascendant compatibility' */
    +
    +#endif // _CLASS_DRUG_H_
    +
    +

    + * + ----------------------------------------------------------------------------------------------------
    + * 2- drug.cpp file : 'setting function' implementation : void + qx::register_class()
    * + ---------------------------------------------------------------------------------------------------- +


    + + + + + + + +
    +
    #include "precompiled.h"   // Precompiled-header with '#include <QxOrm.h>' and '#include "export.h"'
    +#include "drug.h"          // Class definition 'drug'
    +#include <QxOrm_Impl.h>     // Automatic memory leak detection and boost serialization export macro
    +
    +QX_REGISTER_CPP_MY_TEST_EXE(drug)   // This macro is necessary to register 'drug' class in QxOrm context
    +
    +namespace qx {
    +template <> void register_class(QxClass<drug> & t)
    +{
    +  t.id(& drug::id, "id");               // Register 'drug::id' <=> primary key in your database
    +  t.data(& drug::name, "name", 1);      // Register 'drug::name' property with key 'name' and version '1'
    +  t.data(& drug::description, "desc");  // Register 'drug::description' property with key 'desc'
    +}}
    +
    +
    +
    + * + -----------------------------------------------------------------------------------------------
    + * 3- main.cpp file : basic functionalities of QxOrm library with drug class
    * + ----------------------------------------------------------------------------------------------- +


    + + + + + + + +
    +
    #include "precompiled.h"
    +#include "drug.h"
    +#include <QxOrm_Impl.h>
    +
    +int main(int argc, char * argv[])
    +{
    +   QApplication app(argc, argv); // Qt application
    +
    +   // Create 3 new drugs
    +   // It is possible to use 'std' and 'Qt' smart pointer : 'std::shared_ptr', 'QSharedPointer', etc...
    +   typedef std::shared_ptr<drug> drug_ptr;
    +   drug_ptr d1; d1.reset(new drug()); d1->name = "name1"; d1->description = "desc1";
    +   drug_ptr d2; d2.reset(new drug()); d2->name = "name2"; d2->description = "desc2";
    +   drug_ptr d3; d3.reset(new drug()); d3->name = "name3"; d3->description = "desc3";
    +
    +   // Insert drugs into container
    +   // It is possible to use many containers from 'std', 'boost', 'Qt' and 'qx::QxCollection<Key, Value>'
    +   typedef std::vector<drug_ptr> type_lst_drug;
    +   type_lst_drug lst_drug;
    +   lst_drug.push_back(d1);
    +   lst_drug.push_back(d2);
    +   lst_drug.push_back(d3);
    +
    +   // Init parameters to communicate with a database
    +   qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE");
    +   qx::QxSqlDatabase::getSingleton()->setDatabaseName("./test_qxorm.db");
    +   qx::QxSqlDatabase::getSingleton()->setHostName("localhost");
    +   qx::QxSqlDatabase::getSingleton()->setUserName("root");
    +   qx::QxSqlDatabase::getSingleton()->setPassword("");
    +
    +   // Create table 'drug' into database to store drugs
    +   QSqlError daoError = qx::dao::create_table<drug>();
    +
    +   // Insert drugs from container to database
    +   // 'id' property of 'd1', 'd2' and 'd3' are auto-updated
    +   daoError = qx::dao::insert(lst_drug);
    +
    +   // Modify and update the second drug into database
    +   d2->name = "name2 modified";
    +   d2->description = "desc2 modified";
    +   daoError = qx::dao::update(d2);
    +
    +   // Delete the first drug from database
    +   daoError = qx::dao::delete_by_id(d1);
    +
    +   // Count drugs into database
    +   long lDrugCount = qx::dao::count<drug>();
    +
    +   // Fetch drug with id '3' into a new variable
    +   drug_ptr d_tmp; d_tmp.reset(new drug());
    +   d_tmp->id = 3;
    +   daoError = qx::dao::fetch_by_id(d_tmp);
    +
    +   // Export drugs from container to a file under xml format (serialization)
    +   qx::serialization::xml::to_file(lst_drug, "./export_drugs.xml");
    +
    +   // Import drugs from xml file into a new container
    +   type_lst_drug lst_drug_tmp;
    +   qx::serialization::xml::from_file(lst_drug_tmp, "./export_drugs.xml");
    +
    +   // Clone a drug
    +   drug_ptr d_clone = qx::clone(* d1);
    +
    +   // Create a new drug by class name (factory)
    +   qx::any d_any = qx::create("drug");
    +
    +   // Insert drugs container into 'qx::cache'
    +   qx::cache::set("drugs", lst_drug);
    +
    +   // Remove all elements from 'qx::cache'
    +   qx::cache::clear();
    +
    +   // Create a dummy memory leak
    +   drug * pDummy = new drug();
    +
    +   return 0;
    +}
    +
    +
    +

    + * + -------------------------------------------------------------------------
    + * 4- execute program and trace output debug
    * + ------------------------------------------------------------------------- +

    +

    +

    + [QxOrm] qx::QxSqlDatabase : create new database + connection in thread '3616' with key + '{d315250c-b5c9-46e0-9402-f800368a6673}'
    + [QxOrm] sql query (78 ms) : CREATE TABLE drug (id INTEGER NOT NULL + PRIMARY KEY AUTOINCREMENT, name TEXT, desc TEXT)
    + [QxOrm] sql query (63 ms) : INSERT INTO drug (name, desc) VALUES + (:name, :desc)
    + [QxOrm] sql query (62 ms) : UPDATE drug SET id = :id, name = :name, + desc = :desc WHERE id = :id_bis
    + [QxOrm] sql query (63 ms) : DELETE FROM drug WHERE id = :id
    + [QxOrm] sql query (0 ms) : SELECT COUNT(*) FROM drug
    + [QxOrm] sql query (0 ms) : SELECT drug.id AS drug_id_0, drug.name AS + drug_name_0, drug.desc AS drug_desc_0 FROM drug WHERE drug_id_0 = + :id
    + [QxOrm] Leaked object at 0xf52ad8 (size 16, src\main.cpp:74)
    + [QxOrm] **** 1 memory leaks found ****

    +
    +

    + * + ------------------------------------------------------------------------------
    + * 5- ./export_drugs.xml file created by the program
    * + ------------------------------------------------------------------------------
    +

    +
    + quick_sample.export_drugs.xml +
    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/doc/qxorm_en/resource/ES.png b/doc/qxorm_en/resource/ES.png new file mode 100644 index 0000000000000000000000000000000000000000..805a1b66b448e51af64ffc7214adba1eae37184d GIT binary patch literal 605 zcmV-j0;2tiP)X00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy8FWQhbW?9;ba!ELWdKlNX>N2bPDNB8b~7$DE;K%k%ys|(0k%m* zK~z{r?Uy@C15p%&4>n0-B^K$d1HsnPA7Eo)r$0f#(n=v>Wn&>$DYOU(VM|jckiwK9 z2uYj5B3+PRJI-u27!t$YJiMD?7ltkN!FT8ExjV-!JyHi!2bOXGd@2dK!Da|YIuxCk6=oI#{i;#QQAq1R#(jCLMHau<25-?BFCI_zecfhDwE9TwoW=QGSCV zU{WQXUU8>$g?hanYB=0mx;e)0UCcqxK|vLW!h+*Im;%m5xzxc`1MWf&@~53d+;P^Hf|Qe*4Fu~$Gh^ouQ+I9m%m%Oa00000NkvXXu0mjfskrzI literal 0 HcmV?d00001 diff --git a/doc/qxorm_en/resource/FR.png b/doc/qxorm_en/resource/FR.png new file mode 100644 index 0000000000000000000000000000000000000000..35e3aaaf1dc70a96e1f7c794ccb7ef16678f1547 GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<^!3HGb?#A2&QY`6?zK#qG*KS<#k1zuAB}-f* zN`mv#O3D+9QW+dm@{>{(JaZG%Q-e|yQz{EjrrIztF#349IEGZ*O8WEvzy09^HU`Ec zM+{CdIxz57u`{t;SWvD2WeRYcIl8$WV?1ejfai3F9-DxJPQ?`AM1_fF#zsP6Oq>#l z3SYUq>X~K#@GCU1MT@Lv+NdM&nbA<&g8|5&&8*uPA>HBHID=P(0jRb4Ok-BSFT*p9 z42&i>oB}wc_R1J+X9U`(pT@wHalu*usAn%TBe~2D)_UpbkJ^lm0_`ra4B)v{u!TKI zSEJs6!GYmn(F1#he~d@wH~f&5z5AIj>pA}lX$b}*q>ImyNc?ulAo30&l%8&ky)v1TvZMThNwSvVA8_)?m_t-7&bM%>Q7@>Sb~6%H`` YW3q^@`SEH6Fz^{XUHx3vIVCg!0FGdgR{#J2 literal 0 HcmV?d00001 diff --git a/doc/qxorm_en/resource/GB.png b/doc/qxorm_en/resource/GB.png new file mode 100644 index 0000000000000000000000000000000000000000..3a189083842a36757587898f32f8f61eea08875f GIT binary patch literal 942 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<^!3HGb?#A2&QY`6?zK#qG*KS<#k1zuAB}-f* zN`mv#O3D+9QW+dm@{>{(JaZG%Q-e|yQz{EjrrIztFgtjW{1a`(GAw`M-C1{om`CnLU$|3=SxD`0AeEnGmU9?3mX4hT$e_>0VZW zY{rcm0-KwA7&sK>@UcivoBQ|w|JRql|Gytr^!tB=$NT@ko5lZ|oBsXJ&%-!Lmg5rh zC2k|;6tNRN6K*P4D`Xwu(_wiOutK@g(T#ycAdbChL(`%E|E@}U-mafe)otiwefBT|;B^-uOf70`Gc8rjFJOZco-JuiBX!7^J?K za-u}##s@1UUe59?SQ^>DkaTFdoUFHpfkDol69+ylNoPH*ZYJ0!y>BTGe}2vJOm#HN z85UCU=x=)dDsSg&4j+>__Yyeh3TfU%a^bp5xm;jHS%X z4h#>A9_X()q#j@=KUaoh^Jn2JygWet+Ps^bWmUPBTE2MV5V2-2+eL@%e_23=u!J_Q uaX7qZiRh&yj<9hEL01BQfB=E7E@iJfb@OxZun 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 literal 0 HcmV?d00001 diff --git a/doc/qxorm_en/resource/gui_qxClientServer.jpg b/doc/qxorm_en/resource/gui_qxClientServer.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5f10aa19c25899319bf723276eec306aaf54a184 GIT binary patch literal 192022 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
    E2Wbnzx@~aVP@VmO^ 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%0Aiud11eRv>i>|wVq=aMj-i0Q( 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

    n8TqnHboQ(qO{L|7eH8oMx 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 z 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 literal 0 HcmV?d00001 diff --git a/doc/qxorm_en/resource/gui_qxClient_01.jpg b/doc/qxorm_en/resource/gui_qxClient_01.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c505283a8edddff5196710dda59c89c5995047ad GIT binary patch literal 67652 zcmeFZ1yodP+c3Nlq(M5QOOTY1PLY8Dl@O@`>6UH*2T_nt0qK;GP`W|7K^lgV?k=gB ze>~4~&XIGT_kI5FUF-eV_kZ6zEY_NP_I+RbzB=w}Z^Q)RJ3yca0)qe)6coS!`3E58 z0WvvvOEUlfgV_O0;P($=3Xlab(a|x`Z(?F#U|?ZkV&jtF;o{)nQW6p0BB8lWM@w^? znwp-Oo0XoClZl#|P3SJ?JzfC;0XkL@31L2QZhis2pFmKsu&{7(a4GQcDEJtt8TkH} zKZvgYAtp-94Hi@sM&Je^3MwHAq7|S60F;|ZEPf*L&kxECRJ5Du7?@btILHa*1i%dx zRMZ=2s5ft-p&@5`Bd-H!gg1#8cxBLuRShv19Z2|IN2X&kJt%G>RU6!6<~MTm#lj{d zr=Yyea)*`eF1x^eK_OugQQ3!bAbAC_;#2i!8k$<#I>siZX66=_R!+_?u5RugFW&Bco&E6O*v{ zg~g@imDRQNjs1heqvMm)A7|%3@j?Mm|AE#YnEeGWLL^={(9lrPFn;2Na>EVzk4lJk zlYtkVNJbUI(1Dnd?=>dLgUIyaCM+g?wLMZJ$3bi|W`TK@{hz4)hS~o+#C-otnEip+ zpLoFlTvQY!^H2!^DPSk0#`_Y-Bd?8RHT9yCE0BX4+&?5O%qg>UkuplwhYI(*fL?lD zS`3w?ZhPWTfl?q2Ty@a;xASqBx9DOyl;EVT&?CB0=M@hAHc_5?Ce$qTMx6a zs_W5}@4V|7oFSzYEZ3kbl|}&RujUW{EdsbLXhCQu=ABkglRvohHdDVO;HKl zIw#*o0Q+U6KXA3u zFHKdH?DuB-hWS6&|B&$i+v)RIYA^iVLt0wD z9j3~!cHaE)nT(gyFb-3=NxG%_xQKY*La^ME3e2fsrq@S6H9|4W;$~t_!{27Ak^j~< ztl$ofW@7MKk@fZPD`D^C9L=6*dh5aIcF<>n zHWnXauYWksCu-=MBX9;8)F19zQ{N~3m>uIp3=;^ow*Q!^u^bVW7CoMuk-Erym&S*w zF7<+ibh0ykMR%u+;N&G)vR7=WZpoBAdDNt#2|>pAg+Aq4O?Y!&WTXna_Sw9%u{ znVY3k>lGSgFsEq&GL4;H8u>YCc{Pb!%Q|#Zu(&H=2>~<~BWrRJ%!w&WZN5*YQHm!{?zgz_w!lKmcXEoDZ#fw_<0OLAO?z+oPIWSSW#LkrTxh02 z?X2{VP^01S4g}CP;--!@;Usq;;o z$IaM)mV-+Wi&`M@)%3lbu6ZB8W}u%pO3bz>QE|}a)~z`!4*Y0|zKR@oJ0dw3T3OMe z;|Su&PGK0;UQyKW)0*P4pt13mKD;_bGT|YG=3E+p>IbRN$w?*xkQR~J-GW}~aY-LS zNq>+-YH0baIh4zwRvji|-9?|Ah=C195I(v$yj)xoV>G&T1@gw5r7R)!Bt)%spVQ&e z$Hrd0Q~B9fF^B2fM|9iU@^BwV=-dPX_)#NOSx$G3(uDw^#t2|xZR8f8BL-}c$v8oq)_l`5ZFHcr99|Kyo7lIT)@5PdKgAKoW5 zlX)S409+IQ?}4c0c4B#i#QOXxw6dJ%7q-A>M;g~KSg)?a5kPRJ$`>RV*X5COvk}0r z!a8_Wp9lSe_MmjbxyhWNaOB*_*8yC3sv&?h>eSN{DL8lNgO@{Sl68{re@?Z#w*I<~I$M#^TqSt3{hT{@ z4FFOgf5m)+Qw&KDRpi{eNQwDH%YRDDpK3ao``VAb%$+4t1mo_bQ*MYDiDBo~yWmiWCN*#e9UNW=TO2~N!cV*p z(#)lwOs5Jvg2Zm66`D5oprjnjU))3htK%97;OhrAc$4vI!Lb=lQ}vsKp?$LK+1J4q z&Ihe*o)A}N_0fd@Y^+b2V^)^Lc9BR_rX^4aJFPXzWS^JM`cJG@WSI02mamr-5a5A9_ z^w>}*;XAZDgoKrkv+MfbEUO)-8O?czvH3>(eAYi^ z-5)jv+ue4Zvw15g@}OWt@73PdB}aU8d_JHbJ?D}k0s#yZl_G%G_Rk81T6isMLdY{8 zjTF(6;Rj3fvdH4U1H$Btw@hYp+{X641uxaZE-vTdH5WMP%tfshDG!z5Z{&nJU~wmf z6^Zr+_ezu;a2V(hKJtG{SsK*apV-Gk1_ubBqX#-6i1dAO<<9p9+}+@k5O`CC)Hzpv z8Z!W)zwv z!wpg=u!lICN~0}BfItc^yMafW%;x5YOji?N!f&9h`44Qa25H-R(~&LQ{8iYW^G6$b zxYM@{t*0k5N}Le@0n93nu*H)4z&1c}=;loVTNe=ndj*TmMUhS>CT1ncQe0wPe_tm% zi>&EspIQb6p$L#0aarD(diV#=1wG0<$MW2}ZTZuK8O5osj3PucM?Hh?JD)15gFSGI zno1jOrI7b%OA&SC8(y%sH(~7G&h+IGT#+Uw`_7+fdsd?OAyrJ)c~OK^Uo&s&9A2?Q zJ0#Ip!-4jr5v+8!ZIWmgy@KxDIo*!s@P*gGr?5)}Uzn969$LZOpKogAod03WIVM>z zgF8`&o{sT>;N>d4X%z*D_w$Sy$jI|C3CdDABW?FEszgt1T>d&oP7bb4Idw4eGxm>e zv0aD6+24h5E71av`fMeXI}yOc>WYhpbQW(0CNC!7x{>9H6ti{Ibcqx5ihc?+<(0=* z9(d}(8&;~S9_#C@R~L4BnFGpTsog24pNTdXDS1jt6m+fv88(U^9Yw>hDu1Y;m8m~} z`4Wr(UQJ!H9S#pEU!2U%OA`(L9RQ#tC%2lDm3q3}*c>h)!Hm>78#oEdzlSPB_&Rnr zb2Q@h*@6#CMv!rDKogiHpq~^he94A#q`{q+r#Cg7(Y|i|LWQ>~p&$G{DY>bltDx znZ~1+-46$wILTiyS)`>b$j~yyG;eESdl=V?$Z^DO`Z!3&eCo*mYHKqep_5{i9FN`2 z2U6!C5_P1wA!;nr+Wmf^I^mm@gKgV62Zqu<@eFL;Xr+0X`*Yf%I%D1gAlzw*7uHzU zIrFB8q6-LL=pm`DE++)DwBi%Ukwl6qJPM|xfX1!3%%7yZw0D4NjO`|l<(N7 zYV|D$qeV{PC`Ehibe%{H2BJLE5Rb!rr77#vI$vWy!R@7-qiyA+H6!(YkV(X5Z4yN& zHBzhUOh$wqgT{k0=0WD9$K|_iCZxs|H~#06KgC1X;$&yd;O`bD=Vw}t`6-Y&=%aek z!K9>-^ECQ=?DvSPx7*INLqf%sa;EDE(=Ssa;ek#zCjpX!7^$VF#h2Ag)`GnC?!r<+ z#cc@ScS$5onr9PJ$n65_{VU;B=(5PI`=`vS{w~ExY0UUt(QIofL@JDNbz zforzkRu?b=d}LfYrFOAf_I5A5DzDzWfF40KkZION$Hh9bj)skN1n;^-nV$qRfqJtr z9+@iVb}~t+$eAYJq^w)#*_l1!dtWQ5<%~@Y0MZ{?qg>QpIj!*)%Bfo^iV-cOEQ>uB z6jKW1MjP(ojAVW?RH0;J{^IC+G?TqBDgfNnyiHo*nuk~0WjokE+$Lb?25Ke!dQ&li zumX^2_Q8+_ePk>cn&u>6HVxvB+EE8EX^!un6E93SRcW_MURuzXGMFpXjkglTly z6l|v?5t-Ox{H%`W<_xB%SVs-_bM1@-CmLv)v}QMh+rPW|(Q%+0gnMmzzfnN|DXw#; zXN^CCB%5-f17dbaKYA%gJEpnPklsV3)gJsX)Q+((WoKn&LkO28&VU&$@oaK`X2R&2K=b^1)|XhU zr4ifZD!)XcssN(UvN97iR7UPfDIlOX*I`W=PoshLvgd9!Vz~tgVd>V^u_x?@HE!UmSgJBNT5fh}4{ z=c_g=8{=ttHdjaY&5QD*4eO`ZVV>a0!ab&-b1TRMk}bxv#}%6p!pP39e+@U4&N#Be zET=5lm!&ttAnCMeFUL%SHb8xD;vgjDXCHUl5>Cp8m8k&Yd1hAVY~^GIwd~!;(c$AOpM^x&1mjhRQn2(0@oh zPNT`78YRLUEvQ(U5CZKe(BHYW{7~AXGzmA38X0lQkZ95~LC|WIOwH zdm7ByfvbRa^xbIr_+X-glDA7Bsw#{vlhTr^2;F@RYn291_N34l?&qz96a*Nug0V4n z|4wZ90ldM?26P^+XSw%W*;*h!w$gj^>uT7$uz2hV_`%(6^_6c72^{%OZmt!TIBbGz z-Gn-irzU7>16e-I2LdTcxR3t=RdTcd`t5TbbnZeQF7eP@cnbm0Tk*J@#oBQ()%zJm ze0dft0vhgD9qpKpp*;D7(K6;wBg(_)T|vmK>ea%`(YokHYV+M;m6t}WaB*i5Rtma|Uq!*=-=vpj1jX9Boj=D@qom8tl zl=&rPWvP#)ZM1)o^WYQ5J(UN(gz65_{rvs>KKD|s#-!FXM;dppJ|pvs%x#^HE9~>Q zD+ExEtO$*W^c6wY58#dSaRy~}p0A9wExt|k*shE3R1As}FvYYwR1CZ~xFLNojuz!1 za_7@$f15ES$MHBj(vnY*W<{Go4TWho1~7wwT1d@jxr=F9R z^IRYRYh+NI_oIECR5| zy1(*#EhLbatF1@JE>^k3^r2^#rl!bLV=C{!?DrI5;8|+# z!iNQ^%U51a?YAJWjP67=Yq3VF*2XT7y--}BR}>r?j3B_v%1Bytqb7W|{qGdneEa3V z6*e+={wLYIKWih6Hw0M%Xgq1Wd>NIe`T{8kWn!<4V3zl^uio@{+?ptST;Lh7jNh+J z19fdkeyooq%(V@x$GWwzXs0ENSu1^TJ&Sl;_5~xoI|@@+0&C5Rd6^5s&du>qHzTtu zMGWG>8t*H2JI+-CppI5oyHJlOQ}DWUE0$zNP;zNKySirh5d4b^0uw@aOojDq?{n5j7bMjC2V+GJ~wZ9e_8F-hMsY?t^+c z$)DakHc~#`{WDw~=_ zQTlp!zTV={0aqDYQWxlO(p0123-wP9<@RC= zKTXJ@jcd3@=`ucPQ`uJQ5T+Cck{nXvb(fyQhli0>hV!3ZnCHjXtEvRE`t;d#zKu;g zyrsvuQ8EIaCqlUB-&-OhhZ`w`@6*dJJeJiOuB$FPH0o(?v*ffr)PLmY^`7CqT=S|2 zKET;aYRr;Jo^bSvw{kdiCDFlbovU@f!(^SMPQ1XNYxUl2FNn|QUYOcFO;WtEcpInO zCMaD957$mc8ANTrKGPOhPP|2Y(@c1#)Iy9n{Jqgq1?ygv01IL4$i&pf+xVv58h)m*b`wx=fEPx4^dOQ`~PqVi#Zs0?p>u& zh4YPm(y%>wf60^ zLo4e{gPbejnpaajR{5UG1r6?&gwkiy`m^S?-vO!v-k{M8AC@o3gQXJaIF{5Ab3tM_ z<#UU1&EN$*(e|;~p+Rou*kKjEZA+GedD#^?qR=h6m6>u^qBa@y@&c(P&eY>(u%fq^s4t#4)W;kCO0}wdzdj}1DNWwkT6}P$R;jy{t$gmqDQ0+A zuiw+1knaRwAp6IT*KL@8qSMXGbDPn&?X}vPC>vhWP=!E4bc!Wc!m5Y5{FLZ!rV3&Y zqueoC7A_vvR19qyMk~h9mQ=f&b-M%UiBsLNu)0(#{1!NAHb|!()!W76HhXNk+2%Vb3EC`R2P5fJR zb8IXppnw*V=@d3os&ik>x@uSa4RyPlKYf&=Ey`X*9CfI=@LB82inmMOajHF}jyEM9d!E>UnityFJYvUr1^BWTgqUt% zb}rc~D>gu~6lda?#>;V>V|TTNw>brlAEiUu6TTN4kltnqr|Nfj>(*na=_-?p5hk#H zKUA`vqR0f&n#grx`8Gl!6cdNpQwW6lL2wlYz+T?F-~RlEu+_`Ar*5aQ#I;S!cDJnx zqGMty38ra&yuZaDqmqkhhH4Aqu}q2P`mon}AYJ&a}=6Y?AM6oYG=p(qe^5ZG=tpN23Cr znf1iFvS=tv7E!XyJp?vWT3bD~xG1>yKr-k7;!haEcDU3Azt@*)EhXUavP@VknPKj4 zN%vxDpuIpNu@#&T16AWDRK%ZHa1YIzreG~bDO|SUk>t^L`|J8XrIFX^uKz}-<_os7 zD=gw!wzwST%HGr_M132Y#&gKVCw8#3+hUIW313=N%ggCwUxAW&9K^Xqsj>XSG{~o{ zX-rL%;Qhf9ujU*P-5c5Y8_xxu%fUyU6gvS`{#f&k9tEZ$ee4}nXzofRoE6nZi z(D02qnsLl?bSJzUNGlpD62^|{%MUtY51wo=P;~}dRyp+VNk30o@sz@qwK3S?tXQx|W-O(;xFe`3W+cQH^SiIu7+Nn? zyXKbE8jU3a$mF&XyA#yjJUhhNYyYtLE-W8e)4;wFmJFbN!w!PU@C1I=)}ppNNxPsE zTn%4Vj_ry%l@-@S0F3^~+~+wzbPc^0JzkbDvZ+X_O;(a{4B6O}O4)dXs^c`v3S*hN z13L7*MPs4353hT7;;-!Us*aw64wROQZPO^`IK^t?*_?|k7EW- zBb{aJw&`+aQPJw z>9kbzdKc2Wn*_+M#x9d6D%_OvbJSW6IY}WWW8mhxGgPpfk!<#M(ozYR5{nrsiZQ$T zVgLbf0chO7StYkjGBnUjU4X767eab?nkuDQcfnG#-O+6_F;^#6k{|X!q;sn8^+GU3 z(R^{81L^uiv`Fm9YAJI`o4z0=XI$P4HgGs*RUn@ranuqivyBL_Q}&r` zt!8c#0hE45+jJuC!@->93`lvtfCbo_N_$h;Zxo=p!uh}Lq>LKWx+%R6&HMoATE4|A z3;KL#CU=YSUWhEsW=Ai7Pv88Mbt=~SOOjJbqlq>WN!uZzwD9f-10kvfOX9q#My3cn zc`1LJJNx{bbZfXn12nDS_kEQdi-JC(IToLh6Mm)wF9#SXusfoR^ejq<>SOBv2v;)n zwzjrrX*LkB=I%V*#?^%1P{6Q8$B3bqoNCo1n3l*cHKi#Wk2qE3O8V?v(&{I>-D)*8 zQgT`AFjme37}X_fk}0$4A#z0d7EWzBWPRH^gjct-GEfk*HH<|cd7FSt z;lwtC@Se>;8A{oafmK8PVvoCk=hQYM#coPJ7B@^RHg?qPHA$>Hb!)TXsfX_8{v7+x z0ilc^hDS<+o;Dtaxa}U43l(1u(g+p4aC`zWi)kggFqD9_OLCY9w2@}B2RtJ1gZayA zZhNy^1NWcyz1~nd_L3;N8estQU8a=kA&u2G z!SlH(!81wC9k04oBi)?Y0chE3&YtIfD66l{QfYCJQoeO8vJbQ}lzXf7ORaSDoz-yr zY0TsRN-D*?u622O+7K_<>`)>-gJ~3FTxHG@k{21-ZvMKqxU~xGpzkthCPt+KyOf}F zG2Kl;yBsURCpJ053tVoDN@b#-&a582ZSO{=5Vl@ZWx|BC{VtUN!O2!L{d)n8a6fYe~nizS=C|)hx zk{EIWyJpcaS-I;3N6i$k_Tbu%&8lMTT_G~0GBsG1UTa0dV+KDu-eZ| zYfhN>>t0n;2iy01>(ib`1>g@tA)hCp-$apV8Ax};)lKNGRj{WM?l`L0l0Op>t$TB< zk#L986+4F|TEm@sQv-(_*qbe{sT8RgNyr_g8S+&GpdmG z{qp_s-#3Zz3$$Op{s(}cl5Jtcx&d$e<2yS=sEO6opZ`f3kWWpFM;80bFmxz`G!C+m zL0b&1l8PaO79?KnBU@a8B7U}!V@R_LzNN$s>J0&5={g8b z@VgWInsZM81%OWR?;K4B7k&`Vss3N3?bFpbhyOZD?5?qn4!O}+aY)(YuR&@~%*Wr& zMCKnVFJAkO*^{#I+PwGBYa*x-_lI5h#r#koAP;)B%%RQqk%HoSfdJ6m^e-L5e}Kvw zcjln~5MGV&|5H~MO={_^4~n}Mlgv&f&w?VSU%!rZ1b2*O{UPeVlR9gewf1(1Mkni^ zeuF#$Cts{~dkMWA<@mdLnZHL|5RyCXbMR)Nx%1nq^4CU?4x=ZfEsgsJb0>Jn2Gl_t zJv^t`YtCXeev*krnkxeRrAd+o-*gsdNe412C@O9b(w^zR@p>eBLd7&hv6x(y>8|Mm z4|-$fTQZSnLH%PdcDT#%9>7>V%o4`52JuI6+b~h5qBpg5l6G^a3;PnEPs!sIM1uLX z9#@e^5s^N{-i`>vdZJk*Pg>5d=sMomQ#orca*!-X+WI7+)pt6*zF410a4@&3#+duG z%WU>T=Rp8*Xr}Pj7VNbn1y%*y?3&o+<){t&e12+XBh3ktmjIK` z4=!qgNJWwhEaf>O7bzxA+Xjl<# zLScvZ-@%NlprX9DqWM8U;AO=jWr%o5yFCd31np|;qhCYa49^ubWrkZwXGZGt=87x2 zR%@gqh2|rGciwMcwOg+0nj&davpNXC)FbreoW*cR9Mm!v%zN*k__S-{m8CGn zKn^O^?4Pc#J&T0JWXBTAi6aWxZ1Xp}tzr7Jjp1)U|JJYbCy+n=(a%1df2H|9yLNup zi$6@zZ~Z<01N&$6_J1%<@Kzo^t83oS;y_2OZU@~|mZmjbX#BBXi4289PvQRWRj-XS z_9vnX{)4`d|D<0e&?2KcnfReHX?C86NB)KbnDpo*k0yEUeKEVOYIAXqR}iS36(55HU#K>+IU{rV0&K?hs<86{I+VUH?_A=%Z1oby6umY=FEMtjqrV6jq) z6OVV%yz*{#r&6c|)6VIR#p84)W{m3j-m@fm-LEm*!CsL{G?s$9ilqL<>hx5OsS zf6Hksm_zS|?TOhaX<}Z!?(=nx8`_LP2WxE{pecw|8}kP=M?MwY@TWRbd+#jZHF0n} zTV!BIruSgy97m4zk^v@Fg!JNXAjk+){`#nfJkHP}*Lbh!wLmnplfGE<1aPb8OTy)LPzA0kwGOgGAKQsyQQ^GOE+?# z1Iuo)y0ju{f$#k7K_1)3FVCiZ@p*?5z_kp5L-nETY)lxmF^QxhFUJFRwsw}QAl48q zQ*~drx1|b0zDf)juPGKK`k0HJx(ej}ytA16@kryI_3shruftP04_C4vOR_kV^&?>*B!TxsNluKx(Vw)1dj9)=MO7kCW%yWI81#$2;K`Tmu$ zB0E`0giBrYOufdwxW+kog|~0tcEjf2mm9Qk>A7onjP+SZqb7w#%g)qK)i`Dmz?_ts z=K~)X@?6`Ef7D>0;PHb&Ffssl4d2Xzejoci>i>1P@7b5ocBCvl|yN+54!TFzu*=;dq?Dgi^)`s_DsU9WlU85-7%hGGvvq4i|wqckcM>e zPl|V6kh;;o331Mjvh93a_Ssl52(zi>dJm-)=T2!aZ zcaZRa6PV-uOep(xG_K(!G5B21F9XS;ZMt#fxqMUMI0B&ljmMxrd3^fdU`sT}Um4qi zpbpGg*?B108B^{Vp;r;x3_px6*ZS^?c9JJGk2l!3!SuUK*N{hS+-=3-l;yb6&IP$2O9^i>}*gXvviFpYawj?74`3w9%sqq8yljL8vg=84p9W4 z_(pcbIlDM^*L1W6(nEc!_5}05B!X~-w+Wqhcw{UT4^PaGG(KakqGtJh<@ww3LJmD; zq5`3k=G@}$eIK+JX?yX^=VNi#Y=A7-qBZely2W3V?KeKw|1ORa-|A}dGk)eewxI|> zmF^!x8b*h0LC*FZ%kHD}mS41HwE?A8K0fmX1%bQ~X66h|XF`zOB}0wS)x&=(FuPy< zmtmKSZ9z&iPm@aD1zv$dCI;6Sh3VEPDc4)9)frt|^a*^-MW(?2tj3fDiTAWY z_ZRIDfFafH6&_pWwNc?>F{}3c+>bN63-k#DuvSsng#bpwPlpk}1JZZ@(S?x8yF_Dj zzPo~2Rt~!}mCtG^=4$PBvD$?yW~i~}8 z%k93as)5<|SFc3nH$(fzEvC;j9h{i3#2-Zbh&@qKCa223n^EGwt2t-YK8P`;h1Ww7 z=(uqIYy<%mcBoZtTw=X?*L;y#bD?wp>X55n{p{;Zx9qa?^Ot+T>T7j~%09ADpm`{z z)+rYfa{J6bW#9VF-`NjZVNSAb2eVEeXMmZpW*^K|^x3s+uX`ZV_Ac#B zyY1Q0!2Bsag%cr-$bu5sqahDD-#9$tc4<~r;b`&5p*-(OWH~PFCIYznXQvW#f{dr2 zG1MA{0FY0o8D3@6ZR`I~Xc#=9SsXY(0A-ikv--1B%a>>Ef|oNQpG5UTT|}IfD8KJx zjlfpgDP8V%OV3S)4Sg+wy87H90?(bM7cZ@5R7prpxj@eWWC7>5PB@jxWLf3n%98TE zp|uX#7(AFpx0{=V9!+8)E7YB-P3JSmCy^X;&!_Zlk_v>72afrSHcxwfg$Fy0TG-NX zZ;F^&RWDdK3N?!^yj&lbI=`FGzn#!=1@Qc{SuWQOy);it&vH>dWp3NPzB<9<`^4*! zJmV6O_zh)Xlr3x50X!q%TzDEjR$6xEl}fU)y=iVHwSSQ?_`+6rJ)yNOSfzZlh=+?4 zw!`tkC^YIWp&qW;gIoRGn39&eu+n!%9|vRV>_KU8no3cZ*bJ(ttXq{G1YN<9@H$ti zCz;FQuZIhr4u8CRI&`GR-426KDLy_0Z5MH?O=yGbb;P6()JV{%qVAh8xJ$5J zxh(Mc`3dae z#j;^_Wptl5u~mu*VSAV?8KoRxaJPp;o$dv)l@Y}onGSA4e;i*QNh;r40fm)IrFx`$ zS|O_tU|aTW!zS8~P^if%8Ig9ACBH7eGUuayuRJE~behK1bXMVYNDTklJKB$QxYK8% zcjh2HEeIf5ZIq5?;N&w71zs7sr0H?c894@F4?Kpv`;zYckvQ!w*`f467aLyMQGx2B z4C2R(N@(AF9!8aYV3<*$uci@u3ZFver{8WMtBU{ZL(L&MuI6z<7p5uNe1}C1gqaDE1S2N(xSx^ESRCRVD?BSA*S7SH z(oI~t6tkJH#4qH_G)a2Xn&e+^^tT^$Q;b*Sr5qL|w7GUJ`ygAVf(}n=yk6-_Zwz-h zIFQFSy^k|uc*lX?5+BA}4U9GJlLr)Lj}&q}9jG48tZvXac;o#^Q2s(dj6Cu%PB(A* zRZqY8phm-A@2g*d>|+IxhXKE}LWUvv@bDGR_%Y6ICD&}ed0rD;tWgSV-u7OH>oOlR zul!fc2R>RRvBIWBWQSVSqM*|Jt}J8?WE zi)XI(rDnK2T%r*+@989P*i?*~r`BN-;*OSr@<|muyAWMtu@2d#D>Sz}IFr0PIyKhc z3tc?DpD{7*FnmAIgDl+|nS1DHmrrZnYolUTW2~X*eM^`8p1^B(uG}2IO(|1Us?t;S zWY=o>Z^H059yL6h*6m_No~h zNYerBM)qu6!Sk8TbC#8lpC2-WZ)@z$PAO4G&X9R(Xx-NEBb*HClyd%L{{h3Xy6D|@ zO(W8lA%K5${(ii$MHbeiadweKOzWS`xQ-t0_(Ud-wT}nqE1*RE_eFTdlyd3GE{-N2 zCwE!+Qgl6c<7S!u%A@ORUAnhY?%a(a2qIT{x zdM=l%voTH{8*Af6+RF>-A_*w+=V!oJHASNnINjE6DOOwS4@dgYRZX3SWYa>3Qh{ZH z+)mYbWo)50d*O&mc_+lRQZ2Ol8Ku@XJ1R%?;colxH3Rn-o7#i4sScT~Iw$2>RU`M6 z41^|U^gh<*GuIe@9KoH`XV4%BULh(Kcz5cpAkX@F|4D)AUSX(TZmI@+G>S@XP(XS{ z8gv%6m4UaHRaYcp7-BE`H;K8fU2&`*oR|V^9>_XntPkrvwm5&*Ri<@^?U8+`?31XQ z-TbHl&skWRm#CvWBWnmDe4+lK_=#YGrGekqM@(B@9E#+c7 zpIc{^BiJXR#ZsryW6@iOMqlBUhm9To{rBX8eBBHevX44x6%5?#R#aCAup;jbeqncw zm`b%7MTW&F$b+WedGtU$Ur0wO(dHKxi%4T9^ z(&w>dp?U5|t@b0|RzQw?TLI`I{Ol0<;)k}MFMfEh@;}%9hc5pk6aNK$Pz=2*B~jZh zp@e)&>zh`BHwg;KFOMB${LxaO_6_5|=?R3TLUGkX{l|7T_`x~B@j82?-r#2TDaDB5 zX?o_UUVGkes7Xoq71bQdIGY;$km;5}%axTobu6im9YZSlvU&vro6z`@sE_E4nOIP& z$*N7xsbO^mdk{!Lp~DKd4iljvJ%dx!M+&cM3~Hi#)`~Dp+xx;p?wK(Uxusoj`Ku+Y z1-b4P9ElVNTvYnHX=~dq$uMDMaki_~-4&at_;%w2z~|6ufj>YNICPnjG>Y=!mN(Dp zJ!IAxd$QNi^-LA(XZ3c?wIqqQmG+qR#4Hs7ND9uKE<>BYc(E1B6}p!QzsM;MJysQs zD|J`XZ6d18GetRMb~FiD{R~1oCZ&G{??u*4R@pKf1{T=;aUO1ANH-Fzj+U;M!6Rn$%18DFiTnAO37uli&Ow{W8aW z&=>&>5!MjFAK8nb{!sF0C(;J)a%J`fy>+T(hJ*gy;}58A%(OAKE$yNb+wt%9(qn}g z6PyE??Nf}H=qaVacDxU8(VZgDpZN@a8E$Cv$eZ@4h0i!vQn@agF@%Y`tX@5L>de6w*f(AvGW zFtaKQ%ymb(ZYU3*5+g30!*|JcrlX#!k@70+ur>BT3);yzxkGO!2A4c}5Tziw+f~^Y zV6rN9AWFcsPD@iY#KTba`0);oG()Wr`$5_3=km&2lRfl!{I@=Iy82Oea#~r>;=(6~ z%Y&U8FT_tzr7VJ=tkB3r=Sqi4d%PN6`mT2I-Z1ssz-awGZG6sS2b!%?nG^Q}%_t?y zhc7<)O)zc4wn&J}!NMAgZ~WyJl4&*B$Cc4X&5T{pBG6zsq{fUsg=OS0`zgs6HY?Rv zkonz{kITJtFG`igwpq9F3>&u2zV*Whc|d-uFw-}8U*={wX#LHBej<>>3&e<+JZf~${4xGyyw zO`^3SO34NgsTem0YNKyjoIcrOC>Su38=JmQ)RTChhVj&?N_4!l-O*I591*na5>PUA z-LwxgzIYg3>{rKZ7q8ewX}G+DW-8c88}i~Prb@}RqJ+4HKg*QZi9Lei^@xwQj6~%v zo#+dGO<3Q#$OYoy8tIpX7?Yq3Ld`6Z{aK61 zMx5>{kiZmfHBzF(cI|fz}P#@`!b34Ffas zu~~cs1MHMwv~1C^vahc)^C#DzYuE8J+ojGH)`e3W7;`awiU|cd#nKxPr{I}ez2Yh! zUWm^KT3KomSLrmWc;9jOC{l17^Vy_QY9#nYp6(5NelMBtwVw%LC9|{ZQPbcTW4p{S z!$^j%7vLN5PQ>&6D?PRo$$|?xaZS7Tx3>qT3@Z;!wbT8FW-xOmtDVb{dm*5nZADRvRCGjR+Ge;{lLhAfl&N2gIaB%dT3ZLx`gQd(^P^clA5Ri zJEw~NeH)pI%BcCI55{q>6C*Knk`lt3{p_Bhf{-p}kDMxH(PpTmEVZ15#=R~=fQ$uX zGN7>@_Hvw;yk(9Nrl4`qzrj;h7cpf;M(bVY;Nc*D`@?Bye<1d!&L{Q*5lUu0LR4+% z^a%=4E0*0hEfCmazD{k@=1j&&rWXc3)SgUN+`}RcC$i*nA+2+JWu932Vi2?oaHGia z`KW5Y;YKlda{N^+PDF?2jZ_Ka@bEU=Zequ{Y9PAuRS%(Mc%4I%-N5|rGv|SK73Vg6 zA?ZhFy*MvSQfx$Mm+E-4jC&$K1dIf{U$iESYq=q#f?vK%uk_!yQ?_G3%8TiletT!x==VY`oxa?R@vFyK}YWZ z>~IBsDBK~b2da={Dc)-jtKhe0Tsf5oyU5o>Ei7lg2V1WgYM*AF*|{7%RGq@G>&<_Q zkuEMO{xU=m&-w*-b`UsIncl0@50!e6nv(eNq=IH6mc8w_XL0R36T zK+ca6*rG;5Nws!5;dC2mUAQ?PgQh>LDpSd}JQssXf%-uvCDT3223(|%+)B`DoMTP2 z6nCPqCsT6&uHL<~tOW6urk@W$^WLcN65;%!7r3jMT{FUEx8(kvJw2Vk^g%2uuEFu5 zo0p?cm=qaAce7Xr#yK>3<-f4LLgw`9u*bsp!;@*nEQOAxnbbHMkO?hUakR#lk|2&a za%*no$&W-2)&_wR`BKLlckrrDeC{D18A=c*b^bO5>&kyH=BmMEY)MhZ5aKy+z$$4- zlt;i`y)&oQ1fDl6gLZP;x5fK$Uu8Ejr;+%XM>4VoN zr8itI0;2*aC)ytI;AHg1qk6sgibWoC77=;KBXzGYmxsPOVS~bChNU2b(jODHOu9dy zA2YJQsYOe<=hEoEdJB7f-uTVd{{4DiS^jK!esGkD-FrvD8%jhup|yK*;!b)3pPFkas-}5?u1${lIU606b~(e9mI@yOD2e90p-B(T8s8WDkHLZ!4{>8N@P0L8?N0 zc_;xi3OC;qPuU+AR9<{ThDq*?NB48>$e4d5e|%E$G@eSa#ut)R>X_7p(;mvEm$~yu znlW&aO*Zygj@o9+KL4#5fT(gcDA*WgZY*Wli`b#Mso zO=uF_9fCUqcXw&rf?EPX!u)!sYUaG}Ju`Ffx%Zq~zguC^X&bswZ7}K z0@Ap8Exp6* zN)fLlGKG6ApnKZ}O)JCZRuPwP5ca%{wLK(EVD68L$#zuSJH&3}5AI+czT@z`YwVck zbujMtX0BmdLR#GYP`~$?w_XH9ys#? znUi=s6;_Zsyk}b_Z?%e3;9~u@Qdc#tY3F#EG}Ta{ro>khVQBKpZhYTc$0?`6CC{%Y zu%rAF9mnF?>|t&72~!m(J9;8miB zgH7l_ze1N4JE4uNNem#t_%#FdT7(2|e%odLrt@{l%--|V)S7pP{OR(f|J1pqqu0>7 zSSn?D;>Wz%?FGipnU`u`Ze_h^`{}(8!~M5%Qd`h!e^kE;K+-`Ztr|}gHVt!z@?ZSU z0Scg3csLDA^UySix~*McmEy12>7=m5dMMufn1ZLCT};-Fn~4bm${9NX#B^J;ve-f! zlH^I@6Cx3;j!(|Ppy_VR6YR73$6fQ}x5nnCuI6jn81|WwtSA!Ek|Ob5Zvk@KFi#Q8 z;ivWr6R{1l6ju=wOZ7g6uuxnj@Besrf0`zQ@2dZqVEyxdPXy%jcmjVJ!Au&U$`guh z$v!IcNn2_g9l5e#c@P%(>F;csLga>+Ia$~G?yCs&Vd*?GN!nY6*@Az9yJ@`rE&cq# zC}C0L7zlqMyk-A(F&1>?rT0%bI7@3kaqo}*x+60BE4%RQA@b?RU(0UU;W3tl z_x~A@gZbVC&POSNyDBcv9Er`xG&i$w9|kT$BGK{xPe2~_fN$z>**O&M8GZZJ_7yAe z9sW?}se0?r?&ACZBhc#qHjnD>Hjp5g1L|6v#1W+~^e`~chRt36lZzPs(IB3G8eYZU zIYNJ+VLATE9`mj+6C*kW;!z;Sqz?ON==i|r%XMV}#dAeI+<)+fME{QBLizef>&XAf z&gEVIAQAmz_{3eCaF&fSeCxaUWJ2=?E0_OgTPZTYj8!43S5z^hh;*ifA5xB#{~HNk z{}>X%3|6!51ZOgk&MII})GEmpO}%gSpD~vH_nmZNe=w@f-*4;>bSO`*`#18`9<@@3 zONcrD6E?(2Hc0h;qpPQf92XagtJYije>&-Smq_%O3B2l3kt)Ld+`GAJtVNsEyYc++ zuoeHfwvWI5=Q)QwXKZ4etGGC-jZ;l@HU=2&`E$i~LO&cX)i#JIT>gL1tY-Dd$s zyvve{51|O)iY#&Ud2)}o#f`-ciHzNMJR>YAt)q$CrX|gk*^bL<%|A=-V^?M12(>DI zW!oUW27ZkZH@K7aCcfbJtP8XLe#91;zO{hoo$)v2MZ?2BH;1K5zVcW%(ZXO!laN?u z%^??Kq$w)q)ECL?K{>D&g{FgzL{saIdg{mg%8VViZlje|gku!YSB{>#_mo?KKO;?r zx3&<6{q2j5BOirFM6e@((K|Muc*+q0K`CjbH1>xjXoNKjhAOPFt34t-1^KlvO`#^H ztCJ!a*JI2r@l7rvpZX=40%zN;kUlM{VYvGbd{{fjqjLJ#PKoW_=Iy^O6@LVI-iXfM!PR9tNSRzkg1%r zVs#Ipdq<7&pf0YM;5uSQcddG9;a;sspjN18KN|8*M&E*NhYyM_u8o6+@zcM@`ejkX z>#~}>c|Ulz>wei?GDeqcJDsHqH06@pd@gWCRJ zkFXoJ=^ZF}K~u)_x25&cEPWq16_ev_U{67eBJi5QPV|V}MhDNo$6LYUy4GSWQQK`b zKdpZepK-4xIk$0{dJA;@xkW0(IFo9_>@aswKUk8xRNHw3cU8U>&Wbeh&0c%}^6TEL ztB!B=VD=A4oxyxumL+ap7QDRiqf~a>p-}D=KT94GOq>X1f8tk1^1uCOzFx3cRrq@QCB*SUq0SI#IEC@Vt`0 z_1$NT14q>>iTMi+i=gr!mQ9KXnIFpvFgjxBgsr5{>oT02IeL|0W++ZHR`shFPM12R z>DRRTIJ1A@>D@(&%f@P^rfaO9XJ@NU&V%l&mx3)u>#6dTB4Mh&qoHpvF))|4zkjPR z4xD|}NEF(|T2G7#_5gX<6DDD7-HZoyN@*jayOVsu^WP7=%LOWz`X;O?g|336FxX|* zq7AG+@|r7*Fa#QfWxm=y%X|@}SLUa0x=7qPkFZACHf?&{P4Sj`viSWIP|k*}(SlInOLUY{?N{&=-PaS1 zM}-VtU0!NnV@nE`5v;NJSHYtxp?sUHoMimk{S98g2F78qiX(4%r6g{S71M*ZYG&RQ z-Jp^Y?mr_8hDQIti4FW`uiyV&5r_ZJ$GYgd(rXO~WV5SK z9>W}Sc|~;~8X~|J&K~rRujrOa z?b#PzP`@P&2lWL+YQZ<_{6W9);4nodT^H{cTqany)0$vf$ocd6fr1h9#eUh)7sAi? z>R8U<0hek`=s(akNw)-iTwE|^%s$qIM_YP%X@pZm4i~VuY*FGvRHyzlY>hu%&d&kb z2(O~vOSEMBJ{>RnbvN6$n)*H07o{ZaN{5k9+)@8RrY?D8o2>F9jpIv9$LK=hl4vs> zsk*qZ2)4q6Od{HZ)=92^a|?``KB^B@kz*8mu)z$82)pX4iS^Z07+daI0kf>VfQy;; zIS^_Y0Yv=Z%H@8d7JBVxP-NWLeqo9x>M80utUGjidb7Jo#Q@wTx~r!GD`l@^ZCD^pNGjFbi(o@pq<&AnZT z5NS^}M(q3pbV7s%D~k7#8Q77{L$KCyC8a*ZR*Q^KH)=%FuF*q@^M&ax&A+x5ky0m| z#<$0WF5p3rO}g4`4!=wSuOuOWbT|Zoh9&f~Gk`3hALpYLMBh%g&8T{`8N=VSdDcC*?%p?7d4mIA~P0a@FDSA?W?W9ghAj_rXkF z`i_DB{R|`Y^w^eo#xO&I7yB4_U70Y`uN5cE(_KorR|3n4|3 zFv(zvUD)Bjy%Lc@RnE`fKmpRO2r0YdK9o%FJD)dvS!Ine+RH}fTzgrglH%=1B_3xn zRolv3W{ciS0{qE}2%$l=@{jY3(a3~!;N+=cl8X-jH0ggmdHGE9SDvL@!@gVbM^j)F zkod47Cs^ytP1GuPD0Zr@uWSgIN1M9Exv2=hz(_w3d2Fd(uUo?0htd|>^`X4jx>rym z8{P%%GzIpR!BdwM;!o&*GtX0og}~K|Nr~{x9n3s$;ZrL(QG8h z2$f|c776Pn9RZg9q{$-2Oqo#!!`Y39De&i6+7kaXFAxH6Lij3Bsr4(nJSTuwoIJNzjELY74}_XOCEYC5Z#}1qpZ7hxps`}aD(%+OO zYl>mj`mIJe_P|#IUkS$58%nPkDCIsW{hGEE^&n*@i^AlQYtBV%x>-YX`b~82aP^V`B1CG%U24>*dq|Krl^BU zlg_0u&qQ2fwF8bE!Z-J8=r{P5*VYu$ZU-{6C0fma@+{adeGRYKKU^5ic?r^WW$V!z zuaXt$Z?Jk=uh!AAXK2$k1CZXV`qCn4(%>DXk>|}7WJ`g9=%Y3RHa%-$B z?~hMRzHVZe=W`sGSIsCikOT)%D!U1`w!V=kbTZI7K;Nq;^ar%`@+&>%)%}=4TC>k) z4AAA9b9H8FH9$=dJ?$upMQZ^dkO(b?GQT|4SiVikVd-ipWSZOxf!2M6X-}BsoUZ0%kxnXsd zB6HpWQ?b>aogZ%3^c&`!8rpe74O}K3YcWJ2viPs}f-&r#otg;weJIJC!eS8MJsnt zo7u&U{{kRB>sp?A%Z#}DD@-kBXO^sM3BO&&F5BoegsWp)2bQbt?uy8T{nuh|-qRT@NIhDU?9`*XUj&tm4@14)8n_W{C*$*)&Ahdd)9^qv+6^V>9whrXvVp7JQ>36bmNk9ZLjrLGjW+)Hf6^fymLR6OYAJ# z+xhSCdS1sYTGC7jOZ5QJb>%e=nG2h=s}ayhKYZ)cTk!O%OVbWLmuv9z&e3UbT>Mxs zLek91Y?pEOd5kBkwjYC$dkuHnu@B^BNnL`t%(wEbNv`3Q{<+vJhosL9!1rl9K2Q6M z)_E8v!G=q6R6ZB|ZXcpSE{RgqU!P@LFj+=b%BDlbRbg;(Ajv&;28joVG~2*`OM z>ke@^H^KdQFg{{2v`E#N!r8Nn!Z!g;gU9@kHwL;3pigu?04VXM9}>lb+c4_>VxzTf zX^%OB>YWydjmysf#atLt6%7mBXct@ zOB`oLXpAx?ip>b4JTMN{GV4OpFTT1+QX_pRi`@y+S|jrrqG;!YN>>m5J`ILY25g|> zfPJ6I!S7cvZ%@dcdv0Ie#%76(x4y3j2LZY7!$#vfDpUK+M(9S?yU`RxO$r*@VZbyO zJTHY6Hd~hlV&p^yjK`}+gL>-i2Ir#*CrNJD+JGAwlHVm#pY+0O|F`CtgZ?m>;6HA5DD7t1orT3f^VJD zKdyd2ZBB*S9vm4!vvAe_{@}BWt@EDqu{|q7mF861}efkk+T~ zWnyR)8(X19Z`;cKu8HC7yk4437JJ0~1#{Xyg`l?Vp|b*N!LQMIhs9D|UnlC>0|WBo zL0=ngdB!&SeBlVt1{i?Cm;vxevU*SP{fIZoc!GZ9<;Xz;|J(>6bGs=?b4+&aN74$4&J{Gnbd(w6%q|QcyAzo z;mr!1-PK7@{F(6@WCaz`jHOWIn=k531IDdeItPV)6$LnR#_*;sGR!X8ukIL_qQeZ8 z%)$5uUy#@Q>$(~CoQGNZO6lW044#KtXGMoXMT{z%V-5@wklm^pW}%Z3q+D1jgFHVe zS7!Of)LyPJc2=g?wRxRQmS*HfTcs-&d~UF3${D%gAO&0v0w`MtO%EzcCU&hN)#g0| z^b{LoCw-tcO)hT~UD!|vhgegcRP_l8{XbtwP!MM`&$=*0tiQ@;jV`*@gvScal4|8$ zy%q98Q)Aw7QNX($?^zV24LndXVf)a%xMr(_Ubpqs$;GGZzK~HM7ec*TEwRK?(3CSN z34yH1&x6#Dbkp=qwWK+|6tP#jj=gk=(aiWfJo?Lt=ZsEbm3yFI{+Y_YIgPl=1t+~m z+L}0<;8D$JFoHDJ>Dpio{g?NM8?E5GuV}6)W1JMcWnUn98hnw3Q{1bWMENiA>#Arq6i|K*`&+lw-kT2lOtM38=3dHJ zE8B-VQO_d@$~E7x_w!G@=@F3ya-3o_en_k93Wa$eRih!K9ENr~Xvo2oHz6&ai2*Lu z1=wMVv1R!XyE*&&trU(wX9Oz;c?G{;M$ z^vln6YzB!EW~JS<&qZ-T;;}SFn8mel>hu@t06xYxy?706SsqGy3%*@*+BCXPNxC9A z$f6t6(+yD#4`-Swe^s z(TDOOT>F>64c#JS@=ANu%9Nf!>iK$7t8B1DR3ri=<`)tVG_)7qad@6+A3v?FNZi#q zHP2sgE)SCPo4azLc8_-Fy>AHE^0;=3y95~j>Qm~hYYXd3UyLt3k=xL%t5;rzO|axy z8>69-QCUo%;2xW#6}sgnHJW9c#!N?JrVRFz3LO5VR7L2Rg&`avlrQY;!~ZG_je)h2M*JU z-6}C`L74poaI%@W8?7PZWgla|n@G=fq995Vo+XTXWXTn+vYP4%#dnckcdW8oye>at z^PQ!Y>hNwlMv%Yg)JE+iuO;h#g8pt20JT4xYgVT+F=t=ijjeWg$xRs{Y%ID6w4HJX zYCy%LdzZ(C0YIX?@=gG4Xfdt|3zw`E)@E^#)&N1lxyd0f4p|D#;hi~VwFaUIfO-MBd0WEs&N*!yIkI-RW(Z`a~v#Ih2g_Q--4zZ%NIr zOSMY0*GZ?Uqhbf!gL6em+KP`GcCP}1i!eZfZb(dd^4DSNdv$UVHFVy1Ke5^}wYLik zE3NFs6K-a)kEQu*jjtG6K2p}cRpl~Dcm9H!>O5^U;{AQnIHRJ~nL-dLBKdR3(7WBQ zWx41-tJEGD-U&$22+KiPeGAwdcp<~J#RomT=UyX#ByI_&u!!`Fs0+@(3CF@!w1yVq zhXke@eA3Q}Fbb;h$d!ZCR>_-JLc9KH_3wYhpv)CrK35b!--<+?pOY`_CMaZCs~o35 ztvO=QDU&{Y-5^irNaauGv}tcm*tve#JtM&-lw-a_pM8Um$)(Z6JE?z@(oG9MqzW8V zQ9tr%fTr!QScD$=*SnH1dCH%U@&^x@GfGl~X0vL-3kPcg=SqLqYjtMdjHQh?H%_Iw z;Na-pS^l=fqFbkP526u&JiZGyoy|8Nw<^Hbtg@mjrDQ}~Xr0lOhLBu-!xJM#n8 zFB)lG2xI!oe_9x&sqtU!xbGz|acJAKNsiE`uE3HkbtV{}1m6tA!Ka_#0iou%uQIVlp)#<<5{KLU^?A-+^* zK-_Tip*Y)n2UZY?6r^}m4iHy{UaKgv3!}8#yWinD*6I58$&f;jC|`SFuN#Fmwk8B& z+X?7hZ^G=-ot7YZiX6a3x#aocUQB$*i>H zTJvaNyK)#L>s5WpufS_ReXlIx!DHnWT~gVJ^0Cet#$@QC4u=L5C?O%7l z!t~}Utx{B@CT>TnbQS3ouHo|gKFZ{T-$J=K#>auc1*Z~I0wOTy>MEF9p3Xw zpuQz(h~pKt_nQ_5Ggw~jH2TuOYNhUnz1uOnTDxc;Ki3pCNaHN~!YcYqTuz$^C=n<~ zF-Rm8{x;0lQPQeYZiaU@?VS2brQBjWj2Kl_%$+nPC0o@&v4Nd>$H?Ahw$ci+sGGgM zv5pZV2Rc1gxYutUSJ=z`FpVZ&>>H_{rqSRxR0|n=)ajTrkD%~LQ1DN2Yg`$RqoId| zB7Wt}yb7}%e0cojJUXPVAo?@GD4JTFuV90rz9o$ejq-E%eHcRKE-ij5#}}vDXKk(_ z=i_T-tXi3jywpDDRDstBlXt)*kCsCrp%Tly2^TQcz)Okl z=*El;g&+6TIJwDvPTN%$!U6MfRL#xlQc2w9tv$P$IQhLxec3cE^Xxx5xo1axt9D!4 z5_*Rmd98-=cKF6J3xkmYEV?QNP!{26um3*=_aUz#{D(ro7n3fr&+A>=-?EIw~s` zg}&u}2J&lFef;PE>^Z0}h{j#HnmXUtRFIBv%4!Si2_!P|X?~Aw*Nf)m6Q?iR_(k%C z!dj?=4d50Q;IR9>iN>i7-hnlANvk!g|KZ~s`NLfR?&q>e{%RyE|AMd>z|d7!*E2<3 z?K}bQTTAGUeBy-NUDWA0JhK1^lF*ONx5Qn*r z)Lmq1yfcjul`9%U-r#wG(yY~{OORLPu^|NchBi^XDU=73d)F89g|OJ7bMfd?h^@0n43Vq2xjbChu#2Tk$^QU`fben z8Wp6Vp@D_~VmSuqbGJ+!^8JA@hiAqs)mU(V_bo}=+j8_UW;_h+d6)e-GXg@&%*;ms znuh4*>e`r_8=Gqn0SQErlqEj|^0(M%wUeV5ep5%JaRM{SVxC<};}buQu~7{LHJFHp z(2CRGMPd@XOT!kTIG({o-Y>0@S?9e_uvaXY*q(dM(C>#2@@vhJFQlO1(Mz=taqvkW z(zxw?D`uL-rpteU-JaIc24#NXA0a{HKT?Ab=o43m%(jlU*3hYWqdtR`WTJwszzH(& z30IT`>7gZlX7w*=!XRZ}TITf=d4GCQr{CiOeoU+f+m(sYuhqNA{f7At-Dyp$qcIiz zD(L?J&t2Y9NZXe%w_)oUeicqE%HK&V9mSpy;Z&g$X*8!5`OPfQg@Df&jgoCloD+dnlnq`RZPs~rKN z42{(^Svcps(QHL>ts3FY;N~d)i4+3f<>FA!4I2J1 zRE>RmG-sP*TopzmsB$&C7407ZNu`N_bvps)EMb-rz&xQxAU?&vOm z-MTR(PfrV(d#^$y$>TMZ--{Iex|uW4x86(YJ&u-+B@-CQ?8(nlE;TMXnw9+9Bzvop ztMEuyU=6p`DW;WzGnsLUR$bdi)XWhb+XZic04f~>>ETdT6SZe1SQqC@G9Oa7nRFX zlJvA&|648NVS=+QZ^$~^S%JvT^71l{o*-lN5N2#P=B(y%pVF1d#Q62`hluPH0u=0dZOsH_pX4sOX@k94uiaoiJ<}1^ zS(v#=fYTgNr%09oH7tZdd9*xIxGYwJGO%dpxAHr3h*%C~%8{ZE*z3AZ?zW7~*INK6 z0*!ZBA8f{~SwL_ZmGR`FJ3V3KlHJI^vg_BHqiFJVG1EU)*)UpEgk&WjnyjgwSg zO1^--?HwS^n$g&M0oU9!O1@P<`5dz5_Oskum^iUmmQt5oC%({a)K(^RodJ_hGrSuq zf<@`D8b=tumdbq7bi#BXlMjMt0;X%RM$*V3MPX+(Qa14RZM7i;h!!lB0>M3LY1Pl% zOA%Rxu-9|aCry_o24PNn29;^VwS*~mIbC$lAu!P{%cC1}KTHz_S$O84d6u0Xs{9dh zH1JtXSchKLeNFzeG{3Y*#FP*kfpnhT?KNx+C)JWNO|$Y22l^eWj@3oa0&Tpf%S?^- zW-(aEFOlTL(8JhJECaQ`%ieo&P6b)OF8;C9S{H**0L3@MnXGjsIWwt=?Q|%fcujkO z-L|HsHf=11`Dq^ux#=cU=2C7m5SfyLt3C7gM*h^PyNnB4M`Xf<(}A6<%!)eE9d74! zIN$+c6)t_u_^vxu!5KcV{_{u9>Z+zANyfRM#NC@QTNWZcMoW{)tx9t_3es)2+91|n zoT{^xKhESZ3PNYt&1TPM(jD{gFxwbj@MZ+lb}=NG%j3sSm@sCi6d&D@=n)$R6gAwr zbR8&|Yujf4_lLZpR~jlXrLkZos0Z7V7QVk zI-iKpdylk)pdR7LP2I>;(G$1^eUNg|9IW5I@`*}0YtuQ4Os#s}TK>Ai z0Ovw|IV9-PZ?oQJ-v>=_9~Q%cX&Q1=A_6q0G}h^j4GWXYG~Zl}G`qi{y=rA}ilfXu zL}zK4D!Puw7NpJtU%M&$bBQ>fOb-n0;Y#MLuNj{NSBP z@!ntp!6tJJ_MVl6dn_sztOA#~OPy0T!J0LQv%NU+)Y!A03vbJBWP@CnAn5zU*4K2h zbn1iN2ih-2Eiqfa6tR|Ozr2LGua40~{wRdZ(b#y^#gV!W4E80a9Wb_>E1qadZ&#tN zX$u#UzsexaHOX8rDrYOo5J|Y?NrCUe-0hGekIH)XQVjZ8eWh;?T+9GcPiXQX)= zqFKRJWT^#C!d9el*4|tjtjFn{XX{lz2R@~_zKSd+`5a_7id~qj?Dlf>GrVMB)^Ir~ zj4vjK;X`99{O=&v%PL2w2VxmRZR1CwNrMQ zN6W|ek1RuvYR^G|li5D&Yj;=e=T}Q5L>7*iBC%tRBUQAJ<0YS!1K%FsW-R_i(Q0`# zDm$dadE3P&$-GS!)I3XD2QS>ZJ_M+d0Y=0RnNjEDXllFod6YOjD~*)6%yw3=i>PJM zk(}Ue0N;6Wd+75HNK>3otpEGv2rDb=$Il@5Z*=wmt<3DJAMDi1V-+L36(nu#Q9T|! zMtR@@TEv9qF%AH!s4I)|TPBq`2y*S*8M`L3XSi9ox?FWnCI=eAXGDo(iC4jb->4k3 z;XuQW{Le<%Bsz0CAP!T%5WcTj0EHeYuGa|qDNxNxxX31S^dv9HijRiS{=IFAPD4jP z+TBdNl39{WFiPAYgfvVfxMfu*m5KHouw)>Lw{A4%;zsBp$wCMbBIIsM4n-HmlPAsn zmk-5UT=7|dk80y(%yntigjsB<5ob?Wp=`&H?<{j~Z0H=~TrP(Uz^&c+FMu@5zGuco zP8m1@B+Prs`|g`n3xtNAFN$<0NhB|;hJS2^T>AQD51rheA#oz^T1OSjp!gZq?`BXN zZo{F_jo|%FYun)kjbVz)tmiuUdex6kvDraX_cN+WRsv`@8Jz@KH-BVYu&Lu}WFoxM7VcEc^Pc#f zfSnCTHeP><#c;0$cjx^+PB7^@5+4XZrRX!nWIX1NF8=h*^)3daMS?|I!|isH)SsDo z?V&NTEshN{STmT|{>`FnWU^@+ep>LFco(`_XzmUcgl|0qlXu#GTRd>$nHZeTLX24L z3L-U<4Vl^^Q1A26iCwbyC2h?c^;kkeHe0_skcUr(+k4W^H`u?_uaS#Tq+90&c@VsL z#qU-VDT<1G)^o3jJ*`-HRPT^<3^}?@ES1WjZ@1GPjh%Y&YnX<|71!fLuBh;+ZK7@M zI6a`~$BXSnky2e9>`-nbM4nK&fC+$QQ>%M=LZjE)iK6P*d?DpVM_Y&ipjk04X1iM* zIT*Cue=7RajU}Q+?3?vL(gm=|kP&#QSfV{@=vy zU!@dJluMT@m#m8Ay>N;-%hO`v6lL9|^`Gc|J5{9>*&9gFT;EzB+4rFB-E0J}@-mPk zFezbemFP3O=9z`Ro?XtHxO!zF#Y{5xbGd-=NE)N~|f>-Ycw6 z%2g+^6)aS|MIVesKUiUTzM9{4D8c=10)q=Y8IrH9J654Z56BvFQ;q4-k>oA}A9 z_-|xQKg=@k)&NVHjsQ2l5Es;1EF*Fe%n}yR3iyV0J0>e+NosUv|K5E7G%~#svWnz)_qquwBuLS5JjWus2o->ImiRl zYFz(OI@9GrVWCgqEr&t5uljmm`^(Q0Y+Wi&xrD)UzjYhdcvH34#Aj6J`TDVR+%*KE z@>scnCk#LiKjjN5efujD*pzj^EveXSnRf-5;(mygy@ljAUo*NisE$_6cB^svT`c1) z{EV;0RI_h)6fJM^6U}TA6so`Y*PU0i@YOQ84bBZJZXx9?G&ueRfaja|XbJ}odyJX8 z+?uQg>>3W=i4go$UAQq6{{-vb6yaQVvI@pIjP|NRGni=%!e09oD`%p4yWxnw1^J1D zk@e18Dbz?Lfh#niwB_n&YEkF@`r?-|XfXuPodkAYx4vM zDW+FeZKj>(g)8=R8}Kg7fu0(tSTiF;bDU6<1s~KV;JM(c?}#YC_UM@k`quD9XU<<7 zH5K?lTm1;WD2Pd&k<(;jjCsRc4e;kb-~HZ{Q|YGcs@N89SUYxK z_7xVf?U*jAt%+hR=lhsze2(V0jojrPY{{POb5{Cu3gdVQ+Ez*2CjRtp|9QsVOXUi; zS&~<)lKAls9BB_hXzZ4~_7!KbEu_I~a5-a_R;8B)+dUGK9)#*;yjeLMJ0%srPuU)pe!BM?6FK%NlRpLhQ#oGR0c- z!q^jbj?A5MaY{l0dQbBW+mC-X4(TXKO^Hh?sNUeYux!5KjA>Xmk|s_cC%zzb@t|vJ zt7}}{hYd>K*BXOZBh_fon zs4LHk#2!7=Qd*2Ca&xoi3*X2AJ4Z9DnUIcwo%I>QDB89@xy2ps3jy#H(6W|&r8ON{ z9epl0Zv($N<}(%*f5kMaUQ4lI;8tc4&$@gdo~dK9P6I~s$P&Mgc~bu^Mn_Gx)o_RcjW zFkMh!Vwp9@4s$Ck>qRi)HZU%Bt-AMMz8kHzrX;Dhgk?NLa9HT@tb_flfv;i%v_AWI zSMzeAD`^&76JpE4wsqf8ZTbGK1T$4se0@sjx?KFO<9qy-O|eAKZEU+Zdtn2KtB{q2 ztFA)UE=;P6ux4NGth}dmUev4T=)n!T^&4_CVdUmm+Pvbe)DyLFb{+MsDDGR7+R=n= zQd5XHP^ee2d!~D!BJ5e4rq&q2T6guyC;q?&fW{&C*^))<#Lt!;RD9`iEWAl5!*t_SmWPz4`1@7MLoGUyB{tp!Q)6$3bw+|5oo=^{CNmBRG zs|e{PN?!N zaonY70%*bKlLgyrCTgk;-{wv$8y@|t9)Z#66n=Y_jrX*o)4Ti6w0@M<-y)q07x}Vy zp5dj(4GzjB_a98QuDOa>O*R9v7QTeZl}FHv<{HSDnc<;)kWJ|PT>&OPYSNl89|@3> z+?VP@qt*?y_eiA+z8-~0L-;e_iVWtn6R@rgbDYndt?6XjX})2rv<6e7CJ}4)%M{|1 zdXIyaSoD!-3@;a3%j`?~rjH=Z0&PV@x3Lp=&1)V+TXALr8A;CSG%2M{Rsb~(&{z26 zTO?l8b1D91cgjhf;pOInBv-VSRJ!k~z(ku}(j}|6^yG+>v29ePIg3H=dbfCc!h+c4 zRj1nDMInOGhxo-jq=Eg-wed6%v4N3)4Dw6?D;8cq_DG7sGxO!wC_Z*Q zt(L|)rbiQ)UX8LS;v_40%h3RW%sJ4&$EXC5;rzwig9u>0C4?q z_)#W*fW3^eglDQWsGJQ2 zGC5`rh~Go8cm#E@JbJC6Dn>iR@%hI~tDvnUHG3D}uLL`)!WE3qrE!V}ua_&`k1K{u z^=K)dOkYkW7Y+DreLt`Ee2gkuAcHJS*6d*9bC=oPPko_H;c*fK{yt|B`HFEoMO_Gi zH!5*xfitC#d?D(O$<{f8tH+f{WHBY*l7|q8B!{d{c;lhfN*}z@+(kaSB$J z?fbBpQJ$=2ED6-NX_2VWZuXUp@kMbbl@27rS>L$^Cu|Oml15&+k3d73Aa3D6><+~p z6*bu_=Pas(#30mWH^TgF_~MCGPW1wW91gI>d_}i04wiOamefSTB%$VQOXl^<5|Ql6 zo>Onf@9pm|k2jL)Y`4(B?#IHniy9u7l{*;QL7DpDJM;b00i z2D7;TSn69@cONkx$RL%}jtPloi?Z^^2!&jIr0xkUj@qob=bvLL4Pfxdy@V*wi0$qf z!Q%jSt~7`9$2y7lgMObeQj3%QNbYKW@$unu?@QHv7{a`U{;lxc7cg~BnSol#`z|S9 zm_H95hM0E2zH^*ZRvk!lfKc~85~zPqr2eZTk<A~LsniTt^hWQvXy^gn z0t@}wCLOWO{sWkD*$@urhHGYCg-Lf9&4$N7`J{6!+Epzah+)<>2&KX(i@Ae`NJNRb2G-c@{Hz?r9Q#V6Slh zhofNr>z{u8SA;@iQNdpT!vuZLo$%wdtj*$|_WOj{y~ie%oyrom^VP(cmC)5tT7`=$ zqp>V)0x2j>020-PQG}GlX!?A$+WU^KL~Xl9%-BwmzDHE&Qwtbe#)=13SC;s^4n@zC zssJCW&M*MmFB1~k4lo}__G;UpTYbf2EIbYmUM!ylx z!=*-ppA7q8fduxxwR;P7{_=jjeP>2A_vVdVWJE!6a?`2GA;yg3S(OTkcunMKOMK^e z;>j!eBsr2v#%Z|~Hpv@z)E6Pg>A-1YyGvydoK@l2yVkVn65X-+`S5e9R@n6=nksk# z{?ZNAj2xHbG|P260$(>9+IUWdZKfQH|y zKYHy#X%yLf-Jc86iJ}{YDdFt9bPl2;dwlSJ2=DASG)asCHn+83QJwneCK7TiCbf2c zQL3IEwR%y_xzV-qAwDGF_yEh?h0Iy&+5sqio2Wy?$e?*s$E>55)1Cjx6gsWddY zZtU4K(6_i0^$n}-6Y^B~VY%;T#t7UlNC$;2bHqEYY@>bFqP&zT>=v0EUsvzz ze``c<`GWTmPr+G0o)WhSmn}ySv_0n($a*Ear-)e>R|)beW%62EA->T%uO{gV z)@O4YpdE%3txKwuyZ(MEQy93ops=&K2LqBPvy6e6K_$L`oXR z7W98vY)?$`+O2YviuUNl*2~57GAeyTP_&XaTR)i)d+NF^c>FA+<)G{TqVdR{c^0r1 zO}Xki7N3WJ!U3ZOM+V`6DhH43v^+b+CB)0w(So+Ox zn)0o|`o})WVNMp!sr^#~{dGXuJnW9q?jc$cx$UNoY_={H@>T04 zxWDDbEi&L{apzNAL8@W-m`Bua^d*EsR|V$UH%3ck=}X-AD7?`tvi7}7OD*(NZTMmic51H$;Lf2=b+T@UuF-p(kaSdFDDb7j{=?+R5yiXuVF7I z?mO<)Hn*Bd0SM*?OHS4%!hNU09mz7lbda<<@y*ltqX(t#zV}lh>aK_y8FDt2s9S|t znFpa^ChEoxXM-Yzu`K&!3eBon{L}IxAs(;W1jC!8QH0o(aL_98YF^lYhsl~_kjt!i z{b~A*z7|$ht!>AkeHNR*uRg4f4JDVAmY+8h-(3x{_w(lxM(>G;cvMH%;GNXZa ztHf#L|03=!quOj6ZQbCdK(XQuL5ddFqD4Y*_u}qO(bAUU5=wA)EgrnZ9g0H;?iQ?t zQsmwF)*gF}wZ3uIIs4!FMIIPoK%V55dCz&xxseV2lXxr!9^Lsa47|P8zDF~k%d!CgQv=}eZMiuZdAu-j{-T$xh&7aI-SOV^2lSpjazeas zh?dBmF2VpnR7^=JkwQ*T8 z({qy50lw6&A6+VrMOp3*g2A=&GfYs*0m1`0;#3yfhBmJ398m@Yt?mBz2gX+7AfgqE z4Pzrko|;J;Jf)ejnoa% zI;YclkJ@?Ad}CHw<12s~+bu;gpaoxA_0bSr7s=Mk!A@(X#zgaaQ|9Kru|AR`=nGyfa- zG_hnq=WE1E6yYZP3~8}RZZu&p8E09FnlM8Ar|ls0F9&&}h6iNa60oSSO>4n&aFKF7Yd?jQUw?Og@+r3R^g`#IU}e+c9tS? z$7*ZEj=>uNc+ZwyU~~i{2qXvR%Ka=!UsB)K7*kNj0Ok>8Ho{kj|%(VV~J{Y&zvGvDipyQ)c2WM+Jt!TE; za&2?q)@O1gT%~rRDZ1~xFDUhzO79&@%4gSz_od207psgxdx=2a3aVue?g=vGH{(PA zqzc)=dyR%|znNbkrtkUsJqy1y&(Kp>W!Lp$7gva@ngNxhJqprc`n{V`%d;|D%!}?> zs{?TN@s}%JRQKGTVSmN7=k~#)F}-FxjySY=0~j@tf$p{2ZK#sCf;5a_pevQR$_QiP zGnO1w-}ue@PfDg-ZM-%#-WMaZ1Y>kT^i^a1^RM3!1a&P+SY+0yTVZ-CSHq7xTqWcM z?ay4v+}=i#ga=vy%#g$5PcWH5b46roaiE8ykt@=pxzx#qs3q!dKS0HRRX@Ha&z?En zPE`b*;s;k7!0)AD?ukiS)6sjs?+HAc74$Bw#DB$-A@B7~6D z?n)l1dg4P#K*NR$xbo>ISkGrke?R;spFw<%+xby_md| zC;8=GLx%Z{dfw6C<=XRXBL&DkObLTKf;OS^8RhDS=_B1kQEBQ6mXq0|#zPi)TmK$g z7j5676+KjaEqfLq2kkb3gz#Jor%Kzx5YT)t^Vwowz+zh))e#NOG|oc9}C#KE+5o-gdSj zOW&&)6uR5P(0HNVGH){V75e=m*=T}g?p)vXEJqX9QP&AjRp3NPJno+Ad_g``Y*)Jc zAe(nN^rgbt(ByM$Yu5c>~%uhxE{37=!~BGz&uCq9h09Nt|3EM$*Yl*sxo@M*Qf;9)l}=S*=fB3ki%j)v*0)4H zfDYmUk?g2GqU4bk{BiWvF%$J4fSND^$@*VZZo8M)7Q@?_>`kIDqPi0L<=Wla7ul_d zdi77O!vID@Y2)4z8(I2xeO{R*6b?f9S~y{+0!dZ;mE0^-{k5^ei4&;m2%I?y*K!5_jn z(F#Dr6dVu>r6jqm)JUWLdvdvJQRoDylIq<+nB>fZq&IZdOwPwQ&jkTj@QezyNrZ#O zgMy}+Ut@bpd-|Mst4k_h$}3d%R;*29u#N1^wFGU){IJRYEr7)?j`@3st~z@mgrdgT zzg%&{hCIXlH(q}0-PBxosiJphNN#qRLHo(RH>s2ZMoH?xyOmLVlfg7ZKw|ca(1~RA z;a7LYu-{A=VpNO~N};b+FyFt64Uu8Z`8ixAM{yH^>UB#GaJGx@v&C$;=Naom;?bt* z40k(9cNPx^`8lvvTc;bOP$8{cJ`v(uqMI*?>-XZYCo1~*=h3!zgmRA)lytb&HSm5T zouaez5CjfM z<5oVu0HQoNS)2T|k{r{F57Z3ZTWoVlW}A@U-G)o5Gt^5C*vF;cViV3dDPgcnP}3oIT(upWvTn}+CuXV&N7y)5ynrjTe zvt((U%#qdn?JDm;J|nQ3`yxd{;O~(-H+NuAu6m8P*_ujS713Qn>q#0b;8ntah%Am# zR!>|t!u;^&Z+FS9V`Azmd>u_nmaaB8q5By-)#k^Q`{?F+0T_sM;=JW)Mr;@0wt|@6Vm#8#f7wO->aS9?xRv%N$e?#WuF4V=P}m;`*2o zk8y@FJ{QRC%imle+XOcx{a>SR)PMJN61xbYu7i36p8NAatgA+m%x&;FHMnZB z+^E;jhAgM`60e;aFCOo3MQw;kKO)}?jixA3)E3}R>eBJ1t93@bYI&G-z80(FO&?+ku((BhAUq6gU`BlA##r>kAoJxP6Tb}QJ5f@GFd(CJJRO#=4VrfwkLEdAa07Oy` z(hKYqg#J*7bWy6J-*ab})*yQK72Zg7c^Dea2pPz_sM3i?NRGbDwe2K&9+1^0p}Zo&<4&yZda`Bp(YM#M@f~CpkTS@ ze`E>1(32z?H)lR5GOt^7n8AOnGv#pKuX5<{iMp`RqkrankCwHS*t7bgf&#m z)zPnC;$a7SH#P^C=mU#LKEp%+?=m(q62oLLEe4AMqo&xJV}7gCt_<~rG-77v9W8hE zm=C!d@&;K`o>`E?gwAA)W^4RtB(1Dhk^61UZ4hOdll`D~6Y(5F(}DmT$(t*iX#h2a zNuy%9T#%icbQz3B4cDgS;iuAO?@A;V92YTxwuue`?2PVIV}95Na~-j?#+1!JuL(bV z_6_K2JD#xYXwT4bEwhkgOSWd=fq$IkpX(_Uvzc|R)k*3M9KCsI#}pSGud+(n0B9#* z(veH`F6IgT2Y~NF0S#!qSUEEAI?mE@5+xe+zUQ`4cx@O*OengSk0hp~0gU1>VpOy) z@*rErADM-6%!~%{8=D67DZ$^}XTxs)HbKPBaw4%q!g!n7X3kphOeK%tNTjKxizbJn zP@~c;Hf73JcA}9Daox{DqI4~+wNa%E34T1PMzz@aUUnWHq}-==YosbGw#xvVDXV^l zHkt0Los~oCUlQgykbYF_v!l@og-N^?&uT`gF>3LiR&wQoF+!^`rBF=~()zpjJolGR zhkJr4-*H(211vtzN(z>pHgtE-*OX@&vKf~f-wXq-lm-mi#4S%%;zNo9#?T7AcDP*V z`1HHxeb-c{)CJbu%bOi%jn&lKxW0Wwwz1NB_q)%UI$st0PZ=gunaL)Im$(T3XHHEZCg;Z=08+>Izo#Qx)|hIVm%p{(1w@;P6kTQRrl?9m zV99MvUJ+GF04-wKfjr>F!hYx8_+wgO+>slmjEA@SR5rUzSCl4qrU(>KeEXJcD$L_6 z`abk;SZ0-rgnK@DlTUzy2mV#*0%8tV^e&s6El*zX>NSw4osl$6(zf~1mIQSx9y>L9 z=E8dSaKmQyK>4c{Q=nW#>Z+oVi-v~|J%tDC#jdXYmRA>3>-3Q<=$!~Pp|##{O+ zF5ST36*F0AY@Lmk##`qqlxmvLk&?u_N(<<`CuiYqsHdce-@kb3Vkz=M1y5L zOktP{azAW99by6V`%jh>%cl z5DzOw$4iMfr%h%f>g{s8M=WCvU+w+-NGZ`?qNCmU@r6+G{XTMks9eZ4PqrcknwA5!15OhE(agK$|FO#-s>~K8l|lORP1>iej?Zbz$*X- z6r>t(3ujB76PmhYKTX@bDYEcrW0GoH!PuMfd`&2|`F=^X><>LIy~FppTA>PzLDufX zxFBxO%c{m|4SM0|FMjD(H1Cz#*&`Z_16f8d(!cl~xwmc{&M3`{?3`)c9Tx*@iqr7L zxEdD1^lUL>0jLh*Pf_bmzdkj*Yf-KQGgnu#xT++o{UE+pZc{4kIl9)0RN(d6*fxxs zGZOg@_vf>@|Mc`T_DbY4FvXq5rPZ^I{HY|BIsQcpeQ}Yq1xt5V!+c1mgZcCA;WJOA z$(Oah_h1fJ%c9ldmxzs%h_$OI$Gx8+pnz@uZK_(|in`jK>6_rUJt?#1G5!f13eR2* zt}78YHPJdSV$>|OaQM1W-|Mv*Q{!<3@dAy0aO=NpzyRWJzK>0uGoLws$Zt4q6_pPE z`;BdjVFGpV6@ZifTjv*wzvuBd7atm+Q@{A@OtU}O55>=kH6@g}r#~uI^3?VJ2f)pw zrQy@YD?TMzgpzR<#B9bSL*H+M0OE}`p!p*h>onBDqE2iu8RO_TG+WdQR8^Qs)^{tdY@xpG({=6H}MG!bC)(yDr|`p3VH`a??dw zgH4U1j}td}&8-i}FNkin0lHSVpCjuA7x==(EA?aS*tlMW((^?TPk?gv*}6fs4f{=4 z*lOz*;Yvf!UCu)We9h}tv(yfj`0(Z?X7h_qbMEZ;+0P7QA)a@{7^Tw0CP@z%35@8n zP4t?~$2hq@PO&i8#B4Gdc$R0b8WgsIqI+l%ejROGFZ_e(f z7i|FRr)zbzwmapAK+Kgr3+dlvDC+u#Sx#s|rL{Zv4tb+zLKH%4*SN1t)=W&`%qAw2 zUW|e3TcfY2`SD^X^0nlKsQ}Bt_hRSdlgLfe@}Xt52uWLUN{g=OU6#k8U&0#yAFJo- z$)euv)g+>gw?LS>va&hztGXK2s6Y*S0U}5h$(PCK3r|(z2Vsk)K8aG)-oDF!F{Z*4 zq0DSLUA<-=lV+VKB|K22igp9Ftft!20Bu|W{Gfl4NdXS5d#%+r!Zq4KSL$?q0>B`v zu6i-NuU>iiEbplyZ%oy*^|HZnTFs&IFE@CKCd%|I#BHZ!@6sWXNha5w;rM}&gZ{tA z{SG#Ci!Eel2?K_P0&jVm0{q9BiHQZO@#5n@ZOJ|HrOW<#Zr{}nj&tPWp*LOqblqZF zN|OD7*X9ZbxwY-D%X}_|d4_+d_2Pq+$5y)^4UL#^HdW8sJ~yeNAC1y1Q#2(Edtc$; z)pG%r>DB=DJQ~v=cV5;+N@n>h5DS)`mH>!70&pNadXz;G&HbB(K=pVZuafEdtY=bq zTy&+j^ffK_laW(qNPNOEUQmX^7}7~t-C%g2m7vu0sN?JP*x@BA$as@azw5=wwyBsg z!j&AHjPo9_{Ep<18>-Q3MjX*yulfomz4$N?BX;jRgrMdaXJX!cXTH3v(NwY(@)WF}@HX}JnCU0e zWOBDQEAo)8haRg{6k|zozjAAL*5vqEfHQU403^cHHQ<$h^@k5o1}AwX*{$}Y%&;^D|VmC=St7T#HpzbvR|Z>t{&Z-u_&Uu>FhdO0kG&}3pnGk0002i7)MbA z*Xr^rTr)t3_!NTF7CP{fL@*B#zLD0`9xktQ9(<{V{U#|2)wVW*ENok_csqspw`G3B z``I47eEB!8VT%_3oyYJ8%y2=2|Kc=2yZlvCtNElr{{8nk`_reiY6jJ@c~tC~ALUlH zxC%c6sk^f`VEzxqb%GOJhX1<@?S>dtAN+GRR_fob*Ck2(@=!8b5)>6ccT0MTb4L7U z@tslF|MY`#?-ZVY(+`aQ&1}G4-;wT;-?M}SE&+e~W-v-bzGZ`$j>-eQatKZ+rES}| zyU`2p(kBcs4dwKH^KcFu!i@ONigSqvQlMK!p3h#yiz`!!*)b*#=*3_E1EBiz%|!i; zAAx|_>FF6#n6jvi0#()2dXsKL_+m0!Y9tQ7)4`dfAcyd6L+~i~j>{j$$issWPc=Km z-5q>=&<;c28}Spfr|P0V219>o*7QSKvW#Pu-%NbBUH>^Y+Yk-=+MYDUJ*eb8kOLD1 z&i>AR8?~Xhz{A*_zz@@g<=yBE#;{MidS7lL80?-*nABBj4ptQ5_|}F&Y&Bx4i(97(uBj&O^uw zj!DOPCToz5ccq0CsoacXiICUPVv6?04BP-~xrcK=T@(~$p8fa-#lDiIKl;8M135mNi zW|)AUec1Aigl`*J@(}A%Ii^JNn@6MBNiiqJ0s#H#1E_Lz*u~jRZu#=CN7Y(oYoh5Q zb|Cd)knGnx_A#qQkv@-I;v8?q%kBXKcuvD|Xc-;XnW*Ubn-OIrlKpM|J*=b`%dH_? zFNa6)8o2wd9{3AZ2U_DDK0AEHL&;dpYzm}R?RXy7PY1YNZgC!?DG9|d3Gx2;KdPlD z>?_%2s4;dX11IeKA>#6_tlBK#uOStP9RLXjdQa~5zsOFQ?I?o9(1}*Z+?jps=_m^b zH=Fh6P@!HlP#k}lRY@G^=mMV{g1~JJvcgy0h4GqPsHr~55vBpNGm^u!_pT!}a2fg0 z)RGEKSi4*tUV3SIc)i)H9_?O~jCq@#Q@3eEDwNMFd)GFE&9&-eQnPfkM7?&C9SBXd zZ#6Zg;#pzk7;Q_Hj1-ZKCw`DUhz0bAM;FmdD(=qkWveybYq?YuX5_4XSvP97CMp>? zE>T4dcpspv-h^bw+JSi+@(V)QD(t!2c&xS8YK0b(<)dpPv>%<7WL9ycERPxR94CW> zC!k>-*p)u;*40B>4uwZk`zlks zcd^%9Y}Ro6Z$1I!Y~T9q;y&JLpj9k#tGF#!eOIfAGK(4W1yw{E_(BQ$^fTfemg6f- z&p6|U=5<<{(IhyAq{_JH^!*+;5|z6PIUAzf@Bf41ESOe0LM7UJvAM)kQWFEM=7H67W1}`2W z4-8TR}>uln*={aFNY#+}ep_hivti_!`V}!l5EGVlz6x&ibrbEIQ@9n;4hu&nW1$e<$7I&=sn=6@Y6(vi|M79#q#E*8|40czCIZ0t_GIDs7Hw8t?z{Ea6Fw zZ=(FO_KcF?DNhIs{w)Rh-%k`2)M{lenfvqW+h7B|6n>V@CeEs^4KBKZbTJ&Zh*ujS z@*kK;kNgFkiPUZ`R#l74K-|eKy#{X+cG0YeTjfR6B8Ws9sGb-vqpCwU&m8VW!H_BB zhBEZ{2w4tU1bc?;8h#22Uf##CYE$ymx-j~DL_XJV67`Wr&IjYDajom;nyE$OHcHhP z5>NSH;E=MkXO+7rL8}+gLwvCYS1!|}P08fhRf?rKDh$qmg*@306TDt~1?)lESqJ$f z;|eE)?eyQqus#rh%gj`hdw~LY3D6kJ4asMA-! zH=XhF^46AqAYWYOR=X%lqqoXPp|UhfC|&-vK~y{Ll8>LOt2e*dHD~+1S%L0x7*4jn zfTQOP;%DTC7=B0|rW&njaNEq56s+l~1#Nhp)Pjn3*}jrQRoW@9lM|9Zw!anKx81QAW6p<&XoBlqQG5o#Q9w0sUU4 z2qkEG0BWG6isNCt7nMbq*Q7!n@i0|R7XQ(gp4qC-o?J31L6werRgEnL5EBeL z+NVzP$IFbpSbvvjDk9AF?nRA3dRAiLo^ruhr+vC^3ML7emek_9D8Hfh{R9N~Mjy9qIw-5jsDPyT0J&@uWKj4*`(3T;xGi07)H?7|rhr^irY>4>GY z-K{ddT+&|R6FO9HZD7C2LYTwhCc_@&yjgfLC=Ms2oY>81JYyh`yVrY~nt(v*`dz5* zpg(o!fQOWc6zP!{w`!UA#u?tVAPFTj;r*8Ek6|5G2He2+;Mo*m1cjGr(MW4n=>YFc zdP-W!3L`JLc{td%f!ABZu`8($nj{nLhj;S!qdp95n1y`EHjR#jR1j~BF(wRHdQ(kb zipai3wzygJ_gQd2-hlPvj2q@A;mS2~j{7fHnGD}GMSSr}_<@*=j7S?MXxQGJ|FVLe zUbkViZlMZ={!}3AR5C-_r*w^ETY-lwi0C_B#{69SRr zAe9s0^?oVIPm<0@*QmBU5>K5=2Z|S*D{aY|wK2}c1#W&7|M2Jea<7i5Z)~;q)4kc8 zA2@;0xg%|%1mx!B6;4;8v(`)d-ARW;Y#ZJmLX`B|RW@aG6Fv9yVwz2;XVcoMvSeJ4 zn-}{;*(+5lqRP#Wd*#}zh=UjFbNwlQy!fo!d+uLU-&n$;ZVsmzQ|9H1bk?2gj<6{h z3C@xQ=F!Ic>;6O{SALsjuX%6C!(#PZ#wCM=UhN_fK*%)h(LVMSgpD(1$@4kkxG6M` ztGn}00y*TueAOsBxLL(+Zci7O0wN;GR6Sjm3++U)F^pjnMW-@fnh8wSX9G7AP)fEk zQFCr_q$6OAh1-?Hs9uBYBQI6`>tP9W8k$-yi&u%@abU2{IGdV0st1=6BSOU}J0-`4 zmi}L6B z-yBo~u9@_Db24y~vKDmpHianOy!iQ;fL2IMyaOJ>#eyETVBMZYeotO=12I*olz%=+ zgrX?vp!nWxufz~&n-Oo{r+*T9W3c9S&VH2i&!4g zkY%|jE3R@jSh{k?MTvM%olpQpaofW05)bHXc`8=G)ZJGI4jNC+1aV zlA>Zkk?rw>VPK?&qWG_8ytRAjgA^jE;}SHYkfq7SM59uAlR@UY$$h%n*JgQm4+W$U zG|hLH%})$XQQHTqh;dTH`l`~^xNtKdx8zP+0-Lk;c8%1#{>2Y+^gpv#Jhvzk?+Ha` zS5wP9S#RFH6VJo8@>gwH1kdgeU7~8dx61rNh)m(1G$hISM9PBnxNj@0vCmi)d9`f^|3p7WkMb495Hq*s9ShQ4u>hNJF%zkbh%|HA!$iGM$ErRD!xRk5 zqdsJH**;-Q1jJ3v9qqX(`f{Pi&>=lP?!vTUngCuE;Hz(;J1%FMeI`@i3Qd#=O3N~? zAFK9VHEC-dVZ>+AEGz(ElDO9t@)<^eYzRwnwr{kue`4bFO~!dSIEot}S^ffODPPdf zPx+SNM;qfs)E5-M)eWdZf-t4A^5RS3wwB?>WnOF4t;a%aC6Wc*DFFobE~P77>IgW^ zr7iuP8+@RoKqO`zE0TEi$DaR%U<80rkXxpsTFJp4&x&N)-($L2Ci=MVOJ z8Tb6o7WC0|4@`M$=L5?@Ww^ro7e$Ipr&|)vJzQbx!JgMq0*wRY=KG40b#wvf(uSNG z>sW&qt?{h^(4y&E(bjyMc5Fj7qb4TFY;kV2S&};h7y=SPvoS~Q78|7Aps2l?xi>T* z_mJAvfK&NZpO5i7RK0Z``j?Z)%wW3Ai!;O$o;xgnNVak6asYW8+oPuurNiP{Ex;&- zwrm$w`jlFG9PZSF(zGju;%YC|-5zr}QVLw7G+&!}4uWVRe;?3Q?U!zy2^zE+pEIW9 zFedbA0$l{qRY3YixVq3gv)6aoNg2Bs*$WTBL8b8Vin;_$xb=9j2W`}^nn~7X-A;QM z?h2j7se*HMEDec@_H90DIzqirkvAgX>L{=e_6GmQ=I@R`fnl09G`EaB{YE2O03NO| zAFkt^i%BQ~5@cGp=idkoaO90oR;!D{5QS2H39zPMC(Q2}s$q}(_4Yd3?s}3hJv+va zD?L-hW3|yBxx)+iagT**nKsf9Lz;m`Q+P2QGs|#UdZ`)2R&fO{wBb_T>oeh7SMev; z3+Kr+ej)CbMgMHxPu;Y)L;tBR8Lv7{#cl(?^Wo~k|6w_+K96G72D&gH znAXyp$kzFFV1n{{#wBzWm^~VLis#<%z(^f`2wDwuThwi zBLTc74%1$Ku}SqeDZXrO!_1~c<>s&XfJhwYn9;P*I9h@{XVWItlQkM67MvDRO_v@o z{02PI-1;-5TSHTPBA`*-4L6aG%ZEh@U1{GJgP&t;bb1WW)%1{GIyc}&8Dm>p&sWU= zo(?I_D-L~SS@SFr{X{7;*C_RV>|$x+naPf~ES<{A4)aBiNFry zQ(M91?wxfx+K6U@3+4Twf@HEdZs-P^x73s7$nwv2)Q8LeR{c#0gmqT6QQn>P>Ptqc zt!F~eK>55Q0`qJ+6ptB!~m#zSdF_hE~U*0f7W5*FYW-aHyAiFlci z7SZ4KBr*YgBA=-(X!>M)H?uN!{e#yo=w|GEpDN7ThizNM2TGHTiSCW&qdH%>@T)Ua ze$CdXTz!ld|H(4Bf97kNRb)vdu4&S5RI3K@!=!0dI>QO7eTL;O%Ms__JR$})|L!6Y zZp$0^e`fB<{{IysK?OvbdQ$bA{P2Ril~Dl^H*PyPs_~K-%7~$nv6l9t_ao;WzS7R3 zfko-Jt*z-a$A! zwe-x~$YHFSm-ftt&&;OHrFR% zts#-$Y$H}G96Q?Z^z~?>$G;3*3CAp!2ESWD-I?2a| zaI(_XuggglN1b;1_<^gOINCYM&*;1*e&VpZ6tD5@iV*j-J8`cwcBE@N+T~h{%;Jr* zED!Tn)jQr&UML9c)_hQzzEm8JNvy6MEf06@_u@9J&o&Kq8P->9$sSE%TerlYO0Fj^ zYIA^tm~Q>D!V`ofT`*qGfXaqFf_XDBxm@_5XQX4%DMTO4(jvBW#enSGvJ&Y}9J9Xd zsPI-+aCp<(Ybgj^wV|xd^wGU1&jl^XYMAhtM336 zL0Xx_Id{SR1S86pD2jM?z}HdKyjXeT#O;8Z7jbDUIZ*Rr`&cEVvdoX=`X|P@?Y8D9 zkCp-V8ESUum~Fg=n0^2U8EpX;Nu2DhTth)J@9esp}Dk#FQI((m)RsFGOlX~a$}1BXEgns5jBIQJ-1 z|-fSC1*;g~ZIzB*qT7nit0##AkL%+t?Mm4!8Q6{V%IO7Z8z1;Q5Kch3S}ZFQ*&s?Y9qgf|Ye z*2Y$k4TA4%#?2rhJrsYU0k)`DjIb6KzWXjA)K=Y(|U5&8RHa00X&1F?WS=-S+l=c{ahk!wJ=O0!uEHDCp$VO z6$a_)h~L4FN!?pwUw!U{&MHMJD%x;6@A<3r>fbU$JyLoZxUBOEQ6Sy<6}2Rw0qV%t zD}?uqEy}3S)Megk3%%D~OOews}n8woRhOO;hLITm!XDSeeb@h9wokAu{puBiD*iBaC(^yV}j z5zpt!ENQE>U%aS4w7rh9 zCkgxG8FU-?yHm`4d$xtk_|#&dw&q2e6rO_v-9i_HgY9*rssSr2QI0DfrK_NfRoS0v zwRs`O7KY{_;ygH&R7*-4YD{vRCszbANK5-Cb&pUo694(GkjCbR5cUvc)yF0x}c?A?o3^L6fisNwQY*G%Kw-vI8jN|Vhp^x2};>i6dTn} znPXadgS9=7Pug6$IOfCRWT*x9g}UozG6l|*;x&hkMq^&)8&CrhMJ7Ej%+`xN1aNysNS|3RXV=O1e#MdatCH++avyhom$!?niB56oh?ZmSKCB+?i zxj1>d+wrd{3il0Yhvb$JNzV{jn-M8&(iQuc<=25j;M)lzzNz3&7rSVc&q)54-q;rM zb?2`w$$zmsjR0uFA|v5TiWmEpr5na^95n>fspaa-Yiq+VO^wSF@U6wh72gkXd1Syjc7prO@k1K13b^u3; z7BmqHpNdGObktLiD~IT1J+^eE@Z@w9GxYDO&b-d5d1Ja}TRPascLtc;1?d7&awzc< z(Z!?zeA>v5`8KolE_rfmQdETQF)11aU#&oYfi{}%({#^DpwCB}(&Z{-TY2{qKXWD1RnTJ+o}tk7%(V~KGT zBb%M8stVPzq{baC?)$B`SB)DwlhhJ_uECGocm}I0;-&0G5)jz@znAqs;V^z>mkW6( zv@Y0j@j!godxj)oPN%L+JXYif)*Jz%k!b&ls*Wm*)Z)20-)m7L4Bp$i1eC4EsqA#N zUoBeg4YFO0d}}64Wwgx0_J_zv*d0mu?Oq0YsCLag4Z4&%-Q0@4<~3pQq_FkkBI#We zs5<~A_hI7))%*j%RIFQBSv`u{zS~uQGkJ1sz@!7ee|1Y`AoIpJ%ikXVzO`~4&#q2$ zr@babxboPrL(eV1`Bolfl`7_FGxfS(LX3PSrj_8gJ1%zLKBOr~HFSK?cPq*k7typ6 zUhd%S^r0F0jQ)H291XbXJLK&j;dMJ2TK*^8_}*6n@&^*k-qJj8ax#?&vg4w4!YeB3 z19t(QkaPpscYPGZCs6#3`X(23Jt2v@?8h{MoB#%!#7Q8Gr``H-1U@eQcrXF5=;iYA zTRqdbIZ<_^P={W<1b>A={sBx7k@?zEuv%N;w^<%iOkfTzyK>lQXhzG}HVqZONV5y( zLEPM|hgI&as@9Vkn@wC+n%AiX`;!B0(K9{K3NQW+IOP%4*E^mUceKQcxzC=JNuumo zV)3KB@x;UF_dT7#i}aOc>w`5XXO8g6*22)rWf0~~ltHu!vMEs+1Dj~awjE#86^F6# zpyX2GkMYx34|8KfcgFcWYuBRnvBS~#v1l$yOE;coTC~(3RTsyt@97qLay_iDGs{rZ z)$#EP)AO4vFlP;M)sSkFB;JP>QIawK+o(dq=Yr3t4^ zhlN84>}i7)Ahf}t9JC$e3v28o0(k)qX7OGvDz4BKhg}I|e!0^ zor+L1A1fxaq>%lbyxllyEOYsq(`_3pUagX8OU z(D3E3VDABfzUJ_e{N^l1h9uOBFkr0YS6CPGynvSCBJ0 zI^EM*qp5U!BNCAxS}H$2`=sf-P7ME04+w0PJZff*)5t>Ned%s`C-T$&t%46lMn7Xb zCPG7wa!QV>&La4YHv>(L*D>{1{|kg^=~u9uk3@Hc%ERO})h8HNN+qT%C^09Z%zd0!=j-kCtC{Qyp>1_N+1CN$^PqjwU{*I!7&r$q*a3%g& z#LPF}lSy$3HBHX-4I(@r4B4M<7|*ONYw};|pkeHBDq|X$Bg?lyq`EWiJIz^=hJ?ZP zi&1Z1oI-dFo$!&fue3+~*I?YW5zoUU?gRefEI+OJJT;-%dihRY&NQz+P0ll*RxAGi zjIU4;Y`68Uo^rxil<|^iobIOj=A27w#RJ8sgpIB6t+cAzU5SQQu#9So2H-rIP#+6D zzU%FHK>?Zz8x~$aySbs}9v4Nn9!$oH!Rjz8lrF@QiTe$r61Iawbxr17F)u)kW-CJC zRzm1_PE@z}m3MW9ltXfqIG8_i_-YTEOkEalPCIttSUIh?5D?wu{54mXVS`LMb3?|J zTkl(Tv_@I17{mhidD$ffsK9h>W$lY*%La&d9E6Q67aet-?~#7ZS;*Omd8k+G3#C~6 zo0sbr?!cbu;zy#6cgEuV8cTzag)y-ui7>&JrdDG8Y4$j{jaR_xo+oEcDgujpEc$b7 z-JXG~mC+ec^d;I)Z6nl1{p;aSI1iuH$bf~w?^gqJR0XN;q{uedC3E-w ztrN{1u^h1Rb@B*>H8bSkR)^#z2|9@cC0Un0@I*@T9{>Z-KLE>BhDY*+ z_W$$b|Cc^uW|M!T`!~6Ol~HLL)-R92sILY8ste>@d~@@0-vU(O1Zz54I*&Z3+CT-W zak7c;Op7A>U9*coGwjO52(b?Fr*G`0R%$uQKv47LFM36jbR#?W}X?9FtV zi$?{xa?lBgF_o|ndRSgpOs=*<;$kVIc|oU+hZn=B!(BGa)o)HHQJ+=RhM+aK=i=^S zoP0Qcf~Htga?iXNkD51dlAIbA!x*{7*wN7bG;@jd`zwQUtoq_*@md3S?{sF;EB}gT zA_g-)r@NAr?{VnhNuPm5QHuALv9o`Xn6Ng;)#7ApFJ_db(Q0Pg zm|kx@3sTy$50dfze8=>lZO+|yFG*+C{IvF*m!c9Te{`+RyXB$u9U1e|-ceZs zyzuAF1|9CX4lin*66kg$bl=3$#WqNZ<&z`{W$a3QOTWh7Vc;=L|B)JG@|DQBp@h0o z6YEc~&V&^tXb+v47Sog0)_j7N_7(iKT$!H?iF2z3>AdBdnH-THpJ6%^yKOUmJUl`t zQJsXoZfkaIRLsj#?HM2M*H(t4btGfU|1X;k3b!X6v}fI1nP=Uk=R(5+ZXAaOLo;GQ zU^gcgf`PY|9lxi=;IiV~4qHF($-j%_zsTkpT>G zL=G4RbeV?A+F5ZXDLNcxKkvrZy!>1JjnFVGqko^1FGnqQrYmkbPZp<8;}i8aw{K=V zd;94kl~{YVT{@`h1%2Xdl88ul?lMAtFK9mol%ZX39RD9cqdCu1rYlUMwl$&J8J(;( z@5aYlo2L#aKxN#u3~N-pukS~f_O$f{P;fsyTSVh^-J7XtdFDK-*N`3C@vK&Jp$!A3&xA%EgsL~&H1+s0PyZJEDHg9aLk_>i{t9-M-Em2idE<6FIq zwURWQ2jO_B6>E1+b;lxn#33<72_EG9EX3_&(YlZz89{efVz%yzV+r$vL|(VwxZ@^& z!f3|zLQ8pe(NEIMY*MLHJAuEZ000aH)eG~UZ0V1Jng^g#r%O0#{kNRU!bEZD+wm5= zHkzR6+|e;!fnVoQP`F!VBwSqt16{r~)%3`Tw#FJNu3h)e%PH|t*ME-E^*a@(j&dW> zNmlxo?fGxejZ-L$eDT`3iECQ&vHI0PgHxKy2g*-ULAINbTkY2s@1~3XL$L;`Unk!7`FvYWSMG^ir({JHzRWX0Z_flnddw1s^!;t|u8{T^ zdyE*=WZY+Y2ZIDb=tDlwFUsoVv<1QByV#2C{sIgC+nCQWj4 zbCkyoZm8q5c!ijDhu5s@Pfo;!RD06FfdF!Q8aed~;wuOT`e4#$VID#7&*J8VTA5~{ zY__?@pc>;M2>I3DAy!Noq|HGX>UNK;G6!mG)4Y)cCF!pf8HawYAzA$>?KUQWm8POq zS|tCYm^8f?P`Cc>UfCkO_Z?Bf-cYq&sKY*(tY3(8hS=d{lvtnChnKW@xJdm64wz)})6&rM=>xYtUe^#+X-G;74f$!3?Ebp+KwU*^vJiaO8 zHZ(p>&FzAf48Y!(6eKAeynB|4>ru=C1D0p2-sk-tgBxFXel=7&8C;pJtxl54m3GLd z^c^uPv0VSTO9>)K=mHQ5Cw)WC{(j?NG+W0P=hzWC_GhMqh~kNflzo7K)zubSH!ys2=OE#Y!g zoun(2+Zo?Xh?X^7NiQo6S-l>H7qRtD73IU$M(WI=T2D#8qohPqoJML>ITh>*Y7Qx# zIe0K67tB|DPmv=#=*o~ycC$UCM~iuja+!N4k4h~W$~1pB^kwtQ+O!)EkmOO}7d^uw z0Vee1!LwSRkvnt?ato#}XMEfX_Sf{i&edx3O9^@aOEgXH=uaxuUT=6BjlT*pz_Zwl z<)L^LzS`pZ%Z56Y1etqf!bu7nQnP0V$-uoOg8Ty%o+QQtE8TG9+_0TXTYgSSVTCSV z0b8=cQ{f{K9aGI5dm1h~wH6GlI{R|llcW0Sy!KCp22k&H!Qx& zMl}e3QKp&EY@tK4VX^#n;fY9cyGGAo;#!U#$|I_8A;uZf!7N@)(w z@^K@mi7?4HN@i$^*~LeV5FUv7h%rAOlmjADbKLW~b33_BT(?DClnhA=YOcm!F|Fl= z;M-4F#%VM`3;~|0B-z?~QF3!vOt&Dygbu>`Cq;c>4IEfGpSHV3YxI+U?bR*31^1K4 z|0yUEh)xgMkerZR_HDefG^_Ffj=tZu$o^5oJTfw()=3P&w#rq<@F7u8zJ^QOtozDL zo1^>jz(nt0={DoUxNKf4ckG3ZXM?B3J1m(Q6!^BRt0n)3C`%WTZ$0u3_vV6C-{TNR zmBPE_*l*zO%sgzD=dyg>R=*Bu;}cuod(Y0t#@n!=pS>t)VcpWdkjQA!){ZoW0MMU? zF%^&#Ndflb($SqjE^h$G8gyUqTcb17v52d-mlSp5#r+!Y-teHK^BF(e%s*)>cQG>ZqB=A*l+d_7f(0i@ryiW zl##`V*?7?>{B9$3m5Rq70<{fSUzuAv4&9DetEn`d2h)1n>wI`B$P_nI&>0zK!D5xp zpd+ybF=?_oS%ZOV^bcEBmqv`*pk&S2B?Jh4tMyc!c38faDQWlkSDA~q(Qj0uk$gk^ zkMFhzI*002N$^CE~#hwOQ!s;VkoVgmdkO?hy-(2ETf z(^14bygGwvN|Rsd6$x^^S*Jq%*DY&KqLi#;kV}PO3R6$Qbv#bCl>y;ms?x1aZ4}sy zAcY;>0x;3AqO4z{)OuO54qJ?8Ke0r-5yqF*DTaKX_#ACSFG-V4;^16~%73{e4L@nV zeP*(lpvj$jRip5ARz!5CfE120s8MXz-JposBh*0nl&FZk#anye2UK3i%o7Qxk8Q00Clsd+}X6RF&51pKB+(pWuj*phi zeimi)HYt(H{hhd^XDlAjE@=%8bINrxSYUjEeg)r?cjvl( zVs2>>l{9g*m3@r!WUV2`L?k(RYt}A@%-EvhJFh%n$Vzh~EZa%fi91MLMwNjh3Aftb zZY!C7gtACEl$or#eQl+c2tIiMTC8nTL$tWKJ-p1oASkI3r(ueu0XI=f3TK)qD1a5; zg2w51RvIr4;1U_aVq$0DS>XGliYvqR^>q8J3Z`WXDSIy+9JWBGL z5$9;znpK6Saw%f&?A2Xzq;>x_zU(!a-9QdBwPUDC8=U~x0ZQHA!p=LURV;w9WhbI7^pE)oKJ=M zQZk*+gLc3G9-kimUjpq>n#jlV-@$Zf9&5XFMf+G*h{~#g8r6r0#;`tRJEM5RsxsZD z1xRuj%lK>tE>6j>R|`DxZYG?1a)U9ZoLtMLG0U-J;i0*nzz1iwQI(PH8>c%ypWBcQ zYXOseF=1~L$ZP*tC(3^xHi+ERJs=Ar zw7zFonABMM+m4k*Zs^JF@^*G1d3^YypPeXg|FSx?O}tW3vSEkY_;jaw@m`pY;5hYo z6fTUTO*smrkH{E-_u%wgD2L~6#BEJQ>YaJb-dG}re&!-Mbqvf0q8C%xmrb9r?hJI# za4NC76e?66H^>PydPXg-rF~|7x<5MMKJR@QEDl@he?|w>H^Jqdr^z1TWXiWekbT7e zgz}vYn4*w^DjZTRjo6$Q$Iq@Pja9+hPo;jTMhlk*i9%*Ks+PXMD}*aJ*g=E=83?b* z`)y2u-My3wqF$v=N>$mx)LoR{T_ozN=1^$nq_d>6eDd|E=1nfYXHN@yXV6aLRi=@V zOxmsL7TsyVo1K=5`u#J-*qV}(N9iHk`pTy~7&VH)>Xn+N4WiS|MF!+k#CFQ;@+?!DVQic5G> zad0F!TtP8dVKMoA+-m)BvI7PS$wwiktDcN>(j}&T4wQr z^W^>4E`&xJZ+mDj?J$mwOER0UrLK z!J--^gBE1za<~IP#wmk6$H_67pLpr;PPTrQ_2gN@PQET#dLmfQ&*E`R0uIpzm#gka zhQQrsLagzlPOr8D85Z!EDwNLyx(m}+BEHBCU9Ia3ne+vabkm-&TC5xA*T!%*^wrzq zqWkb4&-#^?+kLKtCeaodb@_E^U-9Ttu~oBi)p1d(>@Le~n0ybcz)vt=_DP7%ym@-2 z$CGI~b?&VVRfiH3@uIXhq-j154+BYEd=szgAsBvLjgZsDZ$Oc<3tIWCEH~VkTuO8q zN_KcL5Zj^S2|*`#bGcF+uhEF`VGMFlxG@ruo6Lc3O!f&o7UYP=0zKSZTyns}c zYLD^^y71igjhXp-JMY|a4M^wYIBmH4WjA(BZd2>{?2&Kj9Ok4WM3n}TT_|~aerq_l;5k`o!kP}*=5?B4o9=kAw56N*C+kHOT zQyt@GdbeMos1k=T*7ePdKwPp`m@)IP4HOkPLKYcK=^S7)K8It`+n06rLg?#7;|Wr) z#!71jhw;F~@mih;L_DHvR-K_L{N0p$GYH;iy9RhWnG`|8$6EK>%OXTwk95bqDx>4n zuC>wvlU*^0IXbOiB`etHHR7#Ps*NRT44l%7o5&Xv_MQ&&vqvY~IbFE<=H_C3rO%6k z&ezUztZb`-Z6wW6rl$OPO8Zd9U4BG76|{+z$>l?P&n^dUwpTRn%P3^!-Q1`YhX5gc z#yYv}Q$Gjs@`hpAy(+NfSUp*py)>ZF&LO9f3id5DdnR#;$1lZM(!!oCsUQ=+XYi^t zS55#Kypg_seo6QmsU6r$Pq|c@qO$%MB^W~AvPLapJdulbL5PZW`?&_?h(tLn$N1}} z_&9rO8Vw9*L0lUpXH;yVHxc$N>2_B~5oz@(vxRi-%n=Rt=@_~%ezNIfK-s?g8vV8d zv?b55rJ7GlZn+b{9v&W^J$j70E@}^Tfvi)F{;T>=cRC%*as_;VY9uyk3BH1boB1qj z-Z46Y7y{vw!m51@09R`#V8P~OsuYNe;nBp$eLA^gGj^6+rWeIv-Gd?Zf*0$P-sQFs}SC*8E83N4tARi^+l` zSz4VxL{s1JO%=(6J>BW(NM}R5@F3(k=e93_mQ7DMk?R>a$7rIQ0xZqVmXVz2+}J2w zGm)cOAFiP&b5A>6?RNIh*db!lcDCwvlR^GH@!5p2@$Z2X@%3IGR|GLE4o`y&XhS5M zC{<}lUp85|D_PO>Bwp_AGp0`8tPk(8rAWP+h?xsHu1R@#Knm;N;LD=#wMa*-aP4?kk;! zUZdVlA_?}YWPUL58>Z93s;L*LYy&Ewh1kGl^m5=*9`7=A?|^v0ck(uBvSqcZ)#|y? z!Qa~rK&2ma>Jz_B?(2b!;u`~u)WPW26JIdyrv^*<3zm)fbXg$}LmvsHx~XpW=v4gM z7m#Sc-x=~c){tg%4h~SHQ$Yf0jplTN`?wtd;Y)TV*GM-UVwmMES{Tw~A5wQ#`TV?* zGbbxLZ;q&Ysi>xB{2+TmsApmJ9|Dx)}IX=J!_9Eue+f0xP8` znLMt?U*z`W&%ra>dHO4Sih#(q*yylK6rH1ZVlMs^kR#kk5G@6Agl*roxAv$rd3d&W z>;Aq+evRMyx1(zIPTzd*UFrt$=HlIMXFmoGnoSziDj)}ccL6VFqt=Dz^pA;&+5*Y!oeaAx9FR#=7o4`NAT4Nu*+iL^q1=Rm> z-xnQ;qgmYzq+}Bz4vxCv^0;z|U!20MEi03Sk1f&-#z%~(Vda&q0pxHmbOmzO&^Uzu(|xX2BL9)1Y!)@s3#(Jw1z6qSBV$c7 z@>+sjexa`W`S*Bd2fSGx(_wN@2xe;W4y|&_B_Q!o8MLA&TO{F;6S6A4Tz!tx+&gl} zF|t$KQha&QOLV=(EAnnS2Wl=J^7;K3EO-A4(Q%76#m+y`WUF|8Y+r9&Yz)G3_%b zf+uq$i)izm;NXu0hW2c7DkI9UIoKFg?*1b0Qe|C1;^)bauu<B;DKwM98f%8r8Do_XaIu5Y<8p@U3T1XulEOC>rdH++m_Ztog1jNjJC6` z{)KnZJwK?1pg&ooix)^`*3SY%S=Bu4mhTS)c>SO%BK>6iTXTR`@VfF$ru;bkX(Q^s z>sWW;PZYr31NZ|6Z}d;SGJiH!Gsa>ljz}%ptpAMd%=76k%Lzq0*^3?P{%gL)m)~h_ zP(dR1-0tqQ1rDYE#~PQpk@>v~{<(IeKg`Sf-H`dHpS{NWn8$JTXK+-dkA{}^6kMP2t^Wj`KGZV zZ#F`y$jlml7rp=OJGjF(@KP5c^$!Hj^$n8_LBJe>@lw}W=RI4rxUtE0DgdjXf~ng% zsN0QzVz+qrHcNp?!7?x@Fucho>sJKBoH;|(H#`KT=~@#>llz*yzcMbNO|CV~>|{y! zRT1>EGK}O830x%xeq}Zb?Y0&-oQ>%CRSVcJ!o?dtzr~#4)D0^wucqBMff6r?#s4Ds zrbTv+a#Ng;k8`oydV!!Em!lOmMWh%B+qc4|U`;7KzAt)HwwU6poP1a3H`4lY^#qC#|C{$#5}=ip zg#ha!NGuR$GPcyzxWLIWzWwPj$Or0f$r=wL=PfV))CM@z;^q*xig? zyNBLU3tgD6s_(%Ww)BiX_k+@|4s7KB0EZ?(0=P;gjzrH31S4fcC>T)f3;Wb#XMkeI5h_qGP>*4z$3fHPZ50Ma$yzX3ji?mztQ zCo`h2kpUt9mT&jJEllp74>)bVZD)Glu(TA1!(u4IbnH*T(XuINNMAg(YIP_=B%A7R^uGY@eqI^? literal 0 HcmV?d00001 diff --git a/doc/qxorm_en/resource/gui_qxClient_02.jpg b/doc/qxorm_en/resource/gui_qxClient_02.jpg new file mode 100644 index 0000000000000000000000000000000000000000..90e964b88c1405a7dd6cb2c6103bbfdd9fbafb30 GIT binary patch literal 51014 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%0AE7;M2>ExO#HGp0vDB$S@4DOS8P+_bO3A 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 W5hs_C>Eer1ZCD?R6wasqH2Gi0u4K6Y literal 0 HcmV?d00001 diff --git a/doc/qxorm_en/resource/gui_qxServer.jpg b/doc/qxorm_en/resource/gui_qxServer.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e8a0549d0e055712158636966a3a0b512113cfc4 GIT binary patch literal 60288 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 M!GZH%i2a!PKXOTh*#H0l literal 0 HcmV?d00001 diff --git a/doc/qxorm_en/resource/jquery.min.js b/doc/qxorm_en/resource/jquery.min.js new file mode 100644 index 0000000..0f60b7b --- /dev/null +++ b/doc/qxorm_en/resource/jquery.min.js @@ -0,0 +1,5 @@ +/*! jQuery v1.11.3 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.3",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b="length"in a&&a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1; + +return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="
    a",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h]","i"),ha=/^\s+/,ia=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ja=/<([\w:]+)/,ka=/\s*$/g,ra={option:[1,""],legend:[1,"

    ","
    "],area:[1,"",""],param:[1,"",""],thead:[1,"","
    "],tr:[2,"","
    "],col:[2,"","
    "],td:[3,"","
    "],_default:k.htmlSerialize?[0,"",""]:[1,"X
    ","
    "]},sa=da(y),ta=sa.appendChild(y.createElement("div"));ra.optgroup=ra.option,ra.tbody=ra.tfoot=ra.colgroup=ra.caption=ra.thead,ra.th=ra.td;function ua(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ua(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function va(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wa(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xa(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function ya(a){var b=pa.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function za(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Aa(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Ba(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xa(b).text=a.text,ya(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!ga.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(ta.innerHTML=a.outerHTML,ta.removeChild(f=ta.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ua(f),h=ua(a),g=0;null!=(e=h[g]);++g)d[g]&&Ba(e,d[g]);if(b)if(c)for(h=h||ua(a),d=d||ua(f),g=0;null!=(e=h[g]);g++)Aa(e,d[g]);else Aa(a,f);return d=ua(f,"script"),d.length>0&&za(d,!i&&ua(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=da(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(la.test(f)){h=h||o.appendChild(b.createElement("div")),i=(ja.exec(f)||["",""])[1].toLowerCase(),l=ra[i]||ra._default,h.innerHTML=l[1]+f.replace(ia,"<$1>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&ha.test(f)&&p.push(b.createTextNode(ha.exec(f)[0])),!k.tbody){f="table"!==i||ka.test(f)?""!==l[1]||ka.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ua(p,"input"),va),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ua(o.appendChild(f),"script"),g&&za(h),c)){e=0;while(f=h[e++])oa.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ua(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&za(ua(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ua(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fa,""):void 0;if(!("string"!=typeof a||ma.test(a)||!k.htmlSerialize&&ga.test(a)||!k.leadingWhitespace&&ha.test(a)||ra[(ja.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ia,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ua(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ua(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&na.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ua(i,"script"),xa),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ua(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,ya),j=0;f>j;j++)d=g[j],oa.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qa,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Ca,Da={};function Ea(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fa(a){var b=y,c=Da[a];return c||(c=Ea(a,b),"none"!==c&&c||(Ca=(Ca||m("
    +
    + Video presentation step by step : +
      +
    • Download and install QxOrm library (10s) ;
    • +
    • Download and install QxEntityEditor (56s) ;
    • +
    • Create a QxEntityEditor project (1m 46s) ;
    • +
    • Export entities to a C++/Qt project (8m 26s) ;
    • +
    • Export entities to a DDL SQL database script (10m 22s) ;
    • +
    • Create a client/server application to transfer entities over network (12m 31s) ;
    • +
    • Export QxEntityEditor project to a XML or JSON file (17m 13s) ;
    • +
    • Execute QxEntityEditor with command line (no GUI) (18m 07s).
    • +
    +
    +
    +
    + Here is another video of QxEntityEditor + application to show how to import an existing database structure (MySQL Workbench project) :
    +
    +
    +
    + Video step by step : +
      +
    • MySQL Workbench project - Sakila sample database (10s) ;
    • +
    • Create a DSN to connect to MySQL by ODBC (46s) ;
    • +
    • Import database structure in a QxEntityEditor project (1m 15s) ;
    • +
    • Export QxEntityEditor project to a C++ Qt project (3m 10s) ;
    • +
    • Build the generated C++ Qt project (4m 22s).
    • +
    +
    + + + +
    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/doc/qxorm_fr/customer.html b/doc/qxorm_fr/customer.html new file mode 100644 index 0000000..59e3fe7 --- /dev/null +++ b/doc/qxorm_fr/customer.html @@ -0,0 +1,493 @@ + + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic + editor (data model designer and source code generator) + + + + + + + + + + + + + + + + + + + + + +
    QxOrm + + + Windows + Linux + Macintosh + C++
    +
    + + + + + + + + + + + + + + + + + + +
    AccueilT�l�chargementExemple rapide + Tutoriel (4) + + + + + + +
    + +
    +
    + Manuel (2) + + + + + + +
    + +
    +
    ForumNos clients
    +
    + + + + + + + + + + + + + + + + + +
    + QxOrm >> Nos clients + + + + + + + + + + + + + + +
    + Version courante :  + + QxOrm 1.5.0 - documentation en ligne de la biblioth�que QxOrm - GitHub +
    + + + QxEntityEditor 1.2.8 +
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    +
    + Remarque : la biblioth�que QxOrm est un produit open-source t�l�charg� chaque jour : il est donc + impossible de donner une liste de tous les utilisateurs et de toutes les applications bas�es sur la + biblioth�que QxOrm.
    + Voici une liste d'entreprises utilisant la biblioth�que QxOrm et l'application QxEntityEditor dans leurs + produits commerciaux :
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + AGCO + + AGCO :
    + AGCO Corporation is an American agricultural equipment manufacturer based in Duluth, Georgia, + United States. As a leading global manufacturer of agricultural equipment, AGCO offers a full + line of tractors, combines, hay tools, sprayers, forage and tillage equipment, which are + distributed through more than 3,100 independent dealers and distributors in more than 140 + countries worldwide. +
    + [Description based on Wikipedia webpage] + +

    +
    + ENSCO + + ENSCO :
    + ENSCO, Inc. is a provider of engineering services, products, and advanced technologies for + national security, transportation safety and asset management, information sciences, data + management including information management systems for weather monitoring, aerospace and + avionics, and R and D for private corporations and local, state, and federal agencies and + governments, including the United States Department of Defense. +
    + [Description based on Wikipedia webpage] + +

    +
    + Smiths Detection + + Smiths Detection :
    + Smiths Group plc is a British multinational diversified engineering company headquartered in + London, United Kingdom. It has operations in over 50 countries and employs around 23,550 + staff.
    + Smiths Group has five divisions : Smiths Detection is the world's largest manufacturer of + sensors for the detection of explosives, weapons, chemical agents, biohazards, narcotics and + contraband. +
    + [Description based on Wikipedia webpage] + +

    +
    + Veran Medical + + Veran Medical :
    + Veran Medical Technologies is focused on comprehensive oncology program. + Veran's technology is defining the new standard in early definitive diagnosis of cancer, + providing your hospital with a comprehensive navigation solution. + Established in 2005, Veran Medical Technologies is a privately held soft tissue navigation + company focused on the early diagnosis of cancer. +
    + Veran's mission is to extend life through earlier diagnosis and the delivery of minimally + invasive therapies for interventional oncology procedures with unique navigation + technologies. +
    + Veran is dedicated to increasing patient survival while providing cost effective healthcare + solutions that defined the next standard of care. +

    +
    + Altran AIS + + Altran AIS :
    + Altran Technologies, SA is a global consulting firm founded in 1982 in France. Altran + operates primarily in high technology and innovation consultancy, which account for nearly + 75% of its turnover. Administrative and information consultancy accounts for 20% of its + turnover with strategy and management consulting making up the rest. +
    + [Description based on Wikipedia webpage] + +

    +
    + Intermountain Healthcare + + Intermountain Healthcare + :
    + Intermountain Health Care, Inc., officially doing business as Intermountain Healthcare, is a + non-profit healthcare system and is the largest healthcare provider in the Intermountain + West. Until 2005 it was known as Intermountain Health Care or more commonly IHC; it is now + often referred to as simply Intermountain for short. Intermountain Healthcare provides + hospital and other medical services in Utah and Idaho and also offers integrated managed care + under the insurance brand SelectHealth. Intermountain Healthcare is headquartered in Salt + Lake City, Utah, and (as of 2014) employed over 33,000 people. +
    + [Description based on Wikipedia webpage] + +

    +
    + GSI Electronics Inc. + + GSI Electronics Inc. + :
    + GSI Electronics Inc. develops, manufactures and distributes innovative technological products + for the agricultural industry. GSI Electronics Inc. unique expertise allows them to offer + accurate, simple and diverse electronic, data processing and mechanical solutions for + improving agricultural production. +

    +
    + Sagemcom + + Sagemcom :
    + A French high-tech group of international dimensions, Sagemcom operates on the broadband + (digital home, set-top boxes, Internet routers, telephony and multimedia terminals), telecoms + and energy (smartgrid and energy management) and retail. +

    +
    + Motius + + Motius :
    + Motius is a unique German R&D company. It solves technical problems and develops products for + international customers through a pool of elite senior students, academic researchers and + young engineers. +

    +
    + GSVitec + + GSVitec :
    + Production of high-speed video and measurement data acquisition devices and software.
    + Production of High Power LED light solutions especially for high speed video. Production and + design of High Power custom LED solutions (crash test, military, r&d).
    + Software solutions for synchronous high speed data and video acquisition and recording.
    +

    +
    + Promenade Software + + Promenade Software :
    + Promenade Software is a company comprised of highly skilled, experienced software engineers. + Whether it is for a medical device, avionics test system, or other sensitive system, + Promenade Software knows that their work is critical. + Critical to your company, your customer, and everyone involved. Promenade Software has the + skills and ready-made tools to deliver your product reliably and efficiently. +
    + Unlike other software service providers, Promenade Software took the time to create the + ready-made libraries and test tools that increase your system's quality, while saving you + money and development time. +

    +
    + IVE mbH + + IVE mbH :
    + Since founding IVE mbH, Consulting Company for Traffic and Railway Engineering Ltd., in 1998 + IVE proficiently supports clients from the development of first ideas to the final + realisation and the practical use of projects. + Based on transport and railway expertise, IVE mbH derives concepts and future-oriented + sustainable solutions responding to the problems of clients, strengthening their position and + preparing them for the global competition. +

    +
    + NauchPribor + + NauchPribor :
    + NauchPribor is a high-tech company aimed on developing and producing low dose x-ray devices + for medical and security fields.
    + Founded more than 40 years ago, now this company is one of the most advanced in + business.
    + It has customers from all over the world. Headquartered resides in Orel, Russia.
    +

    +
    + Tang + + Tang :
    + Tang A/S develops a PMS system for the veterinary business.
    + Their products are selling in Denmark, Norway and Sweden.
    +

    +
    +
    + All organization names, logos and trademarks used in this web page are for identification purposes only. + All trademarks and logos are the property of their respective owners. +
    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/doc/qxorm_fr/download.html b/doc/qxorm_fr/download.html new file mode 100644 index 0000000..e5c0972 --- /dev/null +++ b/doc/qxorm_fr/download.html @@ -0,0 +1,1271 @@ + + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic editor + (data model designer and source code generator) + + + + + + + + + + + + + + + + + + + + + +
    QxOrm + + + + Windows + Linux + Macintosh + C++
    +
    + + + + + + + + + + + + + + + + + + +
    AccueilT�l�chargementExemple rapide + Tutoriel (4) + + + + + + +
    + +
    +
    + Manuel (2) + + + + + + +
    + +
    +
    ForumNos clients
    +
    + + + + + + + + + + + + + + + + + +
    + QxOrm >> T�l�chargement + + + + + + + + + + + + + + +
    + Version courante :  + + QxOrm 1.5.0 - documentation en ligne de la biblioth�que QxOrm - GitHub +
    + + + QxEntityEditor 1.2.8 +
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    + + + + + + + + + +
    QxOrm est une biblioth�que C++ disponible sous une double licence : +
      +
    • licence GNU/GPLv3 : open-source et + gratuite (id�ale pour d�velopper un projet open-source ou pour une �valuation de la biblioth�que + QxOrm) ;
    • +
    • licence propri�taire permettant de distribuer une + application bas�e sur la biblioth�que QxOrm sans les restrictions de la licence GNU/GPLv3. +
    • +
    + + + + + + + + + +
    Download QxOrm + + T�l�charger QxOrm version 1.5.0 (source + test + + documentation)
    + Cette version est compatible avec Visual Studio (Windows), GCC (Linux et Mac OS + X), MinGW (Windows) et Clang (Mac OS X) : support de la norme C++11 n�cessaire. +
    +
    +
    +
    qt_ambassador
    + + QxOrm library has been accepted into the Qt Ambassador + Program + +
    +

    QxEntityEditor est un �diteur graphique pour la biblioth�que QxOrm : + QxEntityEditor permet de g�rer graphiquement le mod�le d'entit�s.
    + QxEntityEditor est multi-plateforme (disponible pour Windows, Linux et Mac OS X) et g�n�re du code + natif pour tous les environnements : bureau (Windows, Linux, Mac OS X), embarqu� et mobile (Android, iOS, + Windows Phone, Raspberry Pi, etc.). +

    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    QxEntityEditorQxEntityEditor 1.2.8 - Windows 32 bitsQxEntityEditorQxEntityEditor 1.2.8 - Windows 64 bits
    QxEntityEditorQxEntityEditor 1.2.8 - Windows 32 bits (portable)QxEntityEditorQxEntityEditor 1.2.8 - Windows 64 bits (portable)
    QxEntityEditorQxEntityEditor 1.2.8 - Linux 32 bitsQxEntityEditorQxEntityEditor 1.2.8 - Linux 64 bits
    QxEntityEditorQxEntityEditor 1.2.8 - Mac OS X
    +

    +

    Remarque : par d�faut, l'application QxEntityEditor est limit�e � 5 entit�s + par projet. + Pour obtenir une cl� de licence et ainsi cr�er un nombre illimit� d'entit�s par projet, vous pouvez nous + contacter � l'adresse suivante : ic-east.com. + Prix d'une cl� de licence QxEntityEditor : 500€ (par d�veloppeur, mise � jour gratuite + pendant 12 mois).
    +
    + Un manuel utilisateur (documentation) d�di� � l'application + QxEntityEditor est disponible. +

    + QxEntityEditor +

    +
    +

    QxOrm est bas� sur la biblioth�que Qt. + Vous devez donc t�l�charger et installer le framework Qt avant de pouvoir utiliser QxOrm.

    + + + + + + + + + +
    QtQt : biblioth�que compl�te : IHM + (QtGui), r�seau (QtNetwork), XML (QtXml), base de donn�es + (QtSql), etc...
    + La documentation est excellente et le code C++ �crit � partir + de cette biblioth�que est � la fois performant et simple de + compr�hension.
    + Depuis le rachat par Nokia puis Digia et sa nouvelle licence LGPL, Qt est + sans contexte la biblioth�que phare du moment.
    + QxOrm est compatible avec les principaux objets d�finis par Qt + : QObject, QString, QDate, QTime, QDateTime, QList, QHash, + QSharedPointer, QScopedPointer, etc...
    + Il est conseill� d'installer et d'utiliser la derni�re version + de Qt disponible � l'adresse suivante : http://www.qt.io/
    + +

    + Voici l'historique des modifications apport�es � la biblioth�que QxOrm ainsi que l'application + QxEntityEditor : + +
    + Historique des modifications de la biblioth�que QxOrm : + + + + + + + +
    + +

    Changes in version 1.5.0:

    +
    + - Fix qx::dao::fetch_by_id_with_relation() when a table alias is used (table alias is now used in + the WHERE part instead of table name)
    + - Fix qx::IxDataMember::getType() method when used in a multi-thread environment
    + - New method qx::IxSqlRelation::linkRelationKeyTo() for MongoDB database to simulate lazy loading + for relationships (GitHub #107)
    + - Fix qx::QxSqlDatabase and multi-thread issue when the OS assigns an old and destroyed thread + identifier to a new one (GitHub #42) + add method qx::QxSqlDatabase::removeDatabaseByThread() to + call at the end of a thread execution
    + - Fix qx::QxSimpleCrypt class with Qt version >= 5.10 and error "Attempted to overwrite a + QRandomGenerator to system() or global()"
    + - Fix database not opened using async queries (GitHub #109)
    +
    + +

    Changes in version 1.4.9:

    +
    + - Fix compilation issue with Qt 6.2+ (due to Qt JIRA : https://bugreports.qt.io/browse/QTBUG-92910), + more details in GitHub issue #54
    + - Support QSqlQuery::execBatch() method to improve performance inserting/updating/deleting a list of + C++ instances in database (new optional parameter 'bUseExecBatch' available for functions : + qx::dao::insert, qx::dao::update, qx::dao::delete_by_id)
    + - New method in qx::QxSqlQuery class named setFctOnBeforeSqlPrepare() to define a custom callback + function to modify SQL query before preparing in database
    + - New callbacks functions available in qx::IxDataMember interface to customize SQL generated per + data member (see an example in ./test/qxBlogCompositeKey/src/blog.cpp file)
    + - Fix an issue in qx::QxSqlRelationLinked::hierarchyResolveOutput() which could call + getIdFromQuery() for nothing, which could generate some warnings in Qt SQL driver (for example : + QPSQLResult::data: column XX out of range)
    + - Fix a compilation issue of QxRestApi and QxHttpTransaction modules with Qt QStringBuilder option + enabled (GitHub issues #50, #83)
    + - Improve SQL DISTINCT to support relationships (unit test available in qxBlog sample project)
    + - New feature to fetch relationships only in LEFT OUTER/INNER JOIN and WHERE clauses (so no columns + in SELECT part) : use {NULL} syntax to define no relation columns in SELECT part (unit test + available in qxBlog sample project)
    + - Fix a crash which could occur using qx::QxSession and multi-threaded environment
    + - Add a new parameter caseSensitive (default value : false) to the method + qx::QxSqlQuery::getSqlResultAt()
    + - Fix an issue with JSON REST API (QxRestApi module) and MongoDB database (more details in GitHub + issue #70)
    + - Improve qx::QxClassX::registerAllClasses() : init all validator instances (can fix some issues in + a multi-thread environment)
    + - New settings available in the qx::QxSqlDatabase singleton class + (setSqlDelimiterForTableNameAlias() and setSqlDelimiterForColumnNameAlias()) to add delimiters to + SQL aliases (more details in GitHub issue #57)
    + - Fix linking error which could occur with Qt6 and MSVC2019 (should fix GitHub issues #98, #91, #89, + #90, #62, #65)
    + - Fix all checks with QT_VERSION (using macro QT_VERSION_CHECK), should fix checks from Qt 5.10 to + Qt 5.15 (GitHub issue #81)
    +
    + +

    Changes in version 1.4.8:

    +
    + - Support Qt6 (tested with MSVC 2019 + CMake 3.19 on Windows) ==> QxOrm library is compatible + with Qt4, Qt5 and Qt6
    + - Support PIMPL idiom for persistent classes (useful to reduce compilation times, to provide a + stable ABI, ascendant compatibility for shared libraries, to reduce binaries size)
    + - Documentation about PIMPL idiom for persistent classes : + https://www.qxorm.com/qxorm_en/manual.html#manual_455
    + - New sample project named qxBlogPImpl in ./test/ directory of QxOrm package to show how to + implement persistent classes using the PIMPL idiom
    + - Possibility to ignore soft delete behavior during a session execution (useful to fetch logical + deleted instances for example) : qx::QxSession::ignoreSoftDelete()
    + - Fix SQL DISTINCT keyword in qx::QxSqlQuery class (when DISTINCT is used, then the primary key is + not fetched)
    + - Fix an issue which could happen after enabling _QX_ENABLE_QT_NETWORK compilation option (due to + QT_NO_SSL/QT_NO_OPENSSL used in QxThread.cpp file)
    + - Fix an issue in qx::QxSqlDatabase and multi-thread context when a thread identifier is reused + (GitHub issue #42)
    + - Add new behavior to qx::QxSession class with setAutoRollbackWhenDestroyed() option : if true, then + database rollback is called when session instance destructor (or close() method) is invoked (instead + of commit by default, GitHub issue #43)
    +
    + +

    Changes in version 1.4.7:

    +
    + - Fix a MongoDB issue with qx::dao::fetch_by_id_with_relation() (wrong id fetched)
    + - Fix an issue with qx::dao::fetch_all when a custom list of columns with the primary key is + used
    + - Fix identifier JSON serialization for MongoDB with properties registered as Q_PROPERTY (Qt + property meta system)
    + - Fix a crash (seg fault) using qx::IxPersistable with multiple inheritance (for example QObject + + qx::IxPersistable) due to some static_cast to void * in QxFactory module (fix also for QxService + module and multiple inheritance)
    + - Remove automatic relationship lazy fetch when not requested in query
    + - Fix an issue with QJson and QDataStream serialization when class doesn't contain any identifier + (so not a database class) and is a wrapper of another registered class (can have same address + pointer as parent)
    + - Improve qx::QxSqlQuery::freeText() method to add custom text to build SQL query : support + placeholders (add second parameter const QVariantList & values)
    + - Improve performance in a multi-threads environment (mutex in qx::IxSqlQueryBuilder)
    + - New setting to display more details (execution times) in qx::dao functions logs (to enable this + new feature : qx::QxSqlDatabase::getSingleton()->setDisplayTimerDetails(true))
    + - Improve MongoDB integration to fetch a list of items : build C++ instances as soon as possible + without putting data in a buffer (better performance + less memory usage)
    + - Improve MongoDB integration : support qx::dao::delete_by_query() function with MongoDB aggregation + framework
    + - Improve qx::QxSqlQuery class (or its qx_query alias) : new addJoinQuery() method to insert SQL + sub-queries inside LEFT OUT JOIN / INNER JOIN (more details in documentation here : + https://www.qxorm.com/qxorm_en/manual.html#manual_3855)
    + - Improve qx::QxSqlQuery class : new constructors with placeholders support + new methods to embed + SQL sub-queries (in_Select, notIn_Select, isEqualTo_Select, isNotEqualTo_Select)
    + - Improve qx::QxCollection class : qx::QxCollection is now a thread-safe container (fix for example + QxModelView module when model is fetched in a different thread than the view)
    +
    + +

    Changes in version 1.4.6:

    +
    + - New QxHttpServer module : C++/Qt standalone multi-threaded HTTP 1.1 web server (support SSL/TLS, + persistent connections, cookies, sessions, chunked responses, URL dispatcher/routing, no other + dependency except QtNetwork) : https://www.qxorm.com/qxorm_en/manual.html#manual_96
    + - New QxRestApi module : provide a REST API to send requests in JSON format from external + application (web services), from web-site (written in Angular for example), from QML or from + scripting langage (like Python) : https://www.qxorm.com/qxorm_en/manual.html#manual_97
    + - With QxHttpServer and QxRestApi modules, QxOrm library can now be used to create web applications, + especially single-page applications (SPA) with famous Javascript frameworks like AngularJS, React, + Meteor.js, etc...
    + - New project example named qxBlogRestApi : QML application with a list of REST requests to show how + to send JSON queries from Javascript to QxRestApi module + HTTP web server application to show how + to create web applications using QxHttpServer module
    + - QxRestApi module supports : all CRUD operations, complex queries, several levels of relationships, + custom JSON output format, call dynamically native C++ functions registered in QxOrm context, + instance validation, call custom database queries
    + - Improve JSON serialization engine : possibility to define a custom filter to not export all + properties (https://www.qxorm.com/qxorm_en/manual.html#manual_606)
    + - Possibility to define a custom SQL table alias for complex queries with relationships using syntax + <my_table_alias> (https://www.qxorm.com/qxorm_en/manual.html#manual_3850)
    + - Improve SQL generator for Oracle database : manage last insert id using RETURNING INTO syntax (thx + to Romain Macureau and Abdennour Boutrig)
    + - Fix an issue with stored procedure and output parameters
    + - New function available : qx::dao::count_with_relation<T>()
    + - Fix JSON serialization in multi-thread environment
    + - Change JSON QDateTime and QTime serialization format : use Qt::ISODateWithMs instead of + Qt::ISODate (Qt 5.8 or +)
    + - Improve QxService module : support SSL/TLS secure connections + keep-alive connections
    + - Remove *.suo files (MSVC++ temporary project files) from QxOrm package
    +
    + +

    Changes in version 1.4.5:

    +
    + - Support MongoDB database : QxOrm library becomes a C++/Qt Object Document Mapper ODM library !
    + - For more details about MongoDB integration, see QxOrm manual + (https://www.qxorm.com/qxorm_en/manual.html#manual_95) and new sample project available in + ./test/qxBlogMongoDB/ directory
    + - QxOrm library is now available on GitHub (official repository) : + https://github.com/QxOrm/QxOrm
    + - Fix an issue in qx::IxSqlQueryBuilder class when QxOrm library is used in a multi-thread + environment
    + - Support latest version of boost (1.66)
    + - Update boost portable binary serialization classes to version 5.1 (provided by + https://archive.codeplex.com/?p=epa)
    + - Fix an issue building SQL query for Oracle database (doesn't support AS keyword for table + alias)
    + - Improve qx::QxClassX::registerAllClasses() function : possibility to initialize all relations + (useful to work with introspection engine)
    + - Improve qx::IxPersistable interface : provide new methods toJson() / fromJson()
    + - Improve documentation/website : change http://www.qxorm.com by https://www.qxorm.com + everywhere
    + - Fix fetching relations with soft delete putting SQL condition in the JOIN part instead of WHERE + part
    + - Fix SQL generator for Oracle database : use new limit/pagination syntax (version Oracle > + 12.1)
    + - Improve SQL generator interface : add 'onBeforeSqlPrepare()' method to modify/log SQL queries in + custom classes
    + - Add an option in qx::QxSqlDatabase class to format SQL query (pretty-printing) before logging it + (can be customized creating a qx::dao::detail::IxSqlGenerator sub-class)
    + - Fix an issue with boost/std::optional (to manage NULL database values) and some databases : if + optional is empty, then create a NULL QVariant based on QVariant::Type
    + - Add an option in qx::QxSqlDatabase class to insert square brackets (or any other delimiters) in + SQL queries for table name and/or column name (to support specific database keywords)
    + - Improve introspection engine : add getType() method in qx::IxDataMember interface to get C++ type + of a property dynamically
    + - Improve qx::QxSqlDatabase singleton settings class to make easier working with several databases : + now there are 3 levels of settings : global >> per thread >> per database (see + 'bJustForCurrentThread' and 'pJustForThisDatabase' optional parameters in all setXXXX() methods)
    + - Fix QxOrm.pri for MinGW compiler on Windows : an issue could occurred to export some symbols from + shared library (some Qt signals for example)
    + - Add an option in qx::QxSqlDatabase singleton class to display only slow SQL queries (see + setTraceSqlOnlySlowQueriesDatabase() and setTraceSqlOnlySlowQueriesTotal() methods)
    +
    + +

    Changes in version 1.4.4:

    +
    + QxOrm library doesn't depend on boost framework anymore (the boost dependency has been fully + removed, replaced by some C++11 features).
    + So QxOrm library is now a pure Qt library which depends only on QtCore and QtSql by default.
    + For backward compatibility, QxOrm library still supports some boost classes (boost smart-pointers, + unordered containers, boost::optional, etc...) : you have to define _QX_ENABLE_BOOST compilation + option to enable these features.
    +
    + Main advantages are :
    + - QxOrm becomes a much lighter library
    + - easier to install (because you don't have to deal with boost anymore)
    + - reduce compilation times
    + - reduce output binary size
    +
    + Thx also to Jimmy Taker for several improvments and new features in QxModelView module !
    +
    + - QxOrm library now requires a C++11 compiler (please note that QxOrm doesn't require a full + compliant C++11 compiler : for example, QxOrm can be built and used with MSVC 2012, GCC 4.5 or Clang + 3.2)
    + - Implement PIMPL idiom for some QxOrm classes to reduce compilation times and output binary + size
    + - New class named qx::any to replace boost::any (basic implementation of boost::any written by + Kevlin Henney)
    + - qx_shared_ptr alias doesn't exist anymore : it is replaced everywhere by std::shared_ptr
    + - QxModelView module : all models based on qx::IxModel class can now be sorted (on all columns), + please note that you can also use QSortFilterProxyModel Qt class to sort your model
    + - QxModelView module - qx::QxModel<T> : fix setData() with e_auto_update_on_field_change + option when an error occurred saving data in database, now previous value is restored if an error + occurred
    + - QxModelView module - qx::IxModel : fix setHeaderData() using it with default role (Qt::EditRole) + changes the header in a header view (role Qt::DisplayRole)
    + - QxModelView module - qx::IxModel : if a description is registered in QxOrm context, then it is + displayed in header for each property
    + - QxModelView module : new feature available to add automatically an empty row at the end of the + table to insert quickly new items (setShowEmptyLine() method)
    + - QxModelView module : possibility to define an intermediate base class between qx::IxModel and + qx::QxModel<T> to provide your own model features, for example imagine you develop a drag&drop + feature in a class named IxModelDragDrop, you can now create a QxOrm model like this (see the second + template parameter) : qx::IxModel * pModel = new qx::QxModel<MyPersistantClass, + IxModelDragDrop>();
    + - QxOrm.pro : fix DESTDIR parameter on Windows
    + - QxOrm.pri and QxOrm.cmake : add a section to enable QT_USE_QSTRINGBUILDER to optimize QString + operations
    + - QxOrm library is now tested with MSVC 2015 compiler (support all MSVC versions from 2012)
    + - Fix a bug in QxSqlError.h file with a qPrintable() call on a temporary object
    + - Provide more details in logs after executing a SQL query : time to execute query in database + + time to fetch C++ instances
    + - Support std::optional<T> (if your compiler supports C++17 features) to manage NULL database + value : new header available named <QxExtras/QxStdOptional.h> to include just after + <QxOrm.h> header file (ideally in a precompiled header)
    +
    + +

    Changes in version 1.4.3:

    +
    + - Support CMake : new CMakeLists.txt file added to build QxOrm library with CMake
    + - Improve SQL error messages when qx::dao functions return a database error
    + - New parameter in singleton class qx::QxSqlDatabase to log SQL bound values + (setTraceSqlBoundValues) : by default, bound values are logged when an error occurred
    + - New syntax to select columns to not fetch : -{ col_1, col_2, etc... }
    + - New function qx::dao::call_query_without_prepare() to execute specific SQL queries without + prepared statement
    + - Improve QxModelView module : all QxOrm models (based on qx::IxModel interface) can be serialized + to JSON format (including all relationships levels) : this is now another way to work with + relationships and QML (thanks to JSON.parse() and JSON.stringify() javascript functions) without + using nested models concept (so without using QxEntityEditor model/view generated classes)
    + - Improve qxBlogModelView sample project and QxOrm manual to show how to access to relationships + data in QML (nested models or JSON)
    + - Fix a memory leak in qx::QxSqlRelation class
    + - Reduce output binary size (~20%) and compilation times (~20%) to build persistent classes based on + QxOrm library
    + - Support unity build concept to reduce compilation times to build QxOrm library and C++ persistent + classes generated by QxEntityEditor application : for more details, see _QX_UNITY_BUILD compilation + option in QxOrm.pri or QxOrm.cmake configuration file
    + - Improve QxConvert module : possibility to store in database complex QVariant properties which + contain QVariantMap, QVariantHash or QVariantList types (JSON format)
    + - Fix an issue with some databases when a foreign key is also a part of the primary key
    + - Fix an issue with QSharedPointer and boost::serialization when a same raw pointer is shared by + several QSharedPointer during deserialization process
    +
    + +

    Changes in version 1.4.2:

    +
    + - Support JSON serialization : each C++ class registered in QxOrm context can be + serialized/deserialized in JSON format (JSON feature requires Qt5)
    + - For more details about JSON serialization, read QxOrm manual here : + https://www.qxorm.com/qxorm_en/manual.html#manual_606
    + - With JSON serialization and QxService module : it is now possible to create REST Web Services to + send data to a javascript engine (web pages for example)
    + - Fix some compilation errors with recent (and less permissive) compilers and latest versions of + boost and Qt
    + - Fix relationship initialization assertion with complex, deep and circular relationships in large + database schema
    + - Improve QDataStream serialization : should be faster now and fix an issue with circular instances + dependencies
    + - Fix a bug fetching 1-n and n-n relationships when root is a container of stack objects (it worked + only with pointers or smart-pointers, for example : QList<blog> vs + QList<std::shared_ptr<blog>>)
    + - Improve qx::dump() function : possibility to display a C++ instance state in XML or JSON + format
    +
    + +

    Changes in version 1.4.1:

    +
    + !!! IMPORTANT NOTE ABOUT THIS VERSION !!! : it is strongly recommended to read the QxOrm.pri + configuration file of this new version (compilation options have changed compared to previous + versions).
    + Now, by default, QxOrm library is a much lighter library : QxOrm depends only on QtCore and + QtSql (boost serialization is now optional and not required by default).
    + By default, serialization engine is now based on Qt QDataStream class (but you can still enable + boost serialization defining _QX_ENABLE_BOOST_SERIALIZATION compilation option in QxOrm.pri + configuration file).
    + So now, with default options :
    + - QxOrm 1.4.1 is much easier to install because you don't have to deal with boost serialization + extra dependency ;
    + - QxOrm 1.4.1 shared library is 3X smaller than 1.3.2 version ;
    + - Generated binaries which depends on QxOrm library are 25% smaller ;
    + - If you are not using serialization functions in current projects based on QxOrm library, then you + can define or not _QX_ENABLE_BOOST_SERIALIZATION compilation option without changing any line of + your source code.
    +
    + Here are all other changes of version 1.4.1:
    + - Improve relationships engine : possibility to select columns to fetch using syntax : my_relation { + col_1, col_2, etc... }
    + - Improve QxTraits module to reduce compilation times and build smaller binaries
    + - Improve QxOrm website adding possibility to search and replacing the old FAQ by a more organized + manual (user guide)
    + - New compilation option _QX_ENABLE_BOOST_SERIALIZATION to enable boost serialization dependency + (read QxOrm.pri configuration file for more details)
    + - New compilation option _QX_ENABLE_QT_NETWORK to enable QxService module (transfer persistent layer + over network) : read QxOrm.pri configuration file for more details
    + - New compilation option _QX_NO_RTTI to build QxOrm library without C++ RTTI type information
    + - Support QDataStream Qt serialization engine (used by default when _QX_ENABLE_BOOST_SERIALIZATION + compilation option is not defined)
    + - Improve qx_query class (SQL queries) : new method (named customOperator()) which gives the + possibility to define a custom operator (for example <@ for PostgreSQL ltree type)
    + - Fix a program startup issue due to 'static initialization order fiasco' creating singletons (it + was an issue with some compilers during the shared library link process)
    + - New namespace qx::dao::throwable : same functions as qx::dao namespace, but they throw a + qx::dao::sql_error exception when a SQL error occurred (instead of returning a QSqlError + instance)
    + - Add a qAssertMsg() macro to put a more explicit error message when throwing an assertion
    + - Include all *.inl files (template implementation) in QxOrm.pro project file : QtCreator can now + index these *.inl files in its project treeview
    + - Rename QxStringCvt to QxConvert : so if you persist custom types to database, you have to rename + from QxStringCvt_FromVariant, QxStringCvt_ToVariant to QxConvert_FromVariant, + QxConvert_ToVariant
    +
    + +

    Changes in version 1.3.2:

    +
    + - Support C++11 types (need to set compilation options in QxOrm.pri config file to enable these + features)
    + - With _QX_CPP_11_SMART_PTR compilation option : std::unique_ptr, std::shared_ptr, std::weak_ptr
    + - With _QX_CPP_11_CONTAINER compilation option : std::unordered_map, std::unordered_set, + std::unordered_multimap, std::unordered_multiset
    + - With _QX_CPP_11_TUPLE compilation option : std::tuple
    +
    + +

    Changes in version 1.3.1:

    +
    + - New class qx::QxModelService<T, S> in QxModelView module to connect a Qt model to services + to execute client-server requests (can be used with QML and QtWidgets views)
    + - Add some useful methods to qx::IxModel class and fix several issues with the QxModelView + module
    + - Support last version of MinGW with large precompiled header bug : new compilation option + _QX_NO_PRECOMPILED_HEADER (to enable in QxOrm.pri file)
    + - Fix issue when loading several shared libraries on Windows with services registered in QxService + module
    + - Fix the qx::QxSqlQuery serialization process used by QxService module to send requests over + network
    + - Fix an issue with qx::QxCollection<Key, Value> class when inserting an item at last + position
    +
    + +

    Changes in version 1.2.9:

    +
    + - Improve nested models in QxModelView module to be able to use several relationships levels in + QML
    +
    + +

    Changes in version 1.2.8:

    +
    + - New function qx::model_view::create_nested_model (QxModelView module) used by QxEntityEditor to + manage complex data structure to work with relationships in QML views and Qt model/view + architecture
    + - New section in the QxOrm.pri file with some tips to reduce output binaries size
    + - Fix the call of triggers to have the inserted ID inside the trigger function with PostgreSQL
    +
    + +

    Changes in version 1.2.7:

    +
    + - New module QxModelView : now, all classes registered into QxOrm context can be used with Qt + model/view architecture (Qt widgets and/or QML views)
    + - qx::IxModel interface provides an easy way to work on QML with QxOrm library and interact with + databases
    + - For more details about the new module QxModelView, goto the FAQ : 'How to use QxModelView module + to interact with Qt model/view architecture (Qt widgets and/or QML views) ?'
    + - New function qx::dao::save_with_relation_recursive(), useful to save a full tree structure for + example
    + - Remove the dependency on the STL compatibility functions in Qt (QT_NO_STL), which may not be + available (thanks to KDE Plasma Media Center team for the patch)
    + - Support database table defined into a schema (using qx::IxDataMember::setName() function)
    +
    + +

    Changes in version 1.2.6:

    +
    + - First version full compatible with QxEntityEditor application : the graphic editor for QxOrm + library !
    + - For more details about QxEntityEditor, go to QxOrm website : https://www.qxorm.com/
    + - Thanks to the recent release of Qt 5.2, QxOrm library can now be used on Android and iOS
    + - Improve relationships and triggers engine
    + - Triggers onBeforeFetch() and onAfterFetch() called when fetching relationships
    + - Fix release mode detection during compilation : should improve performance on some + environments
    + - Add serialization for QSqlError, qx::QxSqlQuery, qx::QxInvalidValue and qx::QxInvalidValueX + classes
    +
    + +

    Changes in version 1.2.5:

    +
    + - New license : go to download page of QxOrm website for more details
    + - Support Qt5
    + - New compiler supported : Clang (tested on Mac OS X)
    + - Now each QxOrm version will be tested in 32-bit and 64-bit mode
    + - Improve QxOrm introspection engine : possibility to register static class methods
    + - Improve QxService module : now it's easy to add an authentication process on server side
    + - New class qx::exception to get error code + error description with services methods throwing an + exception
    + - New settings available in QxOrm.pri config file (whithout changing QxConfig.h file)
    + - Possibility to implement specifics database SQL functions overriding qx_query class
    + - Fix an issue when fetching multiple levels of relationship and NULL pointers
    + - Fix a bug with MS SQL Server database and update queries using auto-increment id
    +
    + +

    Changes in version 1.2.4:

    +
    + - New relationship engine to fetch easily many levels of relationships per query
    + - For more details about this new engine, goto the FAQ : 'How to use relationship engine to fetch + datas from many tables ?'
    + - Add 2 functions : qx::dao::execute_query and qx::dao::call_query to call a stored procedure or a + custom SQL query
    + - For more details about this new feature, goto the FAQ : 'How to execute a stored procedure or a + custom SQL query ?'
    + - Add support for boost::optional type to manage NULL database value without using QVariant type
    + - New class : qx::QxDaoAsync to make easier to execute queries in asynchronous way + (multi-thread)
    + - For more details about this new class, goto the FAQ : 'How to use qx::QxDaoAsync class to execute + queries in asynchronous way (multi-thread) ?'
    +
    + +

    Changes in version 1.2.3:

    +
    + - New interface 'qx::IxPersistable' (abstract class) to simplify polymorphism using QxOrm + library
    + - For more details about this new interface, goto the FAQ : 'How to use qx::IxPersistable interface + ?'
    + - New methods into 'qx::IxCollection' interface to iterate over each items without knowing its + type
    + - New option into 'QxOrm.pri' file to build QxOrm library statically (see '_QX_STATIC_BUILD' + option)
    + - New triggers : 'qx::dao::on_before_fetch' and 'qx::dao::on_after_fetch' (for more details, goto + the FAQ : 'How to define a Trigger with QxOrm ?')
    + - Add 'std::type_info' class information to introspection engine
    + - Some minor bugs fixed ('qx::dao::sql_error' exception message, SQL query column alias, mutex, + etc.)
    +
    + +

    Changes in version 1.2.2:

    +
    + - New module to provide a validation engine : QxValidator module
    + - For more details about QxValidator module, goto the FAQ of QxOrm library : 'How to use QxValidator + module to validate automatically an instance ?'
    + - Fix last insert ID with PostgreSQL using 'RETURNING' keyword : fetch inserted ID instead of + OID
    + - Improve SQL generator providing the good SQL type for all databases
    + - Add support for special database keywords using '[', ']' and '"' characters
    +
    + +

    Changes in version 1.2.1:

    +
    + - Improve 'qx::QxSqlQuery' class : new engine to build queries without writing SQL, for more + details, see the FAQ 'How to build a query without writing SQL with the class qx::QxSqlQuery ?'
    + - Improve 'qx::QxSession' class : provide persistent methods (CRUD) without using 'qx::dao::xxx' + functions, for more details, see the FAQ 'How to use a session (qx::QxSession class) to manage + automatically database transactions (using C++ RAII) ?'
    + - Implement 'repository' pattern to provide a common interface for persistent methods (CRUD) with 3 + new classes : 'qx::IxRepository', 'qx::QxRepository<T>' and 'qx::QxRepositoryX'
    + - Possibility to serialize a QVariant 'UserType' with serialization engine of QxOrm library
    + - Improve thread-safe 'qx::cache' : add insertion date-time into the cache to verify that an element + must be updated or not, for more details, see the FAQ 'How to use the cache (functions into + namespace qx::cache) of QxOrm library ?'
    + - FAQ updated on QxOrm website with now 28 questions and answers
    +
    + +

    Changes in version 1.1.9:

    +
    + - Possibility to register automatically Qt meta-properties (using Q_PROPERTY macro) to QxOrm context + without writing mapping function per class (void qx::register_class<T>())
    + - Strong integration with Qt introspection/moc engine : for more details about this new feature, + goto the FAQ 'How to register automatically Qt meta-properties to QxOrm context ?'
    + - Improve introspection/reflection engine : see the FAQ (How to use introspection engine (or + reflection engine) of QxOrm library ?) for more details
    + - Possibility to add meta-data (using a property bag) to introspection engine : see 'IxClass', + 'IxDataMember' and 'IxFunction' classes for more details
    + - Add function 'qx::QxClassX::dumpSqlSchema()' to explain how to create your own SQL schema based on + C++ classes
    + - New class 'qx::QxSimpleCrypt' to provide encryption/decryption (thanks very much to Andre Somers) + : so it's now possible to store encrypted data into database without using an external library
    + - QxService module : new feature to encrypt/decrypt data before transfering it over network
    +
    + +

    Changes in version 1.1.8:

    +
    + - QxOrm library can now be used on Mac (thanks very much to Dominique Billet) : see + 'osx_build_all_debug.sh' and 'osx_build_all_release.sh' scripts to build QxOrm library and all + samples in './test/' directory
    + - Add 'qx::QxSession' class : define a session to manage automatically database transactions (using + C++ RAII), see the FAQ for more details
    + - Add 'qx::QxDateNeutral', 'qx::QxTimeNeutral' and 'qx::QxDateTimeNeutral' classes : helper classes + to store date-time value into database under neutral format => cross database compatibility
    +
    + +

    Changes in version 1.1.7:

    +
    + - Add soft delete behavior : see the FAQ (How to define a soft delete behavior ?) for more details + about this new feature
    + - Add functions into namespace 'qx::dao' to update an element with a SQL condition : + update_by_query, update_optimized_by_query, etc.
    + - Fix a bug when QVariant type is used for a property of a persistent class : so, it's now possible + to insert NULL value into database
    +
    + +

    Changes in version 1.1.6:

    +
    + - QxOrm library online documentation available : 'https://www.qxorm.com/doxygen/index.html'
    + - Possibility to disable QtGui dependency using compilation option in 'QxConfig.h' file : + _QX_ENABLE_QT_GUI_DEPENDENCY
    + - Possibility to disable QtNetwork dependency (so QxService module too) using compilation option in + 'QxConfig.h' file : _QX_ENABLE_QT_NETWORK_DEPENDENCY
    + - Provide a new macro to register abstract class into QxOrm context : + QX_REGISTER_ABSTRACT_CLASS()
    +
    + +

    Changes in version 1.1.5:

    +
    + - New feature available : 'QxService' module to create C++ application server
    + - 'QxService' provides an easy and powerful way to create services and to transfer data over + network
    + - New tutorial available to explain how 'QxService' module works
    + - New sample available at './test/qxClientServer' directory
    + - QxOrm can be built with 'CONFIG += no_keywords' flag in '*.pro' files
    + - Bug fix with 'qx::dao::create_table<>' function and relation 'many-to-many'
    + - QxOrm should now build fine with GCC <= 4.2
    +
    + +

    Changes in version 1.1.4:

    +
    + - New parameter in functions 'qx::dao::fetch_by_id', 'qx::dao::fetch_all', 'qx::dao::fetch_by_query' + and 'qx::dao::update' to define a list of properties to fetch/update (by default, all properties are + fetched/updated)
    + - Support multi-columns primary key (composite key) : see sample './test/qxBlogCompositeKey/'
    + - Improve strategy of inheritance : QxOrm supports 'Concrete Table Inheritance' strategy ('Concrete + Table Inheritance' becomes default strategy)
    + - New smart-pointer 'qx::dao::ptr<T>' based on Qt 'QSharedPointer<T>' to provide 2 new + features : 'is dirty' and 'update optimized'
    + - 'qx::dao::ptr<T>' can be used with a simple object and with many containers (stl, boost, Qt + and 'qx::QxCollection' containers)
    + - 'qx::dao::ptr<T>' keeps original values from database and provides a 'isDirty()' method to + retrieve all properties changed
    + - 'qx::dao::update_optimized' must be used with 'qx::dao::ptr<T>' to save into database only + properties changed
    +
    + +

    Changes in version 1.1.3:

    +
    + - This version works fine with MinGW on Windows
    +
    + +

    Changes in version 1.1.2:

    +
    + - License LGPL
    + - Fix compilation problems on Linux and boost > 1.38
    + - Fix sql query with MySql database
    + - Disable assert when qx::dao functions return an error
    +
    + +

    Changes in version 1.1.1:

    +
    + - This version supports Visual Studio 2010
    +
    + +

    Changes in version 1.1.0:

    +
    + - First release
    +
    +

    +
    + Historique des modifications de l'application QxEntityEditor : + + + + + + + +
    + +

    Changes in version QxEntityEditor 1.2.8:

    +
    + - New export plugin 'QxEESourceControlExport' : export a *.qxee QxEntityEditor project file to a + list of JSON files which can be easily check-in in a source control repository (Git, Perforce, CVS, + etc...)
    + - New import plugin 'QxEESourceControlImport' : import a list of JSON files (generated using + 'QxEESourceControlExport' export plugin) inside a *.qxee QxEntityEditor project file
    + - These 2 new plugins allow a team development to work simultaneously on a same *.qxee + QxEntityEditor project file
    + - A typical workflow could be (with 2 developers named dev A and dev B) :
    + * dev A and dev B work on a same *.qxee QxEntityEditor project file
    + * dev A create/modify/change some entities in QxEntityEditor application
    + * dev B create/modify/change other entities in QxEntityEditor application
    + * dev A and dev B export the *.qxee project file using the new 'QxEESourceControlExport' export + plugin
    + * Once exported, dev A and dev B check-in the JSON files inside the source control manager (Git, + Perforce, CVS, etc...) on their own branch
    + * dev A and dev B integrate from their own branch to the DEV (or MAIN or MASTER) branch (if there + are some conflicts to resolve, it should be easy because JSON files are easy to read and + understand)
    + * dev A and dev B can now get latest JSON files from the DEV (or MAIN or MASTER) branch : these JSON + files contain both modifications from dev A and dev B
    + * dev A and dev B can import these JSON files inside QxEntityEditor application using the new + 'QxEESourceControlImport' import plugin
    + - These 2 new plugins can be executed using command line (like all other QxEntityEditor plugins), + here are 2 examples :
    + * Export : QxEntityEditor --no_gui --project="C:\Temp\qxBlog.qxee" --plugin=QxEESourceControlExport + --QxEESourceControlExport_path="C:\Temp\source_control\"
    + * Import : QxEntityEditor --no_gui --project="C:\Temp\qxBlog.qxee" --plugin=QxEESourceControlImport + --QxEESourceControlImport_path="C:\Temp\source_control\qxBlog.qxee.export.json"
    +
    + +

    Changes in version QxEntityEditor 1.2.7:

    +
    + - Fix an issue with MySQL database schema import process when MySQL server is configured to answer + with column names in UPPERCASE (may fix other imports depending on specific database + configuration)
    +
    + +

    Changes in version QxEntityEditor 1.2.6:

    +
    + - Support PIMPL (private implementation or d-pointer) idiom in the C++ export plugin
    + - Documentation about PIMPL idiom for persistent classes : + https://www.qxorm.com/qxorm_en/manual_qxee.html#cpp_export_settings_parameters
    + - Fix import database schema for PostgreSQL version 12 and +
    + - Fix some assertions on macOS and linux versions due to libavoid library compiled without NDEBUG + compilation option (should improve performance too)
    + - Add CMake 'target_precompile_headers' command for generated CMakeLists.txt files to support + precompiled headers building with CMake (reduce compilation times)
    +
    + +

    Changes in version QxEntityEditor 1.2.5:

    +
    + - Fix an issue with fonts on macOS Catalina 10.15
    + - Add command line parameter --font : define application font with syntax + <family>||<pointSize>||<weight>||<italic> (only <family> is required), + for example : Courier New||14
    + - Add command line parameter --style_sheet : define application style sheet (more details here : + https://doc.qt.io/qt-5/stylesheet-reference.html), for example : QWidget { background-color: black + }
    + - Fix meta-data (property bag) C++ export : keep same order between several C++ exports (this is now + easier to check differences between 2 C++ exports using WinMerge or any other comparison/diff + tool)
    +
    + +

    Changes in version QxEntityEditor 1.2.4:

    +
    + - New feature in Javascript engine to customize exports : include QxRestApi module to query *.qxee + project file
    + - Fix a property/relation sorting issue in C++ export after importing a database schema (which could + be annoying to make some C++ files diffs between 2 exports)
    + - Fix a crash which could occurred sometimes importing a new table on an existing schema
    +
    + +

    Changes in version QxEntityEditor 1.2.3:

    +
    + - Fix a crash which appears sometimes with complex database schema to draw relationships (orthogonal + way)
    + - Improve QxEntityEditor command line parameters : possibility to import/export without using GUI + (useful to manage a Jenkins server for example)
    + - For more details about command line parameters, go to QxEntityEditor documentation : + https://www.qxorm.com/qxorm_en/manual_qxee.html#qxee_command_line
    +
    + +

    Changes in version QxEntityEditor 1.2.2:

    +
    + - Fix an issue after importing a project (from database or JSON file) which forced user to refresh + or reload current project
    + - Improve performance (fetching less data from database) when loading relationships from generated + persistent classes (bLoadFromDatabase parameter)
    + - Change model/view export plugin to take into account some improvments available in the new version + of QxOrm library
    + - Support changes of new QxOrm version : remove boost dependency by default and enable C++11 + features
    + - Improve Javascript engine to customize exports : add 3 functions available in js script to get all + QxEntityEditor settings (at global level, project level and plugin level)
    + - Check entities schema before exporting to C++ project : if a design error is detected, then + display a message to user
    + - Fix SQL type field in property settings : now you can put parenthesis (for example VARCHAR(255) is + now allowed)
    + - DDL export for MySQL (and MariaDB) : add ` character to escape column name and table name
    + - Add full path to project in recent projects list and main window title bar
    + - Now it is easier to select a relationship from entities diagram with a simple click on it
    +
    + +

    Changes in version QxEntityEditor 1.2.1:

    +
    + - Support CMake : each C++ project generated by QxEntityEditor (persistent classes, services, + model/view) provide a CMakeLists.txt file to build with CMake
    + - New online QxEntityEditor manual (user guide) available at : + https://www.qxorm.com/qxorm_en/manual_qxee.html
    + - Improve Javascript engine to customize export generated files : add a parameter named + 'output_location' to know where generated files are located + add functions to get a list of all + entities/enums of a project (see ./samples/custom_script.js file for more details)
    + - Improve database import process (SQLite, MySQL/MariaDB, PostgreSQL, Oracle and MSSQLServer) : fix + an issue importing composite foreign keys + foreign keys embedded in primary key ==> so it is now + possible to import more complex database schema
    + - Improve DDL database schema export process : fix an issue with 1-1 relationship + manage composite + keys
    + - New menu "Naming convention" to provide a fast way to rename all entities/properties/enums + (without breaking mapping to database) : support snake_case, camelCase and PascalCase (or upper + camel case), this new menu can be useful after an import from database process for example
    + - Each screen of QxEntityEditor provides now a fast access to the online manual (user guide) in its + associated topic (new button "Documentation" + shortcut pressing F1 key everywhere)
    + - New buttons undo/redo in the main QxEntityEditor toolbar to undo or redo actions done on entities, + enumerations, comments, layout (undo/redo feature can be disabled to improve performance on large + projects)
    + - Support _QX_UNITY_BUILD compilation option to reduce compilation times of generated C++ projects + (recommended with CMake which doesn't support natively precompiled headers)
    + - Fix an issue importing a relationship where target entity doesn't have a primary key
    + - Fix an issue with C++ services export plugin and JSON serialization : "Unable to create nude + pointer for input parameter"
    + - Fix an issue with abstract entities and C++ model/view export plugin and C++ services export + plugin
    + - Possibility to put hexadecimal values to define an enumeration
    + - New Javascript sample file in directory ./samples/ named q_property.js to show how to add + automatically Q_PROPERTY definition for each property generated by QxEntityEditor
    +
    + +

    Changes in version QxEntityEditor 1.1.9:

    +
    + - New import plugin to connect to MySQL or MariaDB database without having to deal with ODBC + settings
    + - New import plugin to connect to PostgreSQL database without having to deal with ODBC settings
    + - New import plugin to load a QxEntityEditor project from a JSON file
    + - Fix SQLite import process to manage composite keys
    + - Improve Javascript engine to customize export process : new types available by script to manage + files and directories + possibility to get/set environment variables (read custom_script.js file in + ./samples/ directory for script examples)
    + - Improve Javascript engine to customize export process : new events/actions available by script : + PLUGIN_EXPORT_START, PLUGIN_EXPORT_END, AFTER_CLOSING_FILE
    + - New message box displayed if directory doesn't exist when starting an export process (to create it + automatically)
    + - Fix an issue with the "Tag project state" function (project historic) : each tag double the size + of a *.qxee project file (now each tag is optimized)
    + - Fix C++ services export process when C++11 features are enabled in QxOrm.pri configuration + file
    + - Possibility to export a *.qxee project file to a JSON text file (can be useful to store a text + representation in a source control, like Perforce, VSS, CVS, Git, etc...)
    + - Possibility to use an environment variable to define QxOrm library location (using the same syntax + as qmake, for example $$(QXORM_DIR))
    +
    + +

    Changes in version QxEntityEditor 1.1.8:

    +
    + - Improve import plugins : reduce import process time : now, you can import hundred of entities in + few seconds into a QxEntityEditor project
    + - Relationship n-1 : possibility to define a database column name different than the relationship + name
    + - New file qxBlogExec.zip in the ./samples/ directory of QxEntityEditor package : this is a C++/Qt + example project which depends on the qxBlog.qxee generated files
    + - Improve import by ODBC plugin screen : new schema/namespace level in the list of tables/views + treeview
    + - Fix a performance issue to load large diagram with new style to draw relationships : you can now + load quickly a project with hundred of entities
    + - Add new C++11 types to manage relationships, decoration, collection (std::shared_ptr, + std::unordered_map and std::unordered_set) : C++11 features must be enabled in QxOrm.pri config file + to use these classes
    + - Import process more permissive : possibility to import tables without primary key and tables + without column
    +
    + +

    Changes in version QxEntityEditor 1.1.7:

    +
    + - New Navigator window (under project treeview) : useful to navigate over large diagram
    + - New way to draw relationships (orthogonal lines between 2 entities) + display the relationship + type on each side (there is an option to use the old drawing style from previous version)
    + - Possibility to define a background color by namespace : useful to group all entities in the + diagram associated to a same namespace
    + - New feature to customize entities/enumerations/notes colors at several levels
    + - Define items colors at project level (menu Tools >> Project settings >> Colors + tab)
    + - Define items colors at namespace level (right-click on the diagram >> Define colors by + namespace)
    + - Define colors at item level : right-click on an item (entity, enumeration or comment) >> + Define item colors
    + - New action to organize automatically the diagram layout, useful after an import process for + example (menu View >> Organize diagram layout)
    + - Improve the DDL SQL export plugin : new option to export relationships as foreign keys constraints + in database
    + - Support the new compilation option _QX_NO_PRECOMPILED_HEADER of QxOrm library (workaround for a + known bug of recent versions of MinGW on Windows and large precompiled header)
    + - Import database by ODBC plugin : fix the import from MS SQL Server database when tables are not + located in the default schema (dbo)
    + - QxEntityEditor Mac OS X version : fix an issue to load the QxEEPrinter plugin
    + - Export plugin to C++ model/view project : new option to generate models based on the new QxOrm + library class qx::QxModelService<T, S> (models based on services to execute client-server + requests)
    +
    + +

    Changes in version QxEntityEditor 1.1.6:

    +
    + - New javascript engine to customize the C++ and DDL SQL export process writing your own custom + script
    + - Integrated debugger to debug your own custom javascript files (for example, putting a breakpoint + or logging some traces)
    + - New plugin to print the entities diagram as a PNG image file and PDF file
    + - New property parameter UNIQUE (in property params window) used by the DDL SQL generator to create + the database schema
    + - Improve nested models in QxModelView module to be able to use several relationships levels in + QML
    + - Possibility to change the order in the list of properties and list of relationships of an + entity
    + - Change encoding of generated files : now files are UTF-8
    + - Possibility to put a relative path to the QxEntityEditor project file (*.qxee file) in each plugin + location settings
    +
    + +

    Changes in version QxEntityEditor 1.1.5:

    +
    + - New C++ model/view export plugin to manage complex data structure to work with relationships in + QML views and Qt model/view architecture (using QxModelView module of QxOrm library)
    + - Now, with this new export plugin, working on QML with C++ entities has never been so easy !
    + - New function, menu "Tools >> Plugins scripts", to define your own custom scripts + before/after a plugin execution
    + - New command line parameter --log_sql to trace all SQL queries executed by QxEntityEditor : a + QxEntityEditor project file (*.qxee) is just a SQLite database, so it's easy to write your own + script to customize some default behaviour
    + - New option in the C++ export plugin to generate or not the custom directory with all custom files + for each entity
    + - Scrollbar available in the entities viewer
    +
    + +

    Changes in version QxEntityEditor 1.1.4:

    +
    + - Improve import by ODBC plugin to manage relationship, schema and composite key for SQLite, MySQL, + PostgreSQL, Oracle and MS SQL Server databases
    + - New import from SQLite plugin to import SQLite database structure into QxEntityEditor project + without having to create an ODBC DSN connexion
    + - Improve C++ export plugin : add a set of useful methods in generated classes + option to manage + relative path to QxOrm library
    + - New menu to rename a namespace (move all entities to another namespace) and delete a list of + entities by namespace
    + - Fix a bug when executing QxEntityEditor in command line (no GUI)
    + - Add a viewer mode to open a QxEntityEditor project with unlimited entities count without having a + license key (read-only mode)
    +
    + +

    Changes in version QxEntityEditor 1.1.3 (first official release):

    +
    + - New plugin to transfer your data model over network and create quickly client/server applications, + using QxService module
    + - New options to define entity, enum and comment width in entities viewer
    +
    + +

    Changes in version QxEntityEditor 1.1.2 (BETA):

    +
    + - New plugin to generate DDL SQL script (database schema) for SQLite, MySQL, PostgreSQL, Oracle and + MS SQL Server databases
    + - The new DDL SQL plugin manages automatically schema evolution for each project version (ALTER + TABLE, ADD COLUMN, DROP INDEX, etc.)
    + - Add post-it or comments in the entities viewer
    + - Option to display or not property type in the entities viewer
    +
    + +

    Changes in version QxEntityEditor 1.1.1 (BETA):

    +
    + - First BETA version of QxEntityEditor application
    + - Provide a graphic way to manage the data model for QxOrm library
    + - Multi-platform (available for Windows, Linux and Mac OS X) and generate native code for all + environments : desktop (Windows, Linux, Mac OS X), embedded and mobile (Android, iOS, Windows Phone, + Raspberry Pi, etc.)
    + - Based on plugins and provides many ways to import/export the data model
    + - Generate C++ persistent classes automatically (registered into QxOrm context)
    +
    +

    +
    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/doc/qxorm_fr/download_details.php b/doc/qxorm_fr/download_details.php new file mode 100644 index 0000000..de2c9f3 --- /dev/null +++ b/doc/qxorm_fr/download_details.php @@ -0,0 +1,165 @@ + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic editor (data model designer and source code generator) + + + + + + + + + + + + + + + + +
    QxOrm + Windows + Linux + Macintosh + C++
    +
    + + + + + + + + + + + + + +
    AccueilT�l�chargementExemple rapide + Tutoriel (4) +
    + +
    +
    + Manuel (2) +
    + +
    +
    ForumNos clients
    +
    + + + + + + + + + + +
    QxOrm >> Informations sur le t�l�chargement + + + + + + + + + +
    Version courante : QxOrm 1.5.0 - documentation en ligne de la biblioth�que QxOrm - GitHub
    QxEntityEditor 1.2.8
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    + + + + + + + + +
    La biblioth�que QxOrm est disponible avec son code source et peut �tre utilis�e gratuitement en respectant les termes de la licence GNU General Public License (GPL) version 3. +

    + En associant (link) une application avec la biblioth�que QxOrm (directement ou indirectement, statiquement ou dynamiquement, � la compilation ou � l'ex�cution), votre application est soumise aux termes de la licence GPLv3 + qui demande � ce que vous publiez le code source de votre application si et quand vous la distribuez. Distribuer une application signifie la donner ou vendre � des clients, + des soci�t�s, filiales, ou tous autres entit�s l�gales diff�rentes de la votre. D'un autre c�t�, lorsque vous utilisez votre application dans votre organistation uniquement, + alors vous n'avez pas besoin de rendre votre code source public. +

    + Si vous ne souhaitez pas �tre limit� par les termes de la licence GPLv3, vous pouvez acqu�rir la licence QxOrm Proprietary License (QXPL).
    + Les avantages de la licence QxOrm Proprietary License sont : +
      +
    • Le code source de votre application reste priv� ;
    • +
    • Compatible avec toutes les autres licences commerciales et open-source ;
    • +
    • Possibilit� d'utiliser QxOrm sans livrer le code source de la biblioth�que aux utilisateurs ;
    • +
    • Possibilit� de conserver vos propres modifications � la biblioth�que QxOrm sans les d�voiler ;
    • +
    • Possibilit� de cr�er des produits sans mentionner QxOrm aux utilisateurs ;
    • +
    • Protection contre le reverse engineering du produit.
    • +
    +
    qt_ambassador
    + QxOrm library has been accepted into the Qt Ambassador Program +
    + La licence QXPL de la biblioth�que QxOrm est une licence par projet (ou par application), peu importe le nombre de d�veloppeurs travaillant sur le projet, et peu importe le nombre d'instances d�ploy�es de votre logiciel (royalty-free). + Par exemple, si vous d�veloppez 3 applications diff�rentes bas�es sur la biblioth�que QxOrm, alors vous devez acqu�rir 3 licences QXPL (une par projet).
    +
    + Prix d'une licence QxOrm Proprietary License : 400€.
    +
    + Pour toutes questions ou pr�cisions sur les licences, ou bien pour acqu�rir la licence QXPL, vous pouvez nous contacter � l'adresse suivante : ic-east.com.
    +
    + T�l�charger QxOrm 1.5.0 +
    + +
    +
    +

    + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + diff --git a/doc/qxorm_fr/faq.html b/doc/qxorm_fr/faq.html new file mode 100644 index 0000000..44f1926 --- /dev/null +++ b/doc/qxorm_fr/faq.html @@ -0,0 +1,3768 @@ + + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic editor + (data model designer and source code generator) + + + + + + + + + + + + + + + + + + + + + +
    QxOrm + + + + Windows + Linux + Macintosh + C++
    +
    + + + + + + + + + + + + + + + + + + +
    AccueilT�l�chargementExemple rapide + Tutoriel (4) + + + + + + +
    + +
    +
    + Manuel (2) + + + + + + +
    + +
    +
    ForumNos clients
    +
    + + + + + + + + + + + + + + + + + +
    + QxOrm >> Faq + + + + + + + + + + + + + + +
    + Version courante :  + + QxOrm 1.5.0 - documentation en ligne de la biblioth�que QxOrm - GitHub +
    + + + QxEntityEditor 1.2.8 +
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    + + + + + + + + + +
    + + qt_ambassador
    + + QxOrm library has been accepted into the Qt Ambassador + Program + +
    +
    + Qu'est-ce que QxOrm ?

    + + + + + + + + + +
    + QxOrm est une biblioth�que C++ open source de gestion de donn�es (Object Relational Mapping, + ORM).
    + QxOrm est d�velopp� XDL Teamarty, Ing�nieur en d�veloppement logiciel depuis 2003.
    +
    + � partir d'une simple fonction de param�trage (que l'on peut comparer avec un fichier de + mapping XML Hibernate), vous aurez acc�s aux fonctionnalit�s suivantes : +
      +
    • + persistance : communication avec de nombreuses + bases de donn�es (avec support des relations 1-1, 1-n, n-1 et n-n) ; +
    • +
    • + s�rialisation des donn�es (flux binaire, XML + et JSON) ; +
    • +
    • moteur de r�flexion (ou introspection) pour acc�der aux classes, attributs + et invoquer des m�thodes. +
    • +
    + QxOrm est d�pendant des excellentes biblioth�ques boost (compatible � partir de la version 1.38) et Qt (compatible � partir de la version + 4.5.0).
    + La biblioth�que QxOrm a �t� retenue pour faire partie du programme Qt + Ambassador. +

    +
    +
    + Qu'est-ce que QxEntityEditor ?

    + + + + + + + + + +
    + QxEntityEditor est un �diteur graphique pour la biblioth�que QxOrm : + QxEntityEditor permet de g�rer graphiquement le mod�le d'entit�s.
    + QxEntityEditor est multi-plateforme (disponible pour Windows, Linux et Mac OS X) et g�n�re du + code natif pour tous les environnements : bureau (Windows, Linux, Mac OS X), embarqu� et mobile + (Android, iOS, Windows Phone, Raspberry Pi, etc.).
    + Une vid�o de pr�sentation de l'application QxEntityEditor est + disponible.
    +
    + QxEntityEditor est bas� sur un syst�me de plugins et propose diverses fonctionnalit�s pour + importer/exporter le mod�le de donn�es : +
      +
    • g�n�ration automatique du code C++ (classes persistantes enregistr�es dans le contexte QxOrm) ; +
    • +
    • g�n�ration automatique des scripts SQL DDL (sch�ma de base de donn�es) pour les bases SQLite, + MySQL, PostgreSQL, Oracle et MS SQL Server ;
    • +
    • supporte l'�volution du sch�ma de base de donn�es pour chaque version d'un projet (ALTER + TABLE, ADD COLUMN, DROP INDEX, etc.) ;
    • +
    • g�n�ration automatique des classes C++ de services pour transf�rer le mod�le de donn�es sur le + r�seau, en utilisant le module QxService, pour cr�er rapidement des applications client/serveur ;
    • +
    • importation automatique des structures de bases de donn�es existantes (par connexion ODBC) pour + les bases SQLite, MySQL, PostgreSQL, Oracle et MS SQL Server ;
    • +
    • parce que chaque projet est diff�rent, QxEntityEditor propose plusieurs outils pour + personnaliser les fichiers g�n�r�s (notamment un moteur javascript et un d�bogueur int�gr�).
    • +
    + QxEntityEditor +

    + QxEntityEditor est d�velopp� XDL Teamarty, Ing�nieur en d�veloppement logiciel depuis 2003.
    +

    +
    +
    + Comment contacter QxOrm pour indiquer un bug ou poser une question + ?

    + + + + + + + + + +
    + Si vous trouvez un bug ou si vous avez une question concernant le fonctionnement de la biblioth�que + QxOrm, + vous pouvez envoyer un mail � : support@qxorm.com.
    + Un forum (en anglais) d�di� � QxOrm est disponible en cliquant ici.
    + Vous pouvez �galement retrouver la communaut� fran�aise de QxOrm sur le forum de Developpez.com. +
    +

    + Comment installer et compiler QxOrm ?

    + + + + + + + + + +
    + Un tutoriel pour installer un environnement de d�veloppement avec QxOrm + sous Windows est disponible en cliquant ici.
    +
    + QxOrm utilise le processus qmake de la biblioth�que Qt pour g�n�rer les + makefile et compiler le projet.
    + qmake est multiplateforme et fonctionne parfaitement sous Windows, Linux (Unix) et Mac.
    + Pour compiler QxOrm, il suffit d'ex�cuter les commandes suivantes :
    +
    + qmake
    + make debug
    + make release
    +
    + Sous Windows, des fichiers *.vcproj et *.sln sont disponibles pour les �diteurs + Visual C++ 2008, Visual C++ 2010 et Visual C++ 2012.
    + Les fichiers *.pro sont lisibles par l'�diteur Qt Creator, et des plugins existent + permettant de s'interfacer avec de nombreux �diteurs C++.
    + Les fichiers mingw_build_all_debug.bat et mingw_build_all_release.bat pr�sents dans le + dossier ./tools/ permettent de compiler rapidement QxOrm ainsi que tous les tests avec le + compilateur MinGW sous Windows.
    + Les fichiers gcc_build_all_debug.sh et gcc_build_all_release.sh pr�sents dans le dossier + ./tools/ permettent de compiler rapidement QxOrm ainsi que tous les tests avec GCC sous + Linux.
    + Enfin, les fichiers osx_build_all_debug.sh et osx_build_all_release.sh pr�sents dans le + dossier ./tools/ permettent de compiler rapidement QxOrm ainsi que tous les tests sous + Mac (merci � Dominique Billet pour l'�criture des scripts).
    +
    + Remarque : suivant l'environnement de d�veloppement, il peut �tre n�cessaire de modifier le + fichier QxOrm.pri pour param�trer la configuration de la biblioth�que boost :
    +
    + QX_BOOST_INCLUDE_PATH = $$quote(D:/Dvlp/_Libs/Boost/1_42/include)
    + QX_BOOST_LIB_PATH = $$quote(D:/Dvlp/_Libs/Boost/1_42/lib_shared)
    + QX_BOOST_LIB_SERIALIZATION_DEBUG = "boost_serialization-vc90-mt-gd-1_42"
    + QX_BOOST_LIB_SERIALIZATION_RELEASE = "boost_serialization-vc90-mt-1_42"
    +
    +

    + Quelles sont les bases de donn�es prises en compte par QxOrm ?

    + + + + + + + + + +
    + QxOrm utilise le moteur QtSql de Qt bas� sur un syst�me de plugin.
    + Une liste d�taill�e des bases de donn�es support�es est disponible sur le site de Qt en cliquant ici.
    + Le plugin ODBC (QODBC) assure une compatibilit� avec de nombreuses bases de donn�es.
    + Pour des performances optimales, il est conseill� d'utiliser un plugin sp�cifique � une base de + donn�es : +
      +
    • QMYSQL : MySQL ; +
    • +
    • QPSQL : PostgreSQL (versions 7.3 and above) ; +
    • +
    • QOCI : Oracle Call Interface Driver ; +
    • +
    • QSQLITE : SQLite version 3 ; +
    • +
    • QDB2 : IBM DB2 (version 7.1 and above) ; +
    • +
    • QIBASE : Borland InterBase ; +
    • +
    • QTDS : Sybase Adaptive Server. +
    • +
    +
    +
    + Pourquoi QxOrm est d�pendant de deux biblioth�ques : boost et Qt ?

    + + + + + + + + + +
    + QxOrm utilise de nombreuses fonctionnalit�s disponibles dans les excellentes biblioth�ques + boost et Qt.
    + De plus, ces deux biblioth�ques sont utilis�es dans de nombreux projets � la fois professionnels et + open-source.
    + Il existe un grand nombre de forums, de tutoriaux, et toute une communaut� pour r�pondre � toutes les + probl�matiques que vous pourriez rencontrer.
    + L'objectif de QxOrm n'est pas de red�velopper des fonctionnalit�s qui existent d�j� mais de + fournir un outil performant d'acc�s aux bases de donn�es + comme il en existe dans d'autres langages (Java avec Hibernate, .Net avec + NHibernate, Ruby, Python, etc.).

    + + + + + + + + + +
    QtQt : biblioth�que compl�te : IHM + (QtGui), r�seau (QtNetwork), XML (QtXml), base de donn�es + (QtSql), etc.
    + La documentation est excellente et le code C++ �crit � partir + de cette biblioth�que est � la fois performant et simple de + compr�hension.
    + Depuis le rachat par Nokia puis Digia et sa nouvelle licence LGPL, Qt est + sans contexte la biblioth�que phare du moment.
    + QxOrm est compatible avec les principaux objets d�finis par Qt + : QObject, QString, QDate, QTime, QDateTime, QList, QHash, + QSharedPointer, QScopedPointer, etc.
    + Il est conseill� d'installer et d'utiliser la derni�re version + de Qt disponible � l'adresse suivante : http://www.qt.io/
    +

    + + + + + + + + + +
    boostboost : de nombreux modules de la + biblioth�que boost font partie de la nouvelle norme C++.
    + C'est une biblioth�que reconnue pour sa qualit�, son code 'C++ + moderne', sa documentation, sa portabilit�, etc.
    + QxOrm utilise les fonctionnalit�s suivantes de boost : + smart_pointer, serialization, type_traits, + multi_index_container, unordered_container, any, tuple, + foreach, function.
    + Il est conseill� d'installer et d'utiliser la derni�re version + de boost disponible � l'adresse suivante : http://www.boost.org/ +
    +
    +

    + Pourquoi QxOrm n�cessite un en-t�te pr�compil� (precompiled header) pour pouvoir �tre + utilis� ?

    + + + + + + + + + +
    + QxOrm utilise les techniques de m�taprogrammation C++ pour fournir une grande partie de + ses fonctionnalit�s.
    + Vous n'avez pas besoin de savoir utiliser la m�taprogrammation pour travailler avec la biblioth�que + QxOrm.
    + En effet, QxOrm se veut simple d'utilisation et un code C++ �crit avec Qt et QxOrm est facile � lire, + donc facile � d�velopper et � maintenir.

    + Cependant, la m�taprogrammation est couteuse en temps de compilation.
    + En utilisant un fichier precompiled.h, un projet C++ se compilera beaucoup plus vite.
    + Un seul fichier d'en-t�te est n�cessaire pour disposer de l'ensemble des fonctionnalit�s de QxOrm : le + fichier QxOrm.h. +
    +

    + Est-il possible d'acc�l�rer les temps de compilation d'un projet ?

    + + + + + + + + + +
    + Oui, si la serialization des donn�es au format XML n'est pas utilis�e dans le projet, vous + pouvez d�sactiver cette fonctionnalit�.
    + Les temps de compilation seront alors r�duits mais vous n'aurez plus acc�s au namespace + qx::serialization:xml.
    + Pour d�sactiver la serialization XML, il faut ouvrir le fichier de configuration + QxOrm.pri et supprimer (ou mettre en commentaire) l'option de compilation + _QX_ENABLE_BOOST_SERIALIZATION_XML.
    + Une recompilation de la biblioth�que QxOrm est n�cessaire pour prendre en compte cette + modification.
    +
    + Une autre possibilit� est d'utiliser les classes polymorphiques de la biblioth�que boost::serialization (� la place des classes template).
    + Cette fonctionnalit� r�duit les temps de compilation ainsi que la taille de l'�xecutable g�n�r�.
    + En contre-partie, la vitesse d'ex�cution du programme sera r�duite puisqu'une partie du travail + effectu� lors de la compilation devra �tre r�alis� � l'ex�cution de l'application.
    + Pour utiliser cette fonctionnalit� dans QxOrm, vous devez activer l'option de compilation + _QX_ENABLE_BOOST_SERIALIZATION_POLYMORPHIC dans le fichier de configuration + QxOrm.pri.
    + Attention : les fonctions de serialization seront alors accessibles depuis les + namespace suivants : qx::serialization::polymorphic_binary, + qx::serialization::polymorphic_text et qx::serialization::polymorphic_xml.
    + Une recompilation de la biblioth�que QxOrm est n�cessaire pour prendre en compte cette + modification.
    +
    + Enfin, il est �galement possible d'utiliser la macro Q_PROPERTY pour d�clarer les propri�t�s si + la classe h�rite du type QObject.
    + Dans ce cas, il existe deux mani�res diff�rentes pour enregistrer les propri�t�s dans le contexte + QxOrm, dont une qui r�duit sensiblement les temps de compilation du projet.
    + Pour plus d'informations sur cette fonctionnalit�, rendez-vous sur cette + Question-R�ponse de la FAQ.
    +
    + Remarque : il est �galement n�cessaire de s'assurer que toutes les optimisations propos�es par + le compilateur sont activ�es, notamment au niveau de la compilation parall�le sur plusieurs + processeurs : +
      +
    • MSVC++ : utiliser la variable d'environnement SET CL=/MP
    • +
    • GCC et Clang : pr�ciser le nombre de processeurs utilis�s en param�tre du process + make, par exemple pour 8 coeurs : SET MAKE_COMMAND=make -j8 +
    • +
    +
    +

    + Quels sont les diff�rents types de serialization disponibles + ?

    + + + + + + + + + +
    + QxOrm utilise le framework de serialization propos� par la biblioth�que boost.
    + Il existe plusieurs types de serialization disponibles : binaire, XML, JSON, texte, etc.
    + Le fichier de configuration QxOrm.pri permet d'activer et/ou d�sactiver les diff�rents types de + serialization.
    +
    + Chaque type de serialization poss�de ses propres caract�ristiques : +
      +
    • binary : smallest, fastest, non-portable ; +
    • +
    • text : larger, slower, portable ; +
    • +
    • XML : largest, slowest, portable. +
    • +
    + Remarque : le type binary n'est pas portable, ce qui signifie que des donn�es ne peuvent + pas s'�changer entre un syt�me Windows et un syst�me Unix par exemple.
    + Si vous devez faire transiter des donn�es sur un r�seau � travers plusieurs syst�mes d'exploitation, + il faut utiliser le type de serialization text ou XML.
    + QxOrm propose �galement une autre solution : le type de serialization portable_binary.
    + Le type portable_binary poss�de les m�mes caract�ristiques que le type binary et permet + d'�changer des donn�es de mani�re portable.
    + Cependant, ce type de serialization n'est pas propos� officiellement par la biblioth�que boost, + il est donc n�cessaire de tester avant de l'utiliser dans un logiciel en production. +
    +

    + Pourquoi QxOrm fournit un nouveau type de container qx::QxCollection<Key, + Value> ?

    + + + + + + + + + +
    + Il existe de nombreux container dans les biblioth�ques stl, boost et + Qt.
    + Il est donc l�gitime de se poser cette question : � quoi sert qx::QxCollection<Key, + Value> ?
    + qx::QxCollection<Key, + Value> est un nouveau container (bas� sur l'excellente biblioth�que boost::multi_index_container) qui poss�de les fonctionnalit�s suivantes : +
      +
    • conserve l'ordre d'insertion des �l�ments dans la liste ; +
    • +
    • acc�s rapide � un �l�ment par son index : �quivaut � std::vector<T> ou + QList<T> par exemple ; +
    • +
    • acc�s rapide � un �l�ment par une cl� (hash-map) : �quivaut � QHash<Key, + Value> ou boost::unordered_map<Key, Value> par exemple ; +
    • +
    • fonctions de tri sur le type Key et sur le type Value. +
    • +
    + Remarque : + qx::QxCollection<Key, Value> est compatible avec la macro foreach fournie par la + biblioth�que Qt ainsi que par la macro BOOST_FOREACH fournie par la biblioth�que boost.
    + Cependant, chaque �l�ment renvoy� par ces deux macros correspond � un objet de type + std::pair<Key, Value>.
    + Pour obtenir un r�sultat 'plus naturel' et plus lisible, il est conseill� d'utiliser la macro + _foreach : cette macro utilise BOOST_FOREACH pour tous les container sauf pour + qx::QxCollection<Key, Value>.
    + Dans ce cas, l'�l�ment renvoy� correspond au type Value (voir par la suite l'exemple + d'utilisation).
    + La macro _foreach est donc compatible avec tous les container (stl, Qt, + boost, etc.) puisqu'elle utilise la macro BOOST_FOREACH.

    + Autre Remarque : + qx::QxCollection<Key, Value> est particuli�rement adapt� pour recevoir des donn�es issues + d'une base de donn�es.
    + En effet, ces donn�es peuvent �tre tri�es (en utilisant ORDER BY dans une requ�te SQL par + exemple), il est donc important de conserver l'ordre d'insertion des �l�ments dans la liste.
    + De plus, chaque donn�e issue d'une base de donn�es poss�de un identifiant unique. Il est donc + int�ressant de pouvoir acc�der � un �l�ment en fonction de cet identifiant unique de mani�re + extr�mement rapide (hash-map).

    + Exemple d'utilisation de la collection qx::QxCollection<Key, Value> :
    +
    + + + + + + + +
    +
    /* d�finition d'une classe drug avec 3 propri�t�s : 'code', 'name', 'description' */
    +class drug { public: QString code; QString name; QString desc; };
    +
    +/* pointeur intelligent associ� � la classe drug */
    +typedef boost::shared_ptr<drug> drug_ptr;
    +
    +/* collection de drugs (acc�s rapide � un �l�ment de la collection par la propri�t� 'code') */
    +qx::QxCollection<QString, drug_ptr> lstDrugs;
    +
    +/* cr�ation de 3 nouveaux drugs */
    +drug_ptr d1; d1.reset(new drug()); d1->code = "code1"; d1->name = "name1"; d1->desc = "desc1";
    +drug_ptr d2; d2.reset(new drug()); d2->code = "code2"; d2->name = "name2"; d2->desc = "desc2";
    +drug_ptr d3; d3.reset(new drug()); d3->code = "code3"; d3->name = "name3"; d3->desc = "desc3";
    +
    +/* insertion des 3 drugs dans la collection */
    +lstDrugs.insert(d1->code, d1);
    +lstDrugs.insert(d2->code, d2);
    +lstDrugs.insert(d3->code, d3);
    +
    +/* parcours la collection en utilisant le mot-cl� '_foreach' */
    +_foreach(drug_ptr p, lstDrugs)
    +{ qDebug() << qPrintable(p->name) << " " << qPrintable(p->desc); }
    +
    +/* parcours la collection en utilisant une boucle 'for' */
    +for (long l = 0; l < lstDrugs.count(); ++l)
    +{
    +   drug_ptr p = lstDrugs.getByIndex(l);
    +   QString code = lstDrugs.getKeyByIndex(l);
    +   qDebug() << qPrintable(p->name) << " " << qPrintable(p->desc);
    +}
    +
    +/* parcours la collection en utilisant le style Java avec 'QxCollectionIterator' */
    +qx::QxCollectionIterator<QString, drug_ptr> itr(lstDrugs);
    +while (itr.next())
    +{
    +   QString code = itr.key();
    +   qDebug() << qPrintable(itr.value()->name) << " " << qPrintable(itr.value()->desc);
    +}
    +
    +/* effectue un tri croissant par cl� (propri�t� 'code') et d�croissant par valeur */
    +lstDrugs.sortByKey(true);
    +lstDrugs.sortByValue(false);
    +
    +/* acc�s rapide � un drug par son 'code' */
    +drug_ptr p = lstDrugs.getByKey("code2");
    +
    +/* acc�s rapide � un drug par son index (position) dans la collection */
    +drug_ptr p = lstDrugs.getByIndex(2);
    +
    +/* teste si un drug existe dans la collection et si la liste est vide */
    +bool bExist = lstDrugs.exist("code3");
    +bool bEmpty = lstDrugs.empty();
    +
    +/* supprime de la collection le 2�me �l�ment */
    +lstDrugs.removeByIndex(2);
    +
    +/* supprime de la collection l'�l�ment avec le code 'code3' */
    +lstDrugs.removeByKey("code3");
    +
    +/* efface tous les �l�ments de la collection */
    +lstDrugs.clear();
    +
    +
    +

    + Pourquoi QxOrm fournit un nouveau type de pointeur intelligent + qx::dao::ptr<T> ?

    + + + + + + + + + +
    + QxOrm est compatible avec les pointeurs intelligents des biblioth�ques boost et + Qt.
    + Le pointeur intelligent d�velopp� par QxOrm est bas� sur QSharedPointer et apporte de + nouvelles fonctionnalit�s s'il est utilis� avec les fonctions 'qx::dao::...'.
    + qx::dao::ptr<T> + conserve automatiquement les valeurs issues de la base de donn�es.
    + Il est ainsi possible de v�rifier � tout moment si une instance d'objet a subi des modifications gr�ce + � la m�thode 'isDirty()' : cette m�thode peut renvoyer la liste de toutes les propri�t�s ayant + �t� modifi�es.
    + qx::dao::ptr<T> peut �galement �tre utilis� par la fonction + 'qx::dao::update_optimized()' pour mettre � jour en base de donn�es uniquement les champs + modifi�s.
    + qx::dao::ptr<T> peut �tre utilis� avec un objet simple ou bien avec la plupart des + containers : stl, boost, Qt et qx::QxCollection<Key, Value>.
    +
    + Exemple d'utilisation du pointeur intelligent qx::dao::ptr<T> :
    +
    + + + + + + + +
    +
    // exemple d'utilisation de la m�thode 'isDirty()'
    +qx::dao::ptr<blog> blog_isdirty = qx::dao::ptr<blog>(new blog());
    +blog_isdirty->m_id = blog_1->m_id;
    +daoError = qx::dao::fetch_by_id(blog_isdirty);
    +qAssert(! daoError.isValid() && ! blog_isdirty.isDirty());
    +
    +blog_isdirty->m_text = "blog property 'text' modified => blog is dirty !!!";
    +QStringList lstDiff; bool bDirty = blog_isdirty.isDirty(lstDiff);
    +qAssert(bDirty && (lstDiff.count() == 1) && (lstDiff.at(0) == "blog_text"));
    +if (bDirty) { qDebug("[QxOrm] test dirty 1 : blog is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
    +
    +// met � jour uniquement la propri�t� 'm_text' de l'instance 'blog_isdirty'
    +daoError = qx::dao::update_optimized(blog_isdirty);
    +qAssert(! daoError.isValid() && ! blog_isdirty.isDirty());
    +qx::dump(blog_isdirty);
    +
    +// exemple d'utilisation de la m�thode 'isDirty()' avec une liste d'objets
    +typedef qx::dao::ptr< QList<author_ptr> > type_lst_author_test_is_dirty;
    +
    +type_lst_author_test_is_dirty container_isdirty = type_lst_author_test_is_dirty(new QList<author_ptr>());
    +daoError = qx::dao::fetch_all(container_isdirty);
    +qAssert(! daoError.isValid() && ! container_isdirty.isDirty() && (container_isdirty->count() == 3));
    +
    +author_ptr author_ptr_dirty = container_isdirty->at(1);
    +author_ptr_dirty->m_name = "author name modified at index 1 => container is dirty !!!";
    +bDirty = container_isdirty.isDirty(lstDiff);
    +qAssert(bDirty && (lstDiff.count() == 1));
    +if (bDirty) { qDebug("[QxOrm] test dirty 2 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
    +
    +author_ptr_dirty = container_isdirty->at(2);
    +author_ptr_dirty->m_birthdate = QDate(1998, 03, 06);
    +bDirty = container_isdirty.isDirty(lstDiff);
    +qAssert(bDirty && (lstDiff.count() == 2));
    +if (bDirty) { qDebug("[QxOrm] test dirty 3 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
    +
    +// met � jour la propri�t� 'm_name' en position 1, la propri�t� 'm_birthdate' en position 2 et ne change rien en position 0
    +daoError = qx::dao::update_optimized(container_isdirty);
    +qAssert(! daoError.isValid() && ! container_isdirty.isDirty());
    +qx::dump(container_isdirty);
    +
    +// r�cup�re uniquement la propri�t� 'm_dt_creation' du blog
    +QStringList lstColumns = QStringList() << "date_creation";
    +list_blog lst_blog_with_only_date_creation;
    +daoError = qx::dao::fetch_all(lst_blog_with_only_date_creation, NULL, lstColumns);
    +qAssert(! daoError.isValid() && (lst_blog_with_only_date_creation.size() > 0));
    +
    +if ((lst_blog_with_only_date_creation.size() > 0) && (lst_blog_with_only_date_creation[0] != NULL))
    +{ qAssert(lst_blog_with_only_date_creation[0]->m_text.isEmpty()); }
    +
    +qx::dump(lst_blog_with_only_date_creation);
    +
    +
    +

    + Faut-il utiliser QString ou std::string ?

    + + + + + + + + + +
    + QxOrm conseille d'utiliser la classe QString pour la gestion des cha�nes de caract�res.
    + M�me si boost fournit de nombreuses fonctionnalit�s avec son module boost::string_algo, + la classe QString est plus simple � utiliser et surtout prend en charge automatiquement les + diff�rents formats : Ascii, Utf8, Utf16, etc.
    + Cependant, QxOrm est compatible avec std::string et std::wstring si vous pr�f�rez + utiliser ce type de cha�ne de caract�res. +
    +

    + Faut-il utiliser les pointeurs intelligents smart-pointer ?

    + + + + + + + + + +
    + QxOrm conseille fortement d'utiliser les pointeurs intelligents de boost ou + Qt.
    + Le langage C++ ne poss�de pas de Garbage Collector comme Java ou C# par + exemple.
    + L'utilisation des smart-pointer simplifie �norm�ment la gestion de la m�moire en C++.
    + L'id�al dans un programme C++ est de n'avoir aucun appel � delete ou delete[].
    + De plus, les smart-pointer font partie de la nouvelle norme C++ : C++1x.
    + Il est donc essentiel aujourd'hui de conna�tre les classes suivantes : + +
    +

    + La cl� primaire est de type long par d�faut. Est-il possible d'utiliser une cl� + de type QString ou autre ?

    + + + + + + + + + +
    + Par d�faut, lorsqu'un mapping d'une classe C++ est �crit avec la m�thode void + qx::register_class<T>, l'identifiant associ� � la classe est de type long (cl� + primaire avec auto-incr�mentation dans la base de donn�es).
    +
    + Il est possible de d�finir un identifiant d'un autre type en utilisant la macro + QX_REGISTER_PRIMARY_KEY.
    + Cette macro sp�cialise le template qx::trait::get_primary_key pour associer un type + d'identifiant � une classe C++.
    +
    + Par exemple, pour d�finir un identifiant unique de type QString pour la classe C++ + myClass (mapp�e vers une table de la BDD avec une colonne de type VARCHAR pour cl� + primaire), il suffit d'�crire :
    + QX_REGISTER_PRIMARY_KEY(myClass, QString)
    +
    + Voici un exemple d'utilisation de la macro QX_REGISTER_PRIMARY_KEY avec une classe + author poss�dant un identifiant de type QString :
    +
    + + + + + + + +
    +
    #ifndef _QX_BLOG_AUTHOR_H_
    +#define _QX_BLOG_AUTHOR_H_
    + 
    +class author
    +{
    +public:
    +// -- propri�t�s
    +   QString  m_id;
    +   QString  m_name;
    +// -- constructeur, destructeur virtuel
    +   author() { ; }
    +   virtual ~author() { ; }
    +};
    +
    +QX_REGISTER_PRIMARY_KEY(author, QString)
    +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0)
    +
    +#endif // _QX_BLOG_AUTHOR_H_
    +
    +
    +

    + Comment d�finir une cl� primaire sur plusieurs colonnes (composite key) + ?

    + + + + + + + + + +
    + QxOrm supporte la notion de 'multi-columns primary key'.
    + L'identifiant de la classe doit �tre du type suivant : +
      +
    • QPair ou std::pair pour d�finir deux colonnes ; +
    • +
    • boost::tuple pour d�finir de deux � neuf colonnes. +
    • +
    + Il est n�cessaire d'utiliser la macro QX_REGISTER_PRIMARY_KEY() pour sp�cialiser le + template et ainsi d�finir le type d'identifiant sur plusieurs colonnes.
    + La liste des noms des colonnes doit �tre de la forme suivante : + 'column1|column2|column3|etc.'.
    +
    + Exemple d'utilisation avec la classe 'author' du projet 'qxBlogCompositeKey', + cette classe poss�de un identifiant sur trois colonnes :
    +
    + + + + + + + +
    +
    #ifndef _QX_BLOG_AUTHOR_H_
    +#define _QX_BLOG_AUTHOR_H_
    +
    +class blog;
    +
    +class QX_BLOG_DLL_EXPORT author
    +{
    +
    +   QX_REGISTER_FRIEND_CLASS(author)
    +
    +public:
    +
    +// -- cl� compos�e (cl� primaire d�finie sur plusieurs colonnes dans la base de donn�es)
    +   typedef boost::tuple<QString, long, QString> type_composite_key;
    +   static QString str_composite_key() { return "author_id_0|author_id_1|author_id_2"; }
    +
    +// -- typedef
    +   typedef boost::shared_ptr<blog> blog_ptr;
    +   typedef std::vector<blog_ptr> list_blog;
    +
    +// -- enum
    +   enum enum_sex { male, female, unknown };
    +
    +// -- propri�t�s
    +   type_composite_key   m_id;
    +   QString              m_name;
    +   QDate                m_birthdate;
    +   enum_sex             m_sex;
    +   list_blog            m_blogX;
    +
    +// -- constructeur, destructeur virtuel
    +   author() : m_id("", 0, ""), m_sex(unknown) { ; }
    +   virtual ~author() { ; }
    +
    +// -- m�thodes
    +   int age() const;
    +
    +// -- m�thodes d'acc�s � la cl� compos�e
    +   type_composite_key getId() const    { return m_id; }
    +   QString getId_0() const             { return boost::tuples::get<0>(m_id); }
    +   long getId_1() const                { return boost::tuples::get<1>(m_id); }
    +   QString getId_2() const             { return boost::tuples::get<2>(m_id); }
    +
    +// -- m�thodes de modification de la cl� compos�e
    +   void setId_0(const QString & s)     { boost::tuples::get<0>(m_id) = s; }
    +   void setId_1(long l)                { boost::tuples::get<1>(m_id) = l; }
    +   void setId_2(const QString & s)     { boost::tuples::get<2>(m_id) = s; }
    +
    +};
    +
    +QX_REGISTER_PRIMARY_KEY(author, author::type_composite_key)
    +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0)
    +
    +typedef boost::shared_ptr<author> author_ptr;
    +typedef qx::QxCollection<author::type_composite_key, author_ptr> list_author;
    +
    +#endif // _QX_BLOG_AUTHOR_H_
    +
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +#include "../include/author.h"
    +#include "../include/blog.h"
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_BLOG(author)
    +
    +namespace qx {
    +template <> void register_class(QxClass<author> & t)
    +{
    +   t.id(& author::m_id, author::str_composite_key());
    +
    +   t.data(& author::m_name, "name");
    +   t.data(& author::m_birthdate, "birthdate");
    +   t.data(& author::m_sex, "sex");
    +
    +   t.relationOneToMany(& author::m_blogX, blog::str_composite_key(), author::str_composite_key());
    +
    +   t.fct_0<int>(& author::age, "age");
    +}}
    +
    +int author::age() const
    +{
    +   if (! m_birthdate.isValid()) { return -1; }
    +   return (QDate::currentDate().year() - m_birthdate.year());
    +}
    +
    +
    +
    +

    + Comment enregistrer des membres private ou protected dans le contexte + QxOrm ?

    + + + + + + + + + +
    + Pour enregistrer des membres private ou protected dans le contexte QxOrm (fonction + qx::register_class<T>), il faut d�clarer les friend class n�cessaires.
    + Pour simplifier l'�criture avec les template C++, la biblioth�que QxOrm fournit la macro + suivante : QX_REGISTER_FRIEND_CLASS(myClass).
    + Un exemple d'utilisation se trouve dans le dossier ./test/qxDllSample/dll1/ du package QxOrm + avec la classe CPerson :
    +
    + + + + + + + +
    +
    namespace qx {
    +namespace test {
    +
    +class QX_DLL1_EXPORT CPerson : public QObject
    +{
    +
    +   Q_OBJECT
    +   QX_REGISTER_FRIEND_CLASS(qx::test::CPerson)
    +
    +   // etc...
    +
    +};
    +
    +} // namespace test
    +} // namespace qx
    +
    +
    +
    +

    + Comment activer/d�sactiver le module QxMemLeak pour la d�tection automatique + des fuites m�moires ?

    + + + + + + + + + +
    + Le module QxMemLeak permet une d�tection rapide des fuites + m�moire en mode Debug une fois l'ex�cution du programme termin�e + (avec indication du fichier et de la ligne => style MFC de + Microsoft).
    + Ce module a �t� d�velopp� par Wu Yongwei + et a subi + quelques modifications pour �tre int�gr� dans QxOrm.
    + Si un autre outil est d�j� utilis� (Valgrind par exemple), + cette fonctionnalit� ne doit pas �tre activ�e.
    + Pour activer/d�sactiver le module QxMemLeak, il suffit de modifier la constante + _QX_USE_MEM_LEAK_DETECTION d�finie dans le fichier QxConfig.h.
    + Une recompilation de la biblioth�que QxOrm est n�cessaire pour prendre en compte cette modification. +
    +

    + Comment g�rer la notion d'h�ritage avec la base de donn�es ?

    + + + + + + + + + +
    + On retrouve g�n�ralement dans les diff�rents outils de type ORM trois diff�rentes strat�gies + pour g�rer la notion d'h�ritage avec la base de donn�es : +
      +
    • Single Table Inheritance (une seule table regroupant toutes les + propri�t�s d'une hi�rarchie d'h�ritage de classes) ; +
    • +
    • Class + Table Inheritance (� chaque classe d'une hi�rarchie d'h�ritage est associ�e une table + dans la base de donn�es) ; +
    • +
    • Concrete Table Inheritance (une table par classe concr�te dans la + hi�rarchie d'h�ritage). +
    • +
    + QxOrm utilise par d�faut la strat�gie Concrete Table Inheritance (les autres strat�gies + ne sont pas fonctionnelles � l'heure actuelle).
    + De nombreux tutoriaux et forums sont disponibles sur internet pour plus de d�tails sur cette notion + d'h�ritage.
    + Un exemple d'utilisation avec une classe de base se trouve dans le dossier + ./test/qxDllSample/dll2/ avec la classe BaseClassTrigger. +
    +

    + Comment utiliser les Trigger ?

    + + + + + + + + + +
    + Les Trigger de QxOrm permettent d'effectuer divers traitements avant et/ou apr�s une + insertion, une mise � jour ou bien une suppression dans la base de donn�es.
    + Un exemple d'utilisation se trouve dans le dossier ./test/qxDllSample/dll2/ avec la classe + BaseClassTrigger.
    + Cette classe contient cinq propri�t�s : m_id, m_dateCreation, m_dateModification, + m_userCreation et m_userModification.
    + Ces propri�t�s se mettront � jour automatiquement pour chaque classe h�ritant de + BaseClassTrigger (cf. les classes Foo et Bar du m�me projet).
    + Il est n�cessaire de sp�cialiser le template 'QxDao_Trigger' pour profiter de cette + fonctionnalit�.
    +
    + + + + + + + +
    +
    #ifndef _QX_BASE_CLASS_TRIGGER_H_
    +#define _QX_BASE_CLASS_TRIGGER_H_
    +
    +class QX_DLL2_EXPORT BaseClassTrigger
    +{
    +
    +   QX_REGISTER_FRIEND_CLASS(BaseClassTrigger)
    +
    +protected:
    +
    +   long        m_id;
    +   QDateTime   m_dateCreation;
    +   QDateTime   m_dateModification;
    +   QString     m_userCreation;
    +   QString     m_userModification;
    +
    +public:
    +
    +   BaseClassTrigger() : m_id(0)  { ; }
    +   virtual ~BaseClassTrigger()   { ; }
    +
    +   long getId() const                     { return m_id; }
    +   QDateTime getDateCreation() const      { return m_dateCreation; }
    +   QDateTime getDateModification() const  { return m_dateModification; }
    +   QString getUserCreation() const        { return m_userCreation; }
    +   QString getUserModification() const    { return m_userModification; }
    +
    +   void setId(long l)                              { m_id = l; }
    +   void setDateCreation(const QDateTime & dt)      { m_dateCreation = dt; }
    +   void setDateModification(const QDateTime & dt)  { m_dateModification = dt; }
    +   void setUserCreation(const QString & s)         { m_userCreation = s; }
    +   void setUserModification(const QString & s)     { m_userModification = s; }
    +
    +   void onBeforeInsert(qx::dao::detail::IxDao_Helper * dao);
    +   void onBeforeUpdate(qx::dao::detail::IxDao_Helper * dao);
    +
    +};
    +
    +QX_REGISTER_HPP_QX_DLL2(BaseClassTrigger, qx::trait::no_base_class_defined, 0)
    +
    +namespace qx {
    +namespace dao {
    +namespace detail {
    +
    +template <>
    +struct QxDao_Trigger<BaseClassTrigger>
    +{
    +
    +   static inline void onBeforeInsert(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { if (t) { t->onBeforeInsert(dao); } }
    +   static inline void onBeforeUpdate(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { if (t) { t->onBeforeUpdate(dao); } }
    +   static inline void onBeforeDelete(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onBeforeFetch(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onAfterInsert(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onAfterUpdate(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onAfterDelete(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onAfterFetch(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +
    +};
    +
    +} // namespace detail
    +} // namespace dao
    +} // namespace qx
    +
    +#endif // _QX_BASE_CLASS_TRIGGER_H_
    +
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +#include "../include/BaseClassTrigger.h"
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_DLL2(BaseClassTrigger)
    +
    +namespace qx {
    +template <> void register_class(QxClass<BaseClassTrigger> & t)
    +{
    +   IxDataMember * pData = NULL;
    +
    +   pData = t.id(& BaseClassTrigger::m_id, "id");
    +
    +   pData = t.data(& BaseClassTrigger::m_dateCreation, "date_creation");
    +   pData = t.data(& BaseClassTrigger::m_dateModification, "date_modification");
    +   pData = t.data(& BaseClassTrigger::m_userCreation, "user_creation");
    +   pData = t.data(& BaseClassTrigger::m_userModification, "user_modification");
    +}}
    +
    +void BaseClassTrigger::onBeforeInsert(qx::dao::detail::IxDao_Helper * dao)
    +{
    +   Q_UNUSED(dao);
    +   m_dateCreation = QDateTime::currentDateTime();
    +   m_dateModification = QDateTime::currentDateTime();
    +   m_userCreation = "current_user_1";
    +   m_userModification = "current_user_1";
    +}
    +
    +void BaseClassTrigger::onBeforeUpdate(qx::dao::detail::IxDao_Helper * dao)
    +{
    +   Q_UNUSED(dao);
    +   m_dateModification = QDateTime::currentDateTime();
    +   m_userModification = "current_user_2";
    +}
    +
    +
    +
    +

    + Comment d�clarer une classe abstraite dans le contexte QxOrm ?

    + + + + + + + + + +
    + Une classe abstraite C++ (contenant au moins une m�thode virtuelle pure) ne peut pas �tre mapp�e avec + une table d'une base de donn�es (puisqu'elle ne peut pas �tre instanci�e).
    + Cependant, il peut �tre int�ressant de d�finir une classe abstraite contenant une liste de propri�t�s + utilis�es par plusieurs objets persistants.
    + Un exemple de classe abstraite se trouve dans le dossier ./test/qxDllSample/dll2/ de la + distribution de QxOrm avec la classe BaseClassTrigger.
    + QxOrm propose le m�canisme suivant pour d�finir une classe abstraite dans le contexte QxOrm : +
      +
    • d�clarer la classe avec la m�thode 'void register_class' comme n'importe qu'elle autre + classe ; +
    • +
    • utiliser la macro QX_REGISTER_ABSTRACT_CLASS(className) juste apr�s la d�finition de la + classe. +
    • +
    +
    +

    + Comment d�clarer une classe d�finie dans un espace de nom (namespace) dans le + contexte QxOrm ?

    + + + + + + + + + +
    + Si une classe est d�finie dans un espace de nom (namespace), alors une erreur de compilation se + produit avec l'utilisation des macros : QX_REGISTER_HPP et QX_REGISTER_CPP.
    + Pour �viter ces erreurs de compilation, il est n�cessaire d'utiliser les macros suivantes : + QX_REGISTER_COMPLEX_CLASS_NAME_HPP et QX_REGISTER_COMPLEX_CLASS_NAME_CPP.
    +
    + Vous trouverez un exemple d'utilisation dans le dossier ./test/qxDllSample/dll1/ de la + distribution de QxOrm avec la classe CPerson d�finie dans l'espace de nom qx::test :
    + * QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QX_DLL1(qx::test::CPerson, QObject, 0, + qx_test_CPerson)
    +
    + Les macros QX_REGISTER_COMPLEX_CLASS_NAME... n�cessitent un param�tre suppl�mentaire (dans + l'exemple ci-dessus il s'agit du param�tre qx_test_CPerson) afin de cr�er une variable + globale.
    + Celle-ci est appel�e d�s le lancement de l'application.
    + La construction de cette instance globale d�clare la classe dans le module QxFactory (mod�le de + conception fabrique ou design pattern factory).
    + Un objet C++ ne pouvant pas se nommer avec des caract�res "::", le param�tre suppl�mentaire de + la macro permet de remplacer tous les "::" par des "_".
    +
    +

    + Comment utiliser le m�canisme de suppression logique (soft delete) + ?

    + + + + + + + + + +
    + Une suppression logique permet de ne pas effacer de ligne dans une table d'une base de donn�es + (contrairement � une suppression physique) : une colonne suppl�mentaire est ajout�e � la d�finition de + la table pour indiquer que la ligne est supprim�e ou non.
    + Cette colonne peut contenir soit un bool�en (1 signifie ligne supprim�e, 0 ou vide signifie ligne non + supprim�e), soit la date-heure de suppression de la ligne (si vide, la ligne est consid�r�e comme non + supprim�e).
    + Il est donc � tout moment possible de r�activer une ligne supprim�e en r�initialisant la valeur � vide + dans la table de la base de donn�es.
    +
    + Pour activer le m�canisme de suppression logique avec la biblioth�que QxOrm, il faut utiliser la + classe qx::QxSoftDelete dans la fonction de mapping qx::register_class<T>.
    + Voici un exemple d'utilisation avec une classe Bar contenant deux propri�t�s m_id et + m_desc :
    +
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<Bar> & t)
    +{
    +   t.setSoftDelete(qx::QxSoftDelete("deleted_at"));
    +
    +   t.id(& Bar::m_id, "id");
    +   t.data(& Bar::m_desc, "desc");
    +}}
    +
    +
    + Les requ�tes SQL g�n�r�es automatiquement par la biblioth�que QxOrm vont prendre en compte ce + param�tre de suppression logique pour ajouter les conditions n�cessaires (ne pas r�cup�rer les + �l�ments supprim�s, ne pas supprimer physiquement une ligne, etc.).
    + Par exemple, si vous ex�cutez les lignes suivantes avec la classe Bar :
    +
    + + + + + + + +
    +
    Bar_ptr pBar; pBar.reset(new Bar());
    +pBar->setId(5);
    +QSqlError daoError = qx::dao::delete_by_id(pBar);     qAssert(! daoError.isValid());
    +qx_bool bDaoExist = qx::dao::exist(pBar);             qAssert(! bDaoExist);
    +daoError = qx::dao::delete_all<Bar>();                qAssert(! daoError.isValid());
    +long lBarCount = qx::dao::count<Bar>();               qAssert(lBarCount == 0);
    +daoError = qx::dao::destroy_all<Bar>();               qAssert(! daoError.isValid());
    +
    +
    + Vous obtiendrez les traces suivantes :
    +
    + + + + + + + +
    +
    [QxOrm] sql query (93 ms) : UPDATE Bar SET deleted_at = '20110617115148615' WHERE id = :id
    +[QxOrm] sql query (0 ms) : SELECT Bar.id AS Bar_id_0, Bar.deleted_at FROM Bar WHERE Bar.id = :id AND (Bar.deleted_at IS NULL OR Bar.deleted_at = '')
    +[QxOrm] sql query (78 ms) : UPDATE Bar SET deleted_at = '20110617115148724'
    +[QxOrm] sql query (0 ms) : SELECT COUNT(*) FROM Bar WHERE (Bar.deleted_at IS NULL OR Bar.deleted_at = '')
    +[QxOrm] sql query (110 ms) : DELETE FROM Bar
    +
    +
    + Remarque : pour supprimer physiquement une ligne de la base de donn�es, il faut utiliser les + fonctions : qx::dao::destroy_by_id() et qx::dao::destroy_all().
    +
    + Autre remarque : il peut �tre int�ressant de d�finir au niveau du SGBD un index sur la colonne + deleted_at (ou peu importe le nom que vous donnez) afin d'acc�l�rer l'ex�cution des requ�tes + SQL.
    +
    +

    + Comment utiliser les sessions (classe qx::QxSession) pour simplifier la gestion + des transactions des bases de donn�es (C++ RAII) ?

    + + + + + + + + + +
    + Une transaction est une suite d'op�rations effectu�es comme une seule unit� + logique de travail.
    + Une fois termin�e, la transaction est :
    + * soit valid�e (commit), alors toutes les modifications sont faites dans la base de donn�es + ;
    + * soit annul�e (rollback), alors toutes les modifications ne sont pas enregistr�e.
    +
    + La classe qx::QxSession de la biblioth�que QxOrm permet de g�rer automatiquement les + transactions (validation, annulation) en utilisant le m�canisme C++ RAII :
    +
    + + + + + + + +
    +
    { // Ouverture d'un scope o� une session sera instanci�e
    +
    +  // Cr�ation d'une session : une connection valide � la BDD est assign�e � la session et une transaction est d�marr�e
    +  qx::QxSession session;
    +
    +  // Ex�cution d'une s�rie d'op�rations avec la BDD (en utilisant l'op�rateur += de la classe qx::QxSession et la connection de la session)
    +  session += qx::dao::insert(my_object, session.database());
    +  session += qx::dao::update(my_object, session.database());
    +  session += qx::dao::fetch_by_id(my_object, session.database());
    +  session += qx::dao::delete_by_id(my_object, session.database());
    +
    +  // Si la session n'est pas valide (donc une erreur s'est produite) => affichage de la 1�re erreur de la session
    +  if (! session.isValid()) { qDebug("[QxOrm] session error : '%s'", qPrintable(session.firstError().text())); }
    +
    +} // Fermeture du scope : la session est d�truite (transaction => commit ou rollback automatique)
    +
    +
    + Remarque : une session peut d�clencher une exception de type qx::dao::sql_error + lorsqu'une erreur se produit (par d�faut, aucune exception n'est d�clench�e). Il est possible de + param�trer ce comportement en utilisant :
    + * soit le constructeur de la classe qx::QxSession (pour une session en particulier) ;
    + * soit le param�tre du singleton qx::QxSqlDatabase::getSingleton()->setSessionThrowable(bool b) + (pour toutes les sessions).
    +
    + Autre remarque : il est important de ne pas oublier de passer la connection � la base de + donn�es de la session � chaque fonction qx::dao::xxx (en utilisant la m�thode + session.database()).
    + De plus, il est possible d'initialiser une session avec sa propre connection (provenant d'un pool de + connections par exemple) en utilisant le constructeur de la classe qx::QxSession.
    +
    + La classe qx::QxSession propose �galement des m�thodes de persistance (CRUD), ce qui peut + simplifier l'�criture du code C++ suivant les habitudes de programmation.
    + Voici le m�me exemple en utilisant les m�thodes de la classe qx::QxSession � la place des + fonctions du namespace qx::dao :
    +
    + + + + + + + +
    +
    { // Ouverture d'un scope o� une session sera instanci�e
    +
    +  // Cr�ation d'une session : une connection valide � la BDD est assign�e � la session et une transaction est d�marr�e
    +  qx::QxSession session;
    +
    +  // Ex�cution d'une s�rie d'op�rations avec la BDD
    +  session.insert(my_object);
    +  session.update(my_object);
    +  session.fetchById(my_object);
    +  session.deleteById(my_object);
    +
    +  // Si la session n'est pas valide (donc une erreur s'est produite) => affichage de la 1�re erreur de la session
    +  if (! session.isValid()) { qDebug("[QxOrm] session error : '%s'", qPrintable(session.firstError().text())); }
    +
    +} // Fermeture du scope : la session est d�truite (transaction => commit ou rollback automatique)
    +
    +
    +
    +

    + Comment persister un type dont on ne poss�de pas le code source (classe provenant + d'une biblioth�que tierce par exemple) ?

    + + + + + + + + + +
    + La biblioth�que QxOrm permet de persister n'importe quel type, m�me si ce dernier n'est pas enregistr� + dans le contexte QxOrm par la m�thode qx::register_class<T>().
    +
    + Il est n�cessaire d'�crire les fonctions de s�rialisation de la biblioth�que boost, en utilisant la + m�thode non intrusive (puisque le code source n'est pas disponible ou ne peut pas �tre + modifi�). + Pour plus d'informations sur la s�rialisation des donn�es avec la biblioth�que boost, rendez-vous sur + le tutoriel + de developpez.com.
    +
    + Par exemple, imaginons une classe 'ExtObject3D' provenant d'une biblioth�que tierce et dont le + code source n'est pas disponible ou ne peut pas �tre modifi�. + Voici le code n�cessaire pour pouvoir persister une instance de type 'ExtObject3D' en base de + donn�es :
    +
    + + + + + + + +
    +
    #ifndef _PERSIST_EXTOBJECT3D_H_
    +#define _PERSIST_EXTOBJECT3D_H_
    +
    +#include "ExtObject3D.h"
    +
    +#include <boost/serialization/serialization.hpp>
    +#include <boost/serialization/split_free.hpp>
    +#include <boost/serialization/nvp.hpp>
    + 
    +namespace boost {
    +namespace serialization {
    +
    +template <class Archive>
    +void save(Archive & ar, const ExtObject3D & t, unsigned int version)
    +{
    +   Q_UNUSED(version);
    +   double x(t.getX()), y(t.getY()), z(t.getZ()), angle(t.getAngle());
    +
    +   ar << boost::serialization::make_nvp("x", x);
    +   ar << boost::serialization::make_nvp("y", y);
    +   ar << boost::serialization::make_nvp("z", z);
    +   ar << boost::serialization::make_nvp("angle", angle);
    +}
    +
    +template <class Archive>
    +void load(Archive & ar, ExtObject3D & t, unsigned int version)
    +{
    +   Q_UNUSED(version);
    +   double x(0.0), y(0.0), z(0.0), angle(0.0);
    +
    +   ar >> boost::serialization::make_nvp("x", x);
    +   ar >> boost::serialization::make_nvp("y", y);
    +   ar >> boost::serialization::make_nvp("z", z);
    +   ar >> boost::serialization::make_nvp("angle", angle);
    +
    +   t.setX(x);
    +   t.setY(y);
    +   t.setZ(z);
    +   t.setAngle(angle);
    +}
    +
    +} // namespace serialization
    +} // namespace boost
    + 
    +BOOST_SERIALIZATION_SPLIT_FREE(ExtObject3D)
    +
    +#endif // _PERSIST_EXTOBJECT3D_H_
    +
    +
    + Le code ci-dessus est suffisant pour persister une instance de type 'ExtObject3D' en base de + donn�es : il est ainsi possible d'utiliser une propri�t� de type 'ExtObject3D' dans une classe + persistante enregistr�e dans le contexte QxOrm. + Cette propri�t� peut �tre mapp�e sur une colonne de type TEXT ou VARCHAR en base de + donn�es.
    +
    + Le comportement par d�faut de la biblioth�que QxOrm est le suivant : l'instance est s�rialis�e au + format XML avant d'�tre ins�r�e ou mise � jour en base de donn�es. + Ce comportement par d�faut peut �tre utile par exemple si l'on souhaite enregistrer une collection + d'objets sans vouloir faire de relation (et donc g�rer une autre table dans la base de donn�es). + Par exemple, si l'on utilise une propri�t� de type std::vector<mon_objet> dans une classe + persistante sans relation associ�e, la liste d'�l�ments sera automatiquement enregistr�e au format XML + en base de donn�es.
    +
    + Remarque : ce comportement par d�faut peut �tre facilement modifi� pour un type donn�. + Le moteur QtSql utilise le type QVariant pour faire le lien entre le code C++ et la base + de donn�es. + Le type QVariant peut contenir du texte, des valeurs num�riques, du binaire, etc. + Il peut donc �tre int�ressant de sp�cialiser le comportement par d�faut (s�rialisation XML) si l'on + souhaite stocker des donn�es au format binaire ou bien optimiser les performances (la s�rialisation + XML peut �tre couteuse en temps d'ex�cution). + Il suffit de proposer (en plus des fonctions de s�rialisation boost) les conversions n�cessaires en + QVariant, par exemple avec la classe 'ExtObject3D' :
    +
    + + + + + + + +
    +
    namespace qx {
    +namespace cvt {
    +namespace detail {
    +
    +template <> struct QxConvert_ToVariant< ExtObject3D > {
    +static inline QVariant toVariant(const ExtObject3D & t, const QString & format, int index)
    +{ /* Ici je convertis ExtObject3D en QVariant */ } };
    +
    +template <> struct QxConvert_FromVariant< ExtObject3D > {
    +static inline qx_bool fromVariant(const QVariant & v, ExtObject3D & t, const QString & format, int index)
    +{ /* Ici je convertis QVariant en ExtObject3D */; return qx_bool(true); } };
    +
    +} // namespace detail
    +} // namespace cvt
    +} // namespace qx
    +
    +
    +
    +

    + Comment utiliser le moteur d'introspection (ou r�flexion) de la biblioth�que QxOrm + ?

    + + + + + + + + + +
    + Toute classe enregistr�e dans le contexte QxOrm par la m�thode qx::register_class<T>() + peut �tre utilis�e par le moteur d'introspection (ou r�flexion) de la biblioth�que QxOrm. + Le moteur d'introspection permet d'obtenir de fa�on dynamique (donc pendant l'ex�cution du programme) + des informations propres � un type. + Ces informations correspondent � des m�ta-donn�es et d�crivent de fa�on exhaustive les + caract�ristiques d'une classe (propri�t�s, m�thodes, etc.). + De nombreux langages de programmation (par exemple Java ou C#) int�grent nativement ce m�canisme, ce + n'est pas le cas du C++, c'est pourquoi la biblioth�que QxOrm �mule un moteur d'introspection.
    +
    + Voici la liste des classes disponibles pour acc�der aux m�ta-donn�es : +
      +
    • qx::QxClassX : + singleton permettant de parcourir l'ensemble des classes enregistr�es dans le contexte QxOrm par + la m�thode qx::register_class<T>() ;
    • +
    • qx::IxClass : interface + pour une classe enregistr�e dans le contexte QxOrm ;
    • +
    • qx::IxDataMemberX : liste des propri�t�s associ�es � une classe ;
    • +
    • qx::IxDataMember : + interface pour une propri�t� d'une classe ;
    • +
    • qx::IxFunctionX : + liste des m�thodes associ�es � une classe ;
    • +
    • qx::IxFunction : + interface pour une m�thode d'une classe.
    • +
    + Une instance de type qx::IxClass poss�de la liste des propri�t�s d'une classe + (qx::IxDataMemberX) ainsi que la liste des m�thodes d'une classe (qx::IxFunctionX).
    +
    + Le moteur d'introspection de la biblioth�que QxOrm permet par exemple de : +
      +
    • cr�er dynamiquement une instance en fonction du nom d'une classe sous forme de cha�ne de + caract�res (qx::create()) ;
    • +
    • acc�der/modifier le contenu d'un champ d'un objet de fa�on dynamique en prenant pour param�tres + un objet et le nom du champ qu'on souhaite acc�der/modifier (qx::IxDataMember::getValue() + et qx::IxDataMember::setValue()) ;
    • +
    • invoquer une m�thode de classe de fa�on dynamique, en g�rant bien entendu le passage des + param�tres souhait�s � la m�thode (qx::IxFunction::invoke()) ;
    • +
    • acc�der � la hi�rarchie d'une classe (qx::IxClass::getBaseClass()).
    • +
    + Remarque : le module QxService de la biblioth�que QxOrm (cliquez ici pour acc�der au tutoriel) permettant de cr�er un serveur + d'applications C++ est bas� sur le moteur d'introspection pour appeler dynamiquement les m�thodes de + type service (demande du client) sur le serveur.
    +
    + Voici un exemple d'utilisation du moteur d'introspection de la biblioth�que QxOrm : comment lister + toutes les classes, propri�t�s et m�thodes enregistr�es dans le contexte QxOrm ?
    +
    + + + + + + + +
    +
    QString QxClassX::dumpAllClasses()
    +{
    +   QxClassX::registerAllClasses();
    +   QxCollection<QString, IxClass *> * pAllClasses = QxClassX::getAllClasses();
    +   if (! pAllClasses) { qAssert(false); return ""; }
    +
    +   QString sDump;
    +   long lCount = pAllClasses->count();
    +   qDebug("[QxOrm] start dump all registered classes (%ld)", lCount);
    +   _foreach(IxClass * pClass, (* pAllClasses))
    +   { if (pClass) { sDump += pClass->dumpClass(); } }
    +   qDebug("[QxOrm] %s", "end dump all registered classes");
    +
    +   return sDump;
    +}
    +
    +QString IxClass::dumpClass() const
    +{
    +   QString sDump;
    +   sDump += "-- class '" + m_sKey + "' (name '" + m_sName + "', ";
    +   sDump += "description '" + m_sDescription + "', version '" + QString::number(m_lVersion) + "', ";
    +   sDump += "base class '" + (getBaseClass() ? getBaseClass()->getKey() : "") + "')\n";
    +
    +   long lCount = (m_pDataMemberX ? m_pDataMemberX->count() : 0);
    +   sDump += "\t* list of registered properties (" + QString::number(lCount) + ")\n";
    +   if (m_pDataMemberX)
    +   {
    +      IxDataMember * pId = this->getId();
    +      for (long l = 0; l < lCount; l++)
    +      {
    +         IxDataMember * p = m_pDataMemberX->get(l); if (! p) { continue; }
    +         IxSqlRelation * pRelation = p->getSqlRelation();
    +         QString sInfos = p->getKey() + ((p == pId) ? QString(" (id)") : QString());
    +         sInfos += (pRelation ? (QString(" (") + pRelation->getDescription() + QString(")")) : QString());
    +         sDump += "\t\t" + sInfos + "\n";
    +      }
    +   }
    +
    +   lCount = (m_pFctMemberX ? m_pFctMemberX->count() : 0);
    +   sDump += "\t* list of registered functions (" + QString::number(lCount) + ")\n";
    +   if (m_pFctMemberX)
    +   {
    +      _foreach_if(IxFunction_ptr p, (* m_pFctMemberX), (p))
    +      { QString sKey = p->getKey(); sDump += "\t\t" + sKey + "\n"; }
    +   }
    +
    +   qDebug("%s", qPrintable(sDump));
    +   return sDump;
    +}
    +
    +
    + Si on utilise la m�thode qx::QxClassX::dumpAllClasses() avec le tutoriel qxBlog, voici le r�sultat obtenu :
    +
    + + + + + + + +
    +
    [QxOrm] start dump all registered classes (4)
    +-- class 'author' (name 'author', description '', version '0', base class '')
    +	* list of registered properties (5)
    +		author_id (id)
    +		name
    +		birthdate
    +		sex
    +		list_blog (relation one-to-many)
    +	* list of registered functions (1)
    +		age
    +
    +-- class 'blog' (name 'blog', description '', version '0', base class '')
    +	* list of registered properties (6)
    +		blog_id (id)
    +		blog_text
    +		date_creation
    +		author_id (relation many-to-one)
    +		list_comment (relation one-to-many)
    +		list_category (relation many-to-many)
    +	* list of registered functions (0)
    +
    +-- class 'comment' (name 'comment', description '', version '0', base class '')
    +	* list of registered properties (4)
    +		comment_id (id)
    +		comment_text
    +		date_creation
    +		blog_id (relation many-to-one)
    +	* list of registered functions (0)
    +
    +-- class 'category' (name 'category', description '', version '0', base class '')
    +	* list of registered properties (4)
    +		category_id (id)
    +		name
    +		description
    +		list_blog (relation many-to-many)
    +	* list of registered functions (0)
    +
    +[QxOrm] end dump all registered classes
    +
    +
    + Remarque : il est possible d'ajouter de nouvelles informations au moteur d'introspection en + utilisant la notion de property bag. + En effet, les classes qx::IxClass, qx::IxDataMember et qx::IxFunction poss�dent + chacune une liste d'�l�ments de type QVariant accessibles par cl� de type QString (voir + la classe qx::QxPropertyBag pour plus de d�tails sur cette notion).
    +
    +
    +

    + Comment d�clarer automatiquement les m�ta-propri�t�s de Qt (d�finies par la macro + Q_PROPERTY) dans le contexte QxOrm ?

    + + + + + + + + + +
    + Toute classe h�ritant du type QObject peut d�clarer ses propri�t�s avec la macro Q_PROPERTY : les + propri�t�s deviennent alors des m�ta-propri�t�s. + Ce m�canisme permet au framework Qt de proposer un moteur d'introspection gr�ce au pr�-compilateur + moc. + Les m�ta-propri�t�s peuvent alors �tre utilis�es par exemple par le moteur QML, + QtScript, etc.
    +
    + La biblioth�que QxOrm n�cessite une d�claration de chacune des propri�t�s d'une classe dans la + fonction de mapping void qx::register_class<T>() afin de proposer l'ensemble de ses + fonctionnalit�s (persistance des donn�es, s�rialisation XML, JSON et binaire, etc.). + Il est possible de d�clarer automatiquement dans le contexte QxOrm l'ensemble des m�ta-propri�t�s sans + maintenir une fonction de mapping void qx::register_class<T>() : la macro + QX_REGISTER_ALL_QT_PROPERTIES() utilise le moteur d'introspection de Qt pour parcourir la liste + des m�ta-propri�t�s.
    +
    + Voici un exemple d'utilisation avec la classe TestQtProperty se trouvant dans le dossier + ./test/qxDllSample/dll1/include/ de la distribution QxOrm :
    +
    + + + + + + + +
    +
    #ifndef _QX_TEST_QT_META_PROPERTY_H_
    +#define _QX_TEST_QT_META_PROPERTY_H_
    + 
    +class QX_DLL1_EXPORT TestQtProperty : public QObject
    +{
    +
    +   Q_OBJECT
    +   Q_PROPERTY(int id READ id WRITE setId)
    +   Q_PROPERTY(long number READ number WRITE setNumber)
    +   Q_PROPERTY(QString desc READ desc WRITE setDesc)
    +   Q_PROPERTY(QDateTime birthDate READ birthDate WRITE setBirthDate)
    +   Q_PROPERTY(QVariant photo READ photo WRITE setPhoto)
    +
    +protected:
    +
    +   int         m_id;
    +   long        m_number;
    +   QString     m_desc;
    +   QDateTime   m_birthDate;
    +   QVariant    m_photo;
    +
    +public:
    +
    +   TestQtProperty() : QObject(), m_id(0), m_number(0) { ; }
    +   virtual ~TestQtProperty() { ; }
    +
    +   int id() const                { return m_id; }
    +   long number() const           { return m_number; }
    +   QString desc() const          { return m_desc; }
    +   QDateTime birthDate() const   { return m_birthDate; }
    +   QVariant photo() const        { return m_photo; }
    +
    +   void setId(int i)                         { m_id = i; }
    +   void setNumber(long l)                    { m_number = l; }
    +   void setDesc(const QString & s)           { m_desc = s; }
    +   void setBirthDate(const QDateTime & dt)   { m_birthDate = dt; }
    +   void setPhoto(const QVariant & v)         { m_photo = v; }
    + 
    +};
    +
    +QX_REGISTER_HPP_QX_DLL1(TestQtProperty, QObject, 0)
    +
    +#endif // _QX_TEST_QT_META_PROPERTY_H_
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/TestQtProperty.h"
    +
    +#include <QxOrm_Impl.h>
    + 
    +QX_REGISTER_CPP_QX_DLL1(TestQtProperty)
    +QX_REGISTER_ALL_QT_PROPERTIES(TestQtProperty, "id")
    +
    +
    + Pour ceux qui ne souhaitent pas utiliser la macro QX_REGISTER_ALL_QT_PROPERTIES, il est + possible d'�crire � la place les quatre lignes de code suivantes :
    +
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<TestQtProperty> & t)
    +{ qx::register_all_qt_properties<TestQtProperty>(t, "id"); }
    +} // namespace qx
    +
    +
    + Remarque : le deuxi�me param�tre de la macro QX_REGISTER_ALL_QT_PROPERTIES permet + d'indiquer la propri�t� qui servira de cl� primaire dans la base de donn�es. + Si ce param�tre est vide, cela signifie que la classe ne poss�de pas de cl� primaire ou bien que + celle-ci est d�finie dans une classe de base.
    +
    + Toute propri�t� d�finie avec la macro Q_PROPERTY peut s'enregistrer dans le contexte QxOrm de + deux mani�res diff�rentes :
    + 1- par la m�thode classique : t.data(& MyQObject::my_property, "my_property", 0);
    + 2- ou bien sans mentionner le pointeur vers la donn�e membre de la classe : + t.data("my_property", 0);
    +
    + Peu importe la m�thode d'enregistrement des propri�t�s dans le contexte QxOrm, elles seront + accessibles par la m�me interface qx::IxDataMember et proposent donc les m�mes fonctionnalit�s. + Il est possible d'utiliser les deux m�thodes dans une m�me fonction de mapping void + qx::register_class<T>(). + Chaque m�thode d'enregistrement pr�sente des avantages et inconv�nients.
    +
    + Voici la liste des avantages de la deuxi�me m�thode d'enregistrement des propri�t�s dans le contexte + QxOrm : +
      +
    • temps de compilation du projet beaucoup plus rapide ;
    • +
    • taille de l'ex�cutable g�n�r� plus petite ;
    • +
    • forte int�gration avec le moteur d'introspection du framework Qt ;
    • +
    • pas besoin de maintenir la fonction de mapping en utilisant la macro + QX_REGISTER_ALL_QT_PROPERTIES. +
    • +
    + Voici les inconv�nients par rapport � la m�thode classique d'enregistrement des propri�t�s : +
      +
    • n�cessite un h�ritage de la classe QObject pour pouvoir utiliser la macro + Q_PROPERTY ; +
    • +
    • ex�cution du programme plus lente (utilisation du type QVariant � la place des + template C++) ; +
    • +
    • ne supporte pas la notion de relation entre tables de la base de donn�es (one-to-one, + one-to-many, many-to-one et many-to-many) ; +
    • +
    • pas d'acc�s au pointeur sur la donn�e membre de la classe (conversion n�cessaire au type + QVariant pour acc�der et modifier une valeur). +
    • +
    +
    +

    + Comment construire une requ�te pour interroger la base de donn�es sans �crire de SQL + avec la classe qx::QxSqlQuery ?

    + + + + + + + + + +
    + La classe qx::QxSqlQuery (ou bien son alias qx_query) permet d'interroger la + base de donn�es (trier, filtrer, etc.) de deux mani�res diff�rentes : + + Le principal avantage de la premi�re m�thode (�criture manuelle des requ�tes SQL) est de pouvoir + utiliser certaines optimisations sp�cifiques � chaque base de donn�es.
    + La deuxi�me m�thode (utilisation du code C++ pour g�n�rer la requ�te SQL) permet de mapper + automatiquement les param�tres SQL sans utiliser la fonction qx::QxSqlQuery::bind().
    +
    + Voici un exemple d'utilisation de la classe qx::QxSqlQuery avec �criture manuelle d'une requ�te + SQL :
    +
    + + + + + + + +
    +
    // Construit une requ�te pour r�cup�rer uniquement les 'author' de type 'female'
    +qx::QxSqlQuery query("WHERE author.sex = :sex");
    +query.bind(":sex", author::female);
    +
    +QList<author> list_of_female;
    +QSqlError daoError = qx::dao::fetch_by_query(query, list_of_female);
    +for (long l = 0; l < list_of_female.count(); l++)
    +{ /* traitement avec la collection issue de la base de donn�es */ }
    +
    +
    + La biblioth�que QxOrm supporte trois syntaxes pour l'�criture des param�tres SQL.
    + Le type de syntaxe peut �tre modifi� de fa�on globale � un projet en utilisant la m�thode suivante : + qx::QxSqlDatabase::getSingleton()->setSqlPlaceHolderStyle().
    + Les trois param�tres possibles pour cette m�thode sont : +
      +
    • ph_style_2_point_name : "WHERE author.sex = :sex" (syntaxe par d�faut) ;
    • +
    • ph_style_at_name : "WHERE author.sex = @sex" ;
    • +
    • ph_style_question_mark : "WHERE author.sex = ?".
    • +
    + Voici le m�me exemple en utilisant les m�thodes C++ de la classe qx::QxSqlQuery (ou bien son + alias qx_query) pour g�n�rer la requ�te automatiquement :
    +
    + + + + + + + +
    +
    // Construit une requ�te pour r�cup�rer uniquement les 'author' de type 'female'
    +qx_query query;
    +query.where("author.sex").isEqualTo(author::female);
    +
    +QList<author> list_of_female;
    +QSqlError daoError = qx::dao::fetch_by_query(query, list_of_female);
    +for (long l = 0; l < list_of_female.count(); l++)
    +{ /* traitement avec la collection issue de la base de donn�es */ }
    +
    +
    + Cette utilisation de la classe qx::QxSqlQuery pr�sente l'avantage de ne pas avoir � mapper les + param�tres de la requ�te, tout en restant tr�s proche de l'�criture manuelle d'une requ�te SQL.
    + Les param�tres seront automatiquement inject�s en utilisant la syntaxe d�finie de mani�re globale par + la m�thode : qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle().
    +
    + Voici un exemple pr�sentant diff�rentes m�thodes disponibles avec la classe qx::QxSqlQuery (ou + bien son alias qx_query) :
    +
    + + + + + + + +
    +
    qx_query query;
    +query.where("sex").isEqualTo(author::female)
    +     .and_("age").isGreaterThan(38)
    +     .or_("last_name").isNotEqualTo("Dupont")
    +     .or_("first_name").like("Alfred")
    +     .and_OpenParenthesis("id").isLessThanOrEqualTo(999)
    +     .and_("birth_date").isBetween(date1, date2)
    +     .closeParenthesis()
    +     .or_("id").in(50, 999, 11, 23, 78945)
    +     .and_("is_deleted").isNotNull()
    +     .orderAsc("last_name", "first_name", "sex")
    +     .limit(50, 150);
    +
    +
    + Ce qui produira le code SQL suivant pour les bases de donn�es MySQL, PostgreSQL et + SQLite (pour Oracle et SQLServer, le traitement de la m�thode limit() est + diff�rent) :
    +
    + + + + + + + +
    +
    WHERE sex = :sex_1_0 
    +AND age > :age_3_0 
    +OR last_name <> :last_name_5_0 
    +OR first_name LIKE :first_name_7_0 
    +AND ( id <= :id_10_0 AND birth_date BETWEEN :birth_date_12_0_1 AND :birth_date_12_0_2 ) 
    +OR id IN (:id_15_0_0, :id_15_0_1, :id_15_0_2, :id_15_0_3, :id_15_0_4) 
    +AND is_deleted IS NOT NULL 
    +ORDER BY last_name ASC, first_name ASC, sex ASC 
    +LIMIT :limit_rows_count_19_0 OFFSET :offset_start_row_19_0
    +
    +
    + Voici la liste des fonctions et m�thodes disponibles pour utiliser la classe qx::QxSqlQuery (ou + bien son alias qx_query) :
    +
    + + + + + + + +
    +
    // avec les fonctions du namespace qx::dao
    +qx::dao::count<T>()
    +qx::dao::fetch_by_query<T>()
    +qx::dao::update_by_query<T>()
    +qx::dao::delete_by_query<T>()
    +qx::dao::destroy_by_query<T>()
    +qx::dao::fetch_by_query_with_relation<T>()
    +qx::dao::fetch_by_query_with_all_relation<T>()
    +qx::dao::update_by_query_with_relation<T>()
    +qx::dao::update_by_query_with_all_relation<T>()
    +qx::dao::update_optimized_by_query<T>()
    +
    +// avec la classe qx::QxSession
    +qx::QxSession::count<T>()
    +qx::QxSession::fetchByQuery<T>()
    +qx::QxSession::update<T>()
    +qx::QxSession::deleteByQuery<T>()
    +qx::QxSession::destroyByQuery<T>()
    +
    +// avec la classe qx::QxRepository<T>
    +qx::QxRepository<T>::count()
    +qx::QxRepository<T>::fetchByQuery()
    +qx::QxRepository<T>::update()
    +qx::QxRepository<T>::deleteByQuery()
    +qx::QxRepository<T>::destroyByQuery()
    +
    +
    + Remarque : certaines de ces fonctions ont �galement deux autres param�tres optionnels : +
      +
    • const QStringList & columns : pour indiquer la liste des colonnes � r�cup�rer (par + d�faut, toutes les colonnes sont r�cup�r�es) ;
    • +
    • const QStringList & relation : pour indiquer les jointures (one-to-one, + one-to-many, many-to-one et many-to-many d�finies dans la fonction de mapping + void qx::register_class<T>()) entre les tables de la base de donn�es (par d�faut, + aucune relation). +
    • +
    +
    +

    + Comment utiliser le cache (fonctions du namespace qx::cache) pour stocker tous + types de donn�es ?

    + + + + + + + + + +
    + Le cache propos� par la biblioth�que QxOrm (module QxCache) est thread-safe et permet de stocker facilement + n'importe quel type de donn�es.
    + Les fonctions pour acc�der au cache se trouvent dans le namespace + qx::cache.
    + Le cache permet une optimisation du programme : il est possible par exemple de stocker des �l�ments + issus d'une requ�te effectu�e en base de donn�es.
    +
    + Chaque �l�ment stock� dans le cache est associ� � une cl� de type QString : cette cl� permet de + retrouver rapidement un �l�ment du cache.
    + Si un nouvel �l�ment est stock� dans le cache avec une cl� qui existe d�j�, alors l'ancien �l�ment + associ� � cette cl� est effac� automatiquement du cache.
    +
    + Le cache de la biblioth�que QxOrm ne g�re pas la dur�e de vie des objets : il n'y a aucun + delete effectu� par le cache.
    + C'est pourquoi il est fortement recommand� (mais ce n'est pas une obligation) de privil�gier le + stockage de pointeurs intelligents : par exemple, boost::shared_ptr<T> pour la biblioth�que boost ou bien QSharedPointer<T> pour la biblioth�que Qt.
    +
    + Le cache peut avoir un co�t relatif maximum pour �viter une utilisation de la m�moire trop importante + : chaque �l�ment ins�r� dans le cache peut indiquer un co�t repr�sentant une estimation de sa taille + m�moire (par exemple, le nombre d'�l�ments d'une collection).
    + Lorsque le co�t maximum du cache est atteint, les premiers �l�ments ins�r�s dans le cache sont + supprim�s (en respectant l'ordre d'insertion dans le cache) jusqu'� ce que la limite du cache ne soit + plus d�pass�e.
    +
    + Il est possible d'associer � chaque �l�ment du cache une date-heure d'insertion.
    + Si aucune date-heure n'est renseign�e, alors la date-heure courante est prise en compte.
    + Ce m�canisme permet de v�rifier si un �l�ment stock� dans le cache n�cessite une mise � jour ou + non.
    +
    + Voici un exemple d'utilisation du cache de la biblioth�que QxOrm (fonctions du namespace + qx::cache) :
    +
    + + + + + + + +
    +
    // D�fini le co�t maximum du cache � 500
    +qx::cache::max_cost(500);
    +
    +// R�cup�re une liste de 'author' de la base de donn�es
    +boost::shared_ptr< QList<author> > list_author;
    +QSqlError daoError = qx::dao::fetch_all(list_author);
    +
    +// Ins�re la liste de 'author' dans le cache
    +qx::cache::set("list_author", list_author);
    +
    +// R�cup�re une liste de 'blog' de la base de donn�es
    +QSharedPointer< std::vector<blog> > list_blog;
    +daoError = qx::dao::fetch_all(list_blog);
    +
    +// Ins�re la liste de 'blog' dans le cache (co�t = nombre de 'blog')
    +qx::cache::set("list_blog", list_blog, list_blog.count());
    +
    +// Pointeur vers un objet de type 'comment'
    +comment_ptr my_comment;
    +my_comment.reset(new comment(50));
    +daoError = qx::dao::fetch_by_id(my_comment);
    +
    +// Ins�re le 'comment' dans le cache en pr�cisant une date-heure d'insertion
    +qx::cache::set("comment", my_comment, 1, my_comment->dateModif());
    +
    +// R�cup�re la liste de 'blog' stock�e dans le cache
    +list_blog = qx::cache::get< QSharedPointer< std::vector<blog> > >("list_blog");
    +
    +// R�cup�re la liste de 'blog' sans pr�ciser le type
    +qx_bool bGetOk = qx::cache::get("list_blog", list_blog);
    +
    +// Supprime du cache la liste de 'author'
    +bool bRemoveOk = qx::cache::remove("list_author");
    +
    +// Compte le nombre d'�l�ments du cache
    +long lCount = qx::cache::count();
    +
    +// R�cup�re le co�t actuel des �l�ments stock�s dans le cache
    +long lCurrentCost = qx::cache::current_cost();
    +
    +// V�rifie qu'un �l�ment associ� � la cl� "comment" existe dans le cache
    +bool bExist = qx::cache::exist("comment");
    +
    +// R�cup�re le 'comment' stock� dans le cache avec sa date-heure d'insertion
    +QDateTime dt;
    +bGetOk = qx::cache::get("comment", my_comment, dt);
    +
    +// Vide le cache
    +qx::cache::clear();
    +
    +
    +
    +

    + Comment g�n�rer le sch�ma SQL (cr�ation et mise � jour des tables) en fonction des + classes persistantes C++ d�finies dans le contexte QxOrm ?

    + + + + + + + + + +
    + Il est recommand� d'utiliser l'application QxEntityEditor pour g�rer cette + probl�matique.
    +
    + La biblioth�que QxOrm ne fournit pas de m�canisme pour g�rer automatiquement la cr�ation et mise � + jour des tables dans la base de donn�es.
    + En effet, la fonction qx::dao::create_table<T> doit �tre utilis�e uniquement pour cr�er + des prototypes.
    + Il est fortement recommand� d'utiliser un outil sp�cifique � chaque SGBD pour cr�er et maintenir les + tables de la base de donn�es (par exemple Navicat pour MySql, pgAdmin pour + PostgreSQL, SQLite Manager pour SQLite, etc.).
    + De plus, un outil sp�cifique � chaque SGBD permet d'appliquer certaines optimisations (ajout d'index + par exemple).
    +
    + Cependant, il peut �tre int�ressant pour certaines applications de ne pas avoir � g�rer manuellement + les tables de la base de donn�es.
    + Dans ce cas, il est possible de cr�er une fonction C++ pour parcourir la liste des classes + persistantes enregistr�es dans le contexte QxOrm (en utilisant le moteur d'introspection de la + biblioth�que) et ainsi cr�er un script SQL de g�n�ration et mise � jour des tables de la base de + donn�es.
    +
    + La biblioth�que QxOrm fournit une fonction C++ cr��e uniquement � titre d'exemple : elle peut donc + servir de base de travail pour cr�er sa propre fonction de g�n�ration de script SQL.
    + Cette fonction se trouve dans le fichier ./src/QxRegister/QxClassX.cpp et se nomme QString + qx::QxClassX::dumpSqlSchema().
    + Elle g�n�re un script SQL et le renvoie sous forme de QString : il est possible d'adapter cette + fonction pour g�n�rer un fichier contenant le script SQL ou bien appliquer chaque instruction SQL + directement au SGBD.
    +
    + Voici un exemple d'impl�mentation propos� par dodobibi pour g�rer + une base PostgreSQL : cet exemple g�re les �volutions futures de son application (ajout de + colonnes dans une table existante, ajout d'index sur une colonne existante, etc.).
    + Au lancement de l'application, le num�ro de version est indiqu� de la fa�on suivante :
    +
    + + + + + + + +
    +
    QApplication app(argc, argv);
    +app.setProperty("DomainVersion", 1); // Version increment�e � chaque compilation et diffusion de l'application
    +
    +
    + Une table de la base de donn�es permet de stocker le num�ro de version courant.
    + Une classe persistante C++ est mapp�e sur cette table de la fa�on suivante :
    +
    + + + + + + + +
    +
    #ifndef _DATABASE_VERSION_H_
    +#define _DATABASE_VERSION_H_
    + 
    +class MY_DLL_EXPORT DatabaseVersion
    +{
    +public:
    +  QString name;
    +  long version;
    +};
    +
    +QX_REGISTER_HPP_MY_APP(DatabaseVersion, qx::trait::no_base_class_defined, 0)
    +
    +#endif // _DATABASE_VERSION_H_
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +#include "../include/DatabaseVersion.h"
    +#include <QxOrm_Impl.h>
    + 
    +QX_REGISTER_CPP_MY_APP(DatabaseVersion)
    +
    +namespace qx {
    +template <> void register_class(QxClass<DatabaseVersion> & t)
    +{
    +  t.id(& DatabaseVersion::name, "name");
    +  t.data(& DatabaseVersion::version, "version");
    +}}
    +
    +
    + Avec la classe DatabaseVersion, il est possible de v�rifier si la version de la base de donn�es + est � jour.
    + C'est le r�le de la fonction isDatabaseVersionOld() :
    +
    + + + + + + + +
    +
    bool isDatabaseVersionOld()
    +{
    +  DatabaseVersion dbVersion;
    +  dbVersion.name = "MyAppName";
    +  QSqlError err = qx::dao::fetch_by_id(dbVersion);
    +  if (err.isValid()) { qAssert(false); return false; }
    +  return (dbVersion.version < qApp->property("DomainVersion").toInt());
    +}
    +
    +
    + Si au lancement de l'application, la fonction isDatabaseVersionOld() renvoie true, alors + la mise � jour de la base de donn�es est effectu�e de la fa�on suivante :
    +
    + + + + + + + +
    +
    void updateDatabaseVersion()
    +{
    +  try
    +  {
    +    int domainVersion = qApp->property("DomainVersion").toInt();
    +
    +    // On se connecte avec un utilisateur de la base de donn�es qui a les droits de modifications du sch�ma
    +    QSqlDatabase db = qx::QxSqlDatabase::getSingleton()->getDatabaseCloned();
    +    db.setUserName("MyAdminLogin");
    +    db.setPassword("MyAdminPassword");
    +
    +    // On s'assure que la session d�marre une transaction et l�ve une exception � la moindre erreur
    +    qx::QxSession session(db, true, true);
    +
    +    // On "fetch" la version de la base de donn�es avec un verrou pour �viter les modifications concurrentes !
    +    // Si plusieurs utilisateurs lancent l'application en m�me temps et qu'une mise � jour
    +    // est n�cessaire, le premier fera la mise � jour, et les autres seront en attente
    +    DatabaseVersion dbVersion;
    +    session.fetchByQuery(qx_query("WHERE name='MyAppName' FOR UPDATE"), dbVersion);
    +
    +    // Pour les autres utilisateurs, une fois le verrou lev�, on v�rifie si la mise � jour est toujours n�cessaire
    +    if (dbVersion.version >= domainVersion) { return; }
    +
    +    // On ex�cute chaque instruction SQL avec la variable "query"
    +    QSqlQuery query(db);
    +
    +    // On r�cup�re toutes les classes persistantes C++ enregistr�es dans le contexte QxOrm
    +    qx::QxCollection<QString, qx::IxClass *> * pAllClasses = qx::QxClassX::getAllClasses();
    +    if (! pAllClasses) { qAssert(false); return; }
    +
    +    // on r�cup�re la liste des tables existantes dans la base (fonction de Qt)
    +    QStringList tables = db.tables();
    +
    +    for (long k = 0; k < pAllClasses->count(); k++)
    +    {
    +      qx::IxClass * pClass = pAllClasses->getByIndex(k);
    +      if (! pClass) { continue; }
    +
    +      // Filtre les classes non persistantes
    +      if (pClass->isKindOf("qx::service::IxParameter") || pClass->isKindOf("qx::service::IxService")) { continue; }
    +
    +      // Filtre les classes � jour : si la version de pClass est <= � la version enregistr�e dans la base, la mise � jour n'est pas n�cessaire
    +      if (pClass->getVersion() <= dbVersion.version) { continue; }
    +
    +      // On cr�e la table si elle n'existe pas, et on d�finit son propri�taire
    +      if (! tables.contains(pClass->getName()))
    +      {
    +        query.exec("CREATE TABLE " + pClass->getName() + " ( ) WITH (OIDS = FALSE);"
    +                   "ALTER TABLE " + pClass->getName() + " OWNER TO \"MyAdminLogin\";");
    +        session += query.lastError();
    +      }
    +
    +      // On ajoute les colonnes � la table si elles n'existent pas
    +      qx::IxDataMemberX * pDataMemberX = pClass->getDataMemberX();
    +      for (long l = 0; (pDataMemberX && (l < pDataMemberX->count_WithDaoStrategy())); l++)
    +      {
    +        qx::IxDataMember * p = pDataMemberX->get_WithDaoStrategy(l);
    +        if (! p || (p->getVersion() <= dbVersion.version)) { continue; }
    +
    +        query.exec("ALTER TABLE " + pClass->getName() + " ADD COLUMN " + p->getName() + " " + p->getSqlType() + ";");
    +        session += query.lastError();
    +
    +        if (p->getIsPrimaryKey()) // PRIMARY KEY
    +        {
    +          query.exec("ALTER TABLE " + pClass->getName() + " ADD PRIMARY KEY (" + p->getName() + ");");
    +          session += query.lastError();
    +        }
    +
    +        if (p->getAllPropertyBagKeys().contains("INDEX")) // INDEX
    +        {
    +          query.exec("CREATE INDEX " + pClass->getName() + "_" + p->getName() + "_idx" + 
    +                     " ON " + pClass->getName() + " USING " + p->getPropertyBag("INDEX").toString() + " (" + p->getName() + ");");
    +          session += query.lastError();
    +        }
    +
    +        if (p->getNotNull()) // NOT NULL
    +        {
    +          query.exec("ALTER TABLE " + pClass->getName() + " ALTER COLUMN " + p->getName() + " SET NOT NULL;");
    +          session += query.lastError();
    +        }
    +
    +        if (p->getAutoIncrement()) // AUTO INCREMENT
    +        {
    +          query.exec("CREATE SEQUENCE " + pClass->getName() + "_" + p->getName() + "_seq" + "; "
    +                     "ALTER TABLE " + pClass->getName() + "_" + p->getName() + "_seq" + " OWNER TO \"MyAdminLogin\"; "
    +                     "ALTER TABLE " + pClass->getName() + " ALTER COLUMN " + p->getName() + " " +
    +                     "SET DEFAULT nextval('" + pClass->getName() + "_" + p->getName() + "_seq" + "'::regclass);");
    +          session += query.lastError();
    +        }
    +
    +        if (p->getDescription() != "") // DESCRIPTION
    +        {
    +          // $$ceci est un texte ne n�cessitant pas de caract�res d'�chappement dans postgres grace aux doubles dolars$$
    +          query.exec("COMMENT ON COLUMN " + pClass->getName() + "." + p->getName() + " IS $$" + p->getDescription() + "$$ ;");
    +          session += query.lastError();
    +        }
    +      }
    +    }
    +
    +    // On enregistre la version courante de la base de donn�es
    +    dbVersion.version = domainVersion;
    +    session.save(dbVersion);
    +
    +    // Fin du block "try" : la session est d�truite => commit ou rollback automatique
    +    // De plus, un commit ou rollback sur la transaction l�ve automatiquement le verrou pos� pr�c�demment
    +  }
    +  catch (const qx::dao::sql_error & err)
    +  {
    +    QSqlError sqlError = err.get();
    +    qDebug() << sqlError.databaseText();
    +    qDebug() << sqlError.driverText();
    +    qDebug() << sqlError.number();
    +    qDebug() << sqlError.type();
    +  }
    +}
    +
    +
    + Remarque : le code pr�c�dent (tout comme la fonction qx::QxClassX::dumpSqlSchema()) peut �tre modifi� pour s'adapter aux + besoins sp�cifiques d'une application.
    + Par exemple, il pourrait �tre int�ressant de cr�er par d�faut une seconde table (en plus de la table + DatabaseVersion) pour enregistrer la liste des classes persistantes enregistr�es dans le + contexte QxOrm : ainsi, au lieu d'utiliser la fonction propos�e par Qt "db.tables()", il serait + possible de r�cup�rer toutes les tables mapp�es sur des classes persistantes avec des informations + suppl�mentaires (num�ro de version pour chaque table, nombre de colonnes enregistr�es dans le contexte + QxOrm, description de chaque table, etc.). +
    +
    +

    + Comment associer un type SQL � une classe C++ ?

    + + + + + + + + + +
    + Chaque base de donn�es propose des types SQL diff�rents pour stocker l'information.
    + La biblioth�que QxOrm propose une association par d�faut pour les classes C++ les plus fr�quemment + utilis�es dans un programme.
    + Voici cette association par d�faut :
    +
    + + + + + + + +
    +
    "bool" <-> "SMALLINT"
    +"qx_bool" <-> "SMALLINT"
    +"short" <-> "SMALLINT"
    +"int" <-> "INTEGER"
    +"long" <-> "INTEGER"
    +"long long" <-> "INTEGER"
    +"float" <-> "FLOAT"
    +"double" <-> "FLOAT"
    +"long double" <-> "FLOAT"
    +"unsigned short" <-> "SMALLINT"
    +"unsigned int" <-> "INTEGER"
    +"unsigned long" <-> "INTEGER"
    +"unsigned long long" <-> "INTEGER"
    +"std::string" <-> "TEXT"
    +"std::wstring" <-> "TEXT"
    +"QString" <-> "TEXT"
    +"QVariant" <-> "TEXT"
    +"QUuid" <-> "TEXT"
    +"QDate" <-> "DATE"
    +"QTime" <-> "TIME"
    +"QDateTime" <-> "TIMESTAMP"
    +"QByteArray" <-> "BLOB"
    +"qx::QxDateNeutral" <-> "TEXT"
    +"qx::QxTimeNeutral" <-> "TEXT"
    +"qx::QxDateTimeNeutral" <-> "TEXT"
    +
    +
    + Si le type SQL propos� par d�faut par la biblioth�que QxOrm ne correspond pas � la base de donn�es + utilis�e, il peut facilement �tre modifi� (de mani�re globale � toute l'application) en utilisant la + collection suivante :
    +
    + + + + + + + +
    +
    QHash<QString, QString> * lstSqlType = qx::QxClassX::getAllSqlTypeByClassName();
    +lstSqlType->insert("QString", "VARCHAR(255)");
    +lstSqlType->insert("std::string", "VARCHAR(255)");
    +// etc.
    +
    +
    + Pour modifier le type SQL de mani�re sp�cifique pour une colonne d'une table de la base de donn�es, il + faut d�finir le type SQL dans la fonction de mapping de QxOrm :
    +
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<MyClass> & t)
    +{
    +  //...
    +  IxDataMember * p =  t.data(& MyClass::m_MyProperty, "my_property");
    +  p->setSqlType("VARCHAR(255)");
    +  //...
    +}}
    +
    +
    + Pour les classes non support�es par d�faut par la biblioth�que QxOrm (voir cette + Question-R�ponse de la FAQ : Comment persister un type dont on + ne poss�de pas le code source (classe provenant d'une biblioth�que tierce par exemple) ?), + il est possible d'associer un type SQL par d�faut en utilisant la macro suivante (en dehors de tout + namespace) :
    +
    + + + + + + + +
    +
    QX_REGISTER_TRAIT_GET_SQL_TYPE(MyClass, "my_sql_type")
    +
    +
    +
    +

    + Comment utiliser le module QxValidator pour valider automatiquement les donn�es + ?

    + + + + + + + + + +
    + Le module QxValidator + de la biblioth�que QxOrm permet d'ajouter des contraintes sur les propri�t�s enregistr�es dans + le contexte QxOrm.
    + Ces contraintes sont d�finies dans la m�thode de mapping : void + qx::register_class<T>.
    + Si pour une instance de classe donn�e, au moins une contrainte n'est pas respect�e, alors l'instance + est consid�r�e comme invalide : l'objet ne peut alors pas �tre sauvegard� en base de donn�es + (INSERT ou UPDATE).
    +
    + Il est �galement possible d'utiliser le module QxValidator pour valider les donn�es au niveau + de la couche pr�sentation de l'application : si les donn�es saisies par un utilisateur ne sont pas + valides, un message d'erreur peut �tre signal�, il n'est alors pas n�cessaire d'essayer d'enregistrer + l'instance courante en base de donn�es.
    + Les r�gles de validation n'ont pas besoin d'�tre dupliqu�es : elles peuvent �tre utilis�es aussi bien + par la couche pr�sentation que par la couche d'acc�s aux donn�es de l'application.
    +
    + Voici la description de quelques classes du module QxValidator : + + Le module QxValidator g�re automatiquement la notion d'h�ritage de classe : si des contraintes + sont d�finies au niveau de la classe de base, alors elles seront automatiquement v�rifi�es pour chaque + validation d'une classe d�riv�e.
    +
    + Voici un exemple d'utilisation du module QxValidator avec une classe 'person' :
    +
    + * fichier 'person.h' :
    + + + + + + + +
    +
    #ifndef _CLASS_PERSON_H_
    +#define _CLASS_PERSON_H_
    + 
    +class person
    +{
    +
    +public:
    +
    +   enum sex { male, female, unknown };
    +
    +   long        _id;
    +   QString     _firstName;
    +   QString     _lastName;
    +   QDateTime   _birthDate;
    +   sex         _sex;
    +
    +   person() : _id(0), _sex(unknown) { ; }
    +   person(long id) : _id(id), _sex(unknown) { ; }
    +   virtual ~person() { ; }
    +
    +private:
    +
    +   void isValid(qx::QxInvalidValueX & invalidValues);
    +
    +};
    +
    +QX_REGISTER_HPP_MY_EXE(person, qx::trait::no_base_class_defined, 0)
    +
    +#endif // _CLASS_PERSON_H_
    +
    +
    + * fichier 'person.cpp' :
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/person.h"
    +#include "../include/global_validator.h"
    +
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_MY_EXE(person)
    +
    +namespace qx {
    +template <> void register_class(QxClass<person> & t)
    +{
    +   t.id(& person::_id, "id");
    +
    +   t.data(& person::_firstName, "firstName");
    +   t.data(& person::_lastName, "lastName");
    +   t.data(& person::_birthDate, "birthDate");
    +   t.data(& person::_sex, "sex");
    +
    +   QxValidatorX<person> * pAllValidator = t.getAllValidator();
    +   pAllValidator->add_NotEmpty("firstName");
    +   pAllValidator->add_NotEmpty("lastName", "a person must have a lastname");
    +   pAllValidator->add_CustomValidator(& person::isValid);
    +   pAllValidator->add_CustomValidator_QVariant(& validateFirstName, "firstName");
    +   pAllValidator->add_CustomValidator_DataType<QDateTime>(& validateDateTime, "birthDate");
    +}}
    +
    +void person::isValid(qx::QxInvalidValueX & invalidValues)
    +{
    +   // Cette m�thode est appel�e automatiquement par le module 'QxValidator' :
    +   // - avant d'ins�rer ou mettre � jour une instance de type 'person' par les fonctions du namespace 'qx::dao' ;
    +   // - en utilisant la fonction 'qx::validate()' avec pour param�tre une instance de type 'person'.
    +
    +   // L'enregistrement de la m�thode 'person::isValid()' est effectu� dans la fonction de mapping :
    +   // pAllValidator->add_CustomValidator(& person::isValid);
    +
    +   // Dans cette m�thode, il est possible de v�rifier n'importe quelle valeur de l'instance courante
    +   // Si une propri�t� est non valide, il suffit d'ins�rer un �l�ment dans la collection 'invalidValues'
    +
    +   // Remarque : cette m�thode est d�clar�e 'private' pour forcer l'utilisateur � utiliser la fonction 'qx::validate()'
    +   // Mais ce n'est pas une obligation : cette m�thode peut �tre d�clar�e 'public' ou 'protected'
    +
    +   // Par exemple, si on souhaite v�rifier la propri�t� '_sex' d'une personne :
    +   if ((_sex != male) && (_sex != female))
    +   { invalidValues.insert("le sexe de la personne doit �tre d�fini : masculin ou f�minin"); }
    +}
    +
    +
    + * fichier 'global_validator.h' :
    + + + + + + + +
    +
    // Les fonctions suivantes ('validateFirstName()' et 'validateDateTime()') sont globales (non li�es � une classe)
    +// Elles peuvent ainsi �tre utilis�es par plusieurs classes pour valider une propri�t� (par exemple : valider la saisie d'une adresse IP).
    +// Ces fonctions seront appel�es automatiquement par le module 'QxValidator' :
    +// - avant d'ins�rer ou mettre � jour une instance de classe par les fonctions du namespace 'qx::dao' ;
    +// - en utilisant la fonction 'qx::validate()'.
    + 
    +void validateFirstName(const QVariant & value, const qx::IxValidator * validator, qx::QxInvalidValueX & invalidValues)
    +{
    +   // Ici, on peut tester la valeur d'une propri�t� (convertie en type QVariant)
    +   // Si la valeur est invalide, il suffit d'ins�rer un message � la collection 'invalidValues'
    +
    +   // Par exemple, si la valeur ne doit jamais �tre �gale � "admin" :
    +   if (value.toString() == "admin")
    +   { invalidValues.insert("la valeur ne peut pas �tre �gale � 'admin'"); }
    +}
    +
    +void validateDateTime(const QDateTime & value, const qx::IxValidator * validator, qx::QxInvalidValueX & invalidValues)
    +{
    +   // Ici, on peut tester la valeur d'une propri�t� (en conservant son vrai type, ici il s'agit de tester une date-heure de type 'QDateTime')
    +   // Si la valeur est invalide, il suffit d'ins�rer un message � la collection 'invalidValues'
    +
    +   // Par exemple, si la date-heure doit forc�ment �tre renseign�e :
    +   if (! value.isValid())
    +   { invalidValues.insert("la date-heure doit �tre renseign�e et doit �tre valide"); }
    +}
    +
    +
    + * fichier 'main.cpp' :
    + + + + + + + +
    +
    person personValidate;
    +personValidate._lastName = "admin";
    +qx::QxInvalidValueX invalidValues = qx::validate(personValidate);
    +QString sInvalidValues = invalidValues.text();
    +qDebug("[QxOrm] test 'QxValidator' module :\n%s", qPrintable(sInvalidValues));
    +
    +
    + A l'ex�cution de ce bout de code, l'instance 'personValidate' est non valide : la collection + 'invalidValues' contient quatre �l�ments :
    + - "la valeur de la propri�t� 'firstName' ne peut pas �tre vide" ;
    + - "le sexe de la personne doit �tre d�fini : masculin ou f�minin" ;
    + - "la valeur ne peut pas �tre �gale � 'admin'" ;
    + - "la date-heure doit �tre renseign�e et doit �tre valide".
    +
    + Le module QxValidator fournit plusieurs validateurs pour effectuer des v�rifications basiques : +
      +
    • add_NotNull() : v�rifie que la valeur n'est pas nulle ;
    • +
    • add_NotEmpty() : v�rifie que la cha�ne de caract�res n'est pas vide ;
    • +
    • add_MinValue() : v�rifie que la valeur num�rique n'est pas inf�rieure au param�tre ;
    • +
    • add_MaxValue() : v�rifie que la valeur num�rique n'est pas sup�rieure au param�tre ;
    • +
    • add_Range() : v�rifie que la valeur num�rique est comprise entre les deux param�tres ; +
    • +
    • add_MinDecimal() : v�rifie que la valeur d�cimale n'est pas inf�rieure au param�tre ; +
    • +
    • add_MaxDecimal() : v�rifie que la valeur d�cimale n'est pas sup�rieure au param�tre ; +
    • +
    • add_RangeDecimal() : v�rifie que la valeur d�cimale est comprise entre les deux + param�tres ;
    • +
    • add_MinLength() : v�rifie que la cha�ne de caract�res a une taille minimale ;
    • +
    • add_MaxLength() : v�rifie que la cha�ne de caract�res ne d�passe pas un certain nombre de + caract�res ;
    • +
    • add_Size() : v�rifie que la taille de la cha�ne de caract�res est comprise entre les deux + param�tres ;
    • +
    • add_DatePast() : v�rifie que la date-heure est dans le pass� ;
    • +
    • add_DateFuture() : v�rifie que la date-heure est dans le futur ;
    • +
    • add_RegExp() : v�rifie que la cha�ne de caract�res est compatible avec l'expression + r�guli�re pass�e en param�tre ;
    • +
    • add_EMail() : v�rifie que la cha�ne de caract�res correspond � un e-mail.
    • +
    + Comme dans l'exemple de la classe 'person', il est possible de d�finir �galement des + validateurs personnalis�s : ce sont des fonctions ou m�thodes de classe qui seront appel�es + automatiquement par le module QxValidator pour valider une propri�t� ou une instance de + classe.
    + Il existe trois types de validateurs personnalis�s : +
      +
    • add_CustomValidator() : m�thode de classe, la signature de la m�thode doit �tre "void + my_class::my_method(qx::QxInvalidValueX &)" ;
    • +
    • add_CustomValidator_QVariant() : fonction globale avec type QVariant (propri�t� + convertie en QVariant), la signature de la fonction doit �tre "void my_validator(const + QVariant &, const qx::IxValidator *, qx::QxInvalidValueX &)" ;
    • +
    • add_CustomValidator_DataType() : fonction globale avec le type r�el de la propri�t�, la + signature de la fonction doit �tre "void my_validator(const T &, const qx::IxValidator *, + qx::QxInvalidValueX &)" ;
    • +
    + Remarque : � chaque validateur peut �tre associ� un groupe (param�tre optionnel pour chaque + m�thode add_XXX() de la classe qx::IxValidatorX).
    + Il est ainsi possible de cr�er des groupes de validation suivant le contexte d'ex�cution : par + exemple, valider la saisie d'une personne sur une IHM A ne n�cessite peut-�tre pas les m�mes + v�rifications que valider une personne sur une IHM B.
    + Pour ex�cuter la validation d'une instance pour un groupe donn� (par exemple "myGroup"), il + faut appeler la fonction suivante : "qx::QxInvalidValueX invalidValues = + qx::validate(personValidate, "myGroup");".
    +
    + Autre remarque : le module QxValidator d�finit des messages par d�faut lorsqu'une + contrainte n'est pas v�rifi�e.
    + Il est possible de red�finir ces messages par d�faut en modifiant la collection suivante : "QHash + * lstMessage = QxClassX::getAllValidatorMessage(); + ".
    + Par exemple : "lstMessage->insert("min_value", "la valeur '%NAME%' doit �tre inf�rieure ou �gale � + '%CONSTRAINT%'");".
    + Les champs %NAME% et %CONSTRAINT% seront automatiquement remplac�s par les valeurs + correspondantes.
    + Pour modifier le message pour un validateur donn� (et non de mani�re globale), il faut utiliser le + param�tre optionnel disponible pour les m�thodes add_XXX() de la classe + qx::IxValidatorX.
    +
    +

    + Comment utiliser l'interface qx::IxPersistable ?

    + + + + + + + + + +
    + L'interface qx::IxPersistable (ou classe abstraite) dispose uniquement de m�thodes + virtuelles pures.
    + Elle permet d'avoir une classe de base commune pour appeler les fonctions de persistance sans + conna�tre le type r�el de l'instance courante (notion de polymorphisme).
    + La biblioth�que QxOrm n'impose pas de travailler avec une classe de base pour enregistrer un type + persistant dans le contexte QxOrm, cependant il est parfois utile de disposer d'une interface afin + d'�crire des algorithmes g�n�riques.
    +
    + La classe qx::IxPersistable met � disposition les m�thodes virtuelles suivantes (pour plus + d'informations sur ces m�thodes, rendez-vous sur la documentation en ligne de la biblioth�que QxOrm) :
    +
    +
    +
    virtual long qxCount(const qx::QxSqlQuery & query = qx::QxSqlQuery(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxFetchById(const QVariant & id = QVariant(), const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxFetchAll(qx::IxCollection & list, const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxFetchByQuery(const qx::QxSqlQuery & query, qx::IxCollection & list, const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxInsert(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxUpdate(const qx::QxSqlQuery & query = qx::QxSqlQuery(), const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxSave(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDeleteById(const QVariant & id = QVariant(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDeleteAll(QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDeleteByQuery(const qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDestroyById(const QVariant & id = QVariant(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDestroyAll(QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDestroyByQuery(const qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL);
    +virtual qx_bool qxExist(const QVariant & id = QVariant(), QSqlDatabase * pDatabase = NULL);
    +virtual qx::QxInvalidValueX qxValidate(const QStringList & groups = QStringList());
    +virtual qx::IxPersistableCollection_ptr qxNewPersistableCollection() const;
    +virtual qx::IxClass * qxClass() const;
    +
    +
    + Par exemple, � partir d'une liste de pointeurs de type qx::IxPersistable, il est possible + d'enregistrer les �l�ments dans plusieurs tables diff�rentes de la base de donn�es de la fa�on + suivante :
    +
    + + + + + + + +
    +
    QList<qx::IxPersistable *> lst = ...;
    +foreach(qx::IxPersistable * p, lst)
    +{
    +   QSqlError daoError = p->qxSave();
    +   if (daoError.isValid()) { /* an error occured */ }
    +   // etc...
    +}
    +
    +
    + Pour impl�menter l'interface qx::IxPersistable, il faut : +
      +
    • faire h�riter la classe persistante du type qx::IxPersistable ;
    • +
    • dans la d�finition de la classe (myClass.h par exemple), ajouter la macro + QX_PERSISTABLE_HPP(myClass) ; +
    • +
    • dans l'impl�mentation de la classe (myClass.cpp par exemple), ajouter la macro + QX_PERSISTABLE_CPP(myClass). +
    • +
    + Par exemple, impl�menter l'interface qx::IxPersistable pour la classe author du tutoriel qxBlog revient � �crire (les + modifications par rapport au code du tutoriel apparaissent en gras) :
    +
    + + + + + + + +
    +
    #ifndef _QX_BLOG_AUTHOR_H_
    +#define _QX_BLOG_AUTHOR_H_
    +
    +class blog;
    +
    +class QX_BLOG_DLL_EXPORT author : public qx::IxPersistable
    +{
    +   QX_PERSISTABLE_HPP(author)
    +public:
    +// -- typedef
    +   typedef boost::shared_ptr<blog> blog_ptr;
    +   typedef std::vector<blog_ptr> list_blog;
    +// -- enum
    +   enum enum_sex { male, female, unknown };
    +// -- propri�t�s
    +   QString     m_id;
    +   QString     m_name;
    +   QDate       m_birthdate;
    +   enum_sex    m_sex;
    +   list_blog   m_blogX;
    +// -- constructeur, destructeur virtuel
    +   author() : m_id(0), m_sex(unknown) { ; }
    +   virtual ~author() { ; }
    +// -- m�thodes
    +   int age() const;
    +};
    +
    +QX_REGISTER_PRIMARY_KEY(author, QString)
    +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0)
    +
    +typedef boost::shared_ptr<author> author_ptr;
    +typedef qx::QxCollection<QString, author_ptr> list_author;
    +
    +#endif // _QX_BLOG_AUTHOR_H_
    +
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/author.h"
    +#include "../include/blog.h"
    +
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_BLOG(author)
    +QX_PERSISTABLE_CPP(author)
    +
    +namespace qx {
    +template <> void register_class(QxClass<author> & t)
    +{
    +   t.id(& author::m_id, "author_id");
    +
    +   t.data(& author::m_name, "name");
    +   t.data(& author::m_birthdate, "birthdate");
    +   t.data(& author::m_sex, "sex");
    +
    +   t.relationOneToMany(& author::m_blogX, "list_blog", "author_id");
    +
    +   t.fct_0<int>(& author::age, "age");
    +}}
    +
    +int author::age() const
    +{
    +   if (! m_birthdate.isValid()) { return -1; }
    +   return (QDate::currentDate().year() - m_birthdate.year());
    +}
    +
    +
    +
    + Remarque : le projet de test ./test/qxDllSample/dll1/ met � disposition une sorte de + 'super classe de base' : la classe qx::QxPersistable impl�mente l'interface qx::IxPersistable + et h�rite de QObject.
    + Le m�canisme SIGNAL-SLOT de Qt peut donc �tre utilis� avec cette classe, ce qui peut �tre + int�ressant par exemple pour la notion de d�clencheurs (ou trigger).
    + La classe qx::QxPersistable met �galement � disposition des m�thodes virtuelles qu'il est + possible de surcharger pour g�rer notamment la notion de validation des donn�es avec le module QxValidator.
    + La classe qx::QxPersistable ne fait pas partie de la distribution de QxOrm, mais il est + possible de la copier-coller dans un projet afin de profiter de ses fonctionnalit�s : + +
    +

    + Comment utiliser le moteur de relations pour r�cup�rer des donn�es associ�es � + plusieurs tables ?

    + + + + + + + + + +
    + La biblioth�que QxOrm supporte quatre types de relations pour lier les classes C++ enregistr�es dans + le contexte QxOrm : one-to-one, one-to-many, many-to-one et + many-to-many.
    + Pour plus de d�tails sur la d�finition de ces relations, il est conseill� de lire le tutoriel qxBlog.
    + Nous allons d�tailler dans cette Q&R les diff�rentes m�thodes de r�cup�ration des donn�es (module QxDao, fonctions du namespace qx::dao) : + + Le premier param�tre des fonctions fetch_by_id_with_relation, fetch_all_with_relation et fetch_by_query_with_relation correspond � la liste des relations � requ�ter.
    + Cette liste de relations peut contenir les �l�ments suivants : +
      +
    • identifiant d'une relation : chaque relation poss�de une cl� d�finie au niveau de la fonction de + param�trage qx::register_class<T> ;
    • +
    • le mot-cl� "*" signifie "r�cup�rer toutes les relations d�finies dans la fonction de + param�trage qx::register_class<T> sur un niveau" ;
    • +
    • le mot-cl� "->" signifie jointure de type "LEFT OUTER JOIN" (jointure par d�faut + de la biblioth�que QxOrm) ;
    • +
    • le mot-cl� ">>" signifie jointure de type "INNER JOIN" entre deux tables.
    • +
    + Remarque : en utilisant le mot-cl� "*" pour indiquer "toutes les relations sur un + niveau", les appels suivants sont �quivalents : +
      +
    • qx::dao::fetch_by_id_with_relation("*", ...) == + qx::dao::fetch_by_id_with_all_relation(...) ; +
    • +
    • qx::dao::fetch_by_query_with_relation("*", ...) == + qx::dao::fetch_by_query_with_all_relation(...) ; +
    • +
    • qx::dao::fetch_all_with_relation("*", ...) == + qx::dao::fetch_all_with_all_relation(...). +
    • +
    +
    + Exemple : � partir du tutoriel qxBlog, il est possible de r�cup�rer les donn�es suivantes avec + une seule requ�te :
    +
    + 1- r�cup�rer un blog et son author ;
    + 2- pour l'author valoris�, r�cup�rer tous les blog qu'il a �crit ;
    + 3- pour chaque blog que l'author a �crit, r�cup�rer tous les comment + associ�s.
    +
    + + + + + + + +
    +
    blog_ptr my_blog = blog_ptr(new blog(10));
    +QSqlError daoError = qx::dao::fetch_by_id_with_relation("author_id->list_blog->list_comment", my_blog);
    +
    +
    + Ce qui g�n�re la requ�te SQL suivante : +
    +
    SELECT blog.blog_id AS blog_blog_id_0, blog.blog_text AS blog_blog_text_0, blog.date_creation AS blog_date_creation_0, blog.author_id AS blog_author_id_0, 
    +       author_1.author_id AS author_1_author_id_0, author_1.name AS author_1_name_0, author_1.birthdate AS author_1_birthdate_0, author_1.sex AS author_1_sex_0, 
    +       blog_2.blog_id AS blog_2_blog_id_0, blog_2.author_id AS blog_2_author_id_0, blog_2.blog_text AS blog_2_blog_text_0, blog_2.date_creation AS blog_2_date_creation_0, 
    +       comment_4.comment_id AS comment_4_comment_id_0, comment_4.blog_id AS comment_4_blog_id_0, comment_4.comment_text AS comment_4_comment_text_0, comment_4.date_creation AS comment_4_date_creation_0 
    +FROM blog 
    +LEFT OUTER JOIN author author_1 ON author_1.author_id = blog.author_id 
    +LEFT OUTER JOIN blog blog_2 ON blog_2.author_id = author_1.author_id 
    +LEFT OUTER JOIN comment comment_4 ON comment_4.blog_id = blog_2.blog_id 
    +WHERE blog.blog_id = :blog_id
    +
    +

    + Autre exemple : il est �galement possible de cr�er une liste de relations � r�cup�rer, comme + ceci par exemple :
    +
    + + + + + + + +
    +
    blog_ptr my_blog = blog_ptr(new blog(10));
    +QStringList relation;
    +relation << "author_id->list_blog->list_comment";
    +relation << "author_id->list_blog->list_category";
    +relation << "list_comment";
    +relation << "list_category";
    +QSqlError daoError = qx::dao::fetch_by_id_with_relation(relation, my_blog);
    +
    +
    + Ce qui g�n�re la requ�te SQL suivante : +
    +
    SELECT blog.blog_id AS blog_blog_id_0, blog.blog_text AS blog_blog_text_0, blog.date_creation AS blog_date_creation_0, blog.author_id AS blog_author_id_0, 
    +       author_1.author_id AS author_1_author_id_0, author_1.name AS author_1_name_0, author_1.birthdate AS author_1_birthdate_0, author_1.sex AS author_1_sex_0, 
    +       blog_2.blog_id AS blog_2_blog_id_0, blog_2.author_id AS blog_2_author_id_0, blog_2.blog_text AS blog_2_blog_text_0, blog_2.date_creation AS blog_2_date_creation_0, 
    +       category_5.category_id AS category_5_category_id_0, category_5.name AS category_5_name_0, category_5.description AS category_5_description_0, 
    +       comment_6.comment_id AS comment_6_comment_id_0, comment_6.blog_id AS comment_6_blog_id_0, comment_6.comment_text AS comment_6_comment_text_0, comment_6.date_creation AS comment_6_date_creation_0, 
    +       category_7.category_id AS category_7_category_id_0, category_7.name AS category_7_name_0, category_7.description AS category_7_description_0 
    +FROM blog 
    +LEFT OUTER JOIN author author_1 ON author_1.author_id = blog.author_id 
    +LEFT OUTER JOIN blog blog_2 ON blog_2.author_id = author_1.author_id 
    +LEFT OUTER JOIN category_blog category_blog_5 ON blog_2.blog_id = category_blog_5.blog_id 
    +LEFT OUTER JOIN category category_5 ON category_blog_5.category_id = category_5.category_id 
    +LEFT OUTER JOIN comment comment_6 ON comment_6.blog_id = blog.blog_id 
    +LEFT OUTER JOIN category_blog category_blog_7 ON blog.blog_id = category_blog_7.blog_id 
    +LEFT OUTER JOIN category category_7 ON category_blog_7.category_id = category_7.category_id 
    +WHERE blog.blog_id = :blog_id
    +
    +

    + Autre exemple : pour r�cup�rer toutes les relations pour un niveau donn�, il faut utiliser le + mot-cl� "*".
    + Pour r�cup�rer toutes les donn�es de toutes les relations sur trois niveaux, il faut �crire :
    +
    + + + + + + + +
    +
    blog_ptr my_blog = blog_ptr(new blog(10));
    +QSqlError daoError = qx::dao::fetch_by_id_with_relation("*->*->*", my_blog);
    +
    +
    + Ce qui g�n�re la requ�te SQL suivante : +
    +
    SELECT blog.blog_id AS blog_blog_id_0, blog.blog_text AS blog_blog_text_0, blog.date_creation AS blog_date_creation_0, blog.author_id AS blog_author_id_0, 
    +       author_1.author_id AS author_1_author_id_0, author_1.name AS author_1_name_0, author_1.birthdate AS author_1_birthdate_0, author_1.sex AS author_1_sex_0, 
    +       blog_2.blog_id AS blog_2_blog_id_0, blog_2.author_id AS blog_2_author_id_0, blog_2.blog_text AS blog_2_blog_text_0, blog_2.date_creation AS blog_2_date_creation_0, blog_2.author_id AS blog_2_author_id_0_2, 
    +       author_3.author_id AS author_3_author_id_0, author_3.name AS author_3_name_0, author_3.birthdate AS author_3_birthdate_0, author_3.sex AS author_3_sex_0, 
    +       comment_4.comment_id AS comment_4_comment_id_0, comment_4.blog_id AS comment_4_blog_id_0, comment_4.comment_text AS comment_4_comment_text_0, comment_4.date_creation AS comment_4_date_creation_0, 
    +       category_5.category_id AS category_5_category_id_0, category_5.name AS category_5_name_0, category_5.description AS category_5_description_0, 
    +       comment_6.comment_id AS comment_6_comment_id_0, comment_6.blog_id AS comment_6_blog_id_0, comment_6.comment_text AS comment_6_comment_text_0, comment_6.date_creation AS comment_6_date_creation_0, comment_6.blog_id AS comment_6_blog_id_0_6, 
    +       blog_7.blog_id AS blog_7_blog_id_0, blog_7.blog_text AS blog_7_blog_text_0, blog_7.date_creation AS blog_7_date_creation_0, blog_7.author_id AS blog_7_author_id_0_7, 
    +       author_8.author_id AS author_8_author_id_0, author_8.name AS author_8_name_0, author_8.birthdate AS author_8_birthdate_0, author_8.sex AS author_8_sex_0, 
    +       comment_9.comment_id AS comment_9_comment_id_0, comment_9.blog_id AS comment_9_blog_id_0, comment_9.comment_text AS comment_9_comment_text_0, comment_9.date_creation AS comment_9_date_creation_0, 
    +       category_10.category_id AS category_10_category_id_0, category_10.name AS category_10_name_0, category_10.description AS category_10_description_0, 
    +       category_11.category_id AS category_11_category_id_0, category_11.name AS category_11_name_0, category_11.description AS category_11_description_0, 
    +       blog_12.blog_id AS blog_12_blog_id_0, blog_12.blog_text AS blog_12_blog_text_0, blog_12.date_creation AS blog_12_date_creation_0, blog_12.author_id AS blog_12_author_id_0_12, 
    +       author_13.author_id AS author_13_author_id_0, author_13.name AS author_13_name_0, author_13.birthdate AS author_13_birthdate_0, author_13.sex AS author_13_sex_0, 
    +       comment_14.comment_id AS comment_14_comment_id_0, comment_14.blog_id AS comment_14_blog_id_0, comment_14.comment_text AS comment_14_comment_text_0, comment_14.date_creation AS comment_14_date_creation_0, 
    +       category_15.category_id AS category_15_category_id_0, category_15.name AS category_15_name_0, category_15.description AS category_15_description_0 
    +FROM blog 
    +LEFT OUTER JOIN author author_1 ON author_1.author_id = blog.author_id 
    +LEFT OUTER JOIN blog blog_2 ON blog_2.author_id = author_1.author_id 
    +LEFT OUTER JOIN author author_3 ON author_3.author_id = blog_2.author_id 
    +LEFT OUTER JOIN comment comment_4 ON comment_4.blog_id = blog_2.blog_id 
    +LEFT OUTER JOIN category_blog category_blog_5 ON blog_2.blog_id = category_blog_5.blog_id 
    +LEFT OUTER JOIN category category_5 ON category_blog_5.category_id = category_5.category_id 
    +LEFT OUTER JOIN comment comment_6 ON comment_6.blog_id = blog.blog_id 
    +LEFT OUTER JOIN blog blog_7 ON blog_7.blog_id = comment_6.blog_id 
    +LEFT OUTER JOIN author author_8 ON author_8.author_id = blog_7.author_id 
    +LEFT OUTER JOIN comment comment_9 ON comment_9.blog_id = blog_7.blog_id 
    +LEFT OUTER JOIN category_blog category_blog_10 ON blog_7.blog_id = category_blog_10.blog_id 
    +LEFT OUTER JOIN category category_10 ON category_blog_10.category_id = category_10.category_id 
    +LEFT OUTER JOIN category_blog category_blog_11 ON blog.blog_id = category_blog_11.blog_id 
    +LEFT OUTER JOIN category category_11 ON category_blog_11.category_id = category_11.category_id 
    +LEFT OUTER JOIN category_blog category_blog_12 ON category_11.category_id = category_blog_12.category_id 
    +LEFT OUTER JOIN blog blog_12 ON category_blog_12.blog_id = blog_12.blog_id 
    +LEFT OUTER JOIN author author_13 ON author_13.author_id = blog_12.author_id 
    +LEFT OUTER JOIN comment comment_14 ON comment_14.blog_id = blog_12.blog_id 
    +LEFT OUTER JOIN category_blog category_blog_15 ON blog_12.blog_id = category_blog_15.blog_id 
    +LEFT OUTER JOIN category category_15 ON category_blog_15.category_id = category_15.category_id 
    +WHERE blog.blog_id = :blog_id
    +
    +
    +
    +

    + Comment appeler une proc�dure stock�e ou une requ�te SQL personnalis�e + ?

    + + + + + + + + + +
    + La biblioth�que QxOrm fournit deux fonctions pour appeler une proc�dure stock�e ou une requ�te SQL + personnalis�e : + + Le premier param�tre de ces deux fonctions, de type qx::QxSqlQuery (ou + qx_query), correspond � la proc�dure stock�e ou � la requ�te SQL personnalis�e.
    + Pour plus d'informations sur la classe qx::QxSqlQuery, rendez-vous sur cette Q&R de la FAQ de QxOrm : Comment construire une requ�te pour interroger la base de donn�es sans + �crire de SQL avec la classe qx::QxSqlQuery ?
    +
    + La fonction qx::dao::execute_query<T>() est une fonction template : le type T + doit �tre enregistr� dans le contexte QxOrm (fonction qx::register_class<T>).
    + Toutes les donn�es renvoy�es par la proc�dure stock�e ou la requ�te SQL personnalis�e qui pourront + �tre associ�es aux membres des classes C++ (de type T) seront valoris�es automatiquement.
    + Une recherche automatique est effectu�e sur le nom des champs associ�s aux donn�es.
    + Voici un exemple d'utilisation (disponible dans le projet qxBlog du package QxOrm) :
    +
    + + + + + + + +
    +
    // Call a custom SQL query or a stored procedure and fetch automatically properties (with a collection of items)
    +qx_query testStoredProcBis("SELECT * FROM author");
    +daoError = qx::dao::execute_query(testStoredProcBis, authorX);
    +qAssert(! daoError.isValid()); qAssert(authorX.count() > 0);
    +qx::dump(authorX);
    +
    +

    + La fonction qx::dao::call_query() n'est pas une fonction template : les r�sultats de + la requ�te doivent �tre parcourus manuellement sur la classe qx::QxSqlQuery (ou + qx_query).
    + Pour r�cup�rer un param�tre de sortie (qui doit �tre pass� � la requ�te en tant que QSql::Out + ou QSql::InOut), il suffit d'utiliser la m�thode : QVariant qx::QxSqlQuery::boundValue(const + QString & sKey) const;.
    +
    + Pour parcourir la liste des r�sultats de la requ�te, il faut utiliser les m�thodes suivantes :
    + * long qx::QxSqlQuery::getSqlResultRowCount() const;
    + * long qx::QxSqlQuery::getSqlResultColumnCount() const;
    + * QVariant qx::QxSqlQuery::getSqlResultAt(long row, long column) const;
    + * QVariant qx::QxSqlQuery::getSqlResultAt(long row, const QString & column) const;
    + * QVector qx::QxSqlQuery::getSqlResultAllColumns() const;
    + * void qx::QxSqlQuery::dumpSqlResult();
    +
    + Voici un exemple d'utilisation avec la fonction qx::dao::call_query() :
    +
    + + + + + + + +
    +
    qx_query query("CALL MyStoredProc(:param1, :param2)");
    +query.bind(":param1", "myValue1");
    +query.bind(":param2", 5024, QSql::InOut);
    +QSqlError daoError = qx::dao::call_query(query);
    +QVariant vNewValue = query.boundValue(":param2");
    +query.dumpSqlResult();
    +
    +
    +
    +

    + Comment utiliser la classe qx::QxDaoAsync pour appeler des requ�tes de mani�re + asynchrone (multi-thread) ?

    + + + + + + + + + +
    + Il peut �tre parfois int�ressant d'ex�cuter certaines requ�tes � la base de donn�es de mani�re + asynchrone (multi-thread), par exemple pour �viter de bloquer une IHM si une requ�te est trop longue � + s'ex�cuter.
    + Pour simplifier les requ�tes asynchrones, la biblioth�que QxOrm fournit la classe qx::QxDaoAsync.
    + Cette classe ex�cute une requ�te dans un thread d�di� et renvoie un SIGNAL + queryFinished() lorsque la requ�te est termin�e.
    + Pour utiliser la classe qx::QxDaoAsync, il suffit de : +
      +
    • v�rifier que la requ�te fait appel � une classe qui impl�mente l'interface qx::IxPersistable ;
    • +
    • cr�er une instance de type qx::QxDaoAsync (par exemple, une propri�t� membre d'une classe + d�rivant du type QWidget) ;
    • +
    • connecter un SLOT au SIGNAL qx::QxDaoAsync::queryFinished() (par exemple, + un SLOT d�fini dans une classe d�rivant du type QWidget) ;
    • +
    • ex�cuter une requ�te � la base de donn�es en utilisant l'une des m�thodes commen�ant par + async : qx::QxDaoAsync::asyncXXXX(). +
    • +
    + Voici un exemple d'utilisation avec une classe nomm�e MyWidget :
    +
    + + + + + + + +
    +
    class MyWidget : public QWidget
    +{ Q_OBJECT
    +
    +   //...
    +   qx::QxDaoAsync m_daoAsync;
    +   //...
    +Q_SLOTS:
    +   void onQueryFinished(const QSqlError & daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams);
    +   //...
    +
    +};
    +
    +
    + Et voici l'impl�mentation de la classe MyWidget :
    +
    + + + + + + + +
    +
    MyWidget::MyWidget() : QObject()
    +{
    +   //...
    +   QObject::connect((& m_daoAsync), SIGNAL(queryFinished(const QSqlError &, qx::dao::detail::QxDaoAsyncParams_ptr)), 
    +                    this, SLOT(onQueryFinished(const QSqlError &, qx::dao::detail::QxDaoAsyncParams_ptr)));
    +   //...
    +}
    +
    +void MyWidget::onQueryFinished(const QSqlError & daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams)
    +{
    +   if (! pDaoParams) { return; }
    +   qx::QxSqlQuery query = pDaoParams->query;
    +   if (! daoError.isValid()) { ; }
    +   // If the async query is associated to a simple object, just use 'pDaoParams->pInstance' method
    +   qx::IxPersistable_ptr ptr = pDaoParams->pInstance;
    +   // If the async query is associated to a list of objects, just use 'pDaoParams->pListOfInstances' method
    +   qx::IxPersistableCollection_ptr lst = pDaoParams->pListOfInstances;
    +   //...
    +}
    +
    +
    +
    +

    + Comment utiliser le module QxModelView pour travailler avec le moteur + model/view de Qt (Qt widgets et vues QML) ?

    + + + + + + + + + +
    + Le module QxModelView + permet d'utiliser le moteur model/view + de Qt avec toutes les classes enregistr�es dans le contexte QxOrm : +
      +
    • Qt widgets : utilisation de QTableView ou QListView par exemple pour + afficher/modifier le contenu d'une table de base de donn�es ;
    • +
    • QML : toute propri�t� enregistr�e dans le contexte QxOrm est accessible en QML : le module QxModelView permet + ainsi de faciliter l'int�raction entre QML et les bases de donn�es.
    • +
    + L'interface qx::IxModel + propose une base commune pour tous les mod�les li�s aux classes persistantes d�clar�es dans le + contexte QxOrm. Les m�thodes de cette classe pr�fix�es par 'qx' appellent les fonctions du + namespace 'qx::dao' et communiquent donc directement avec la base de donn�es.
    +
    + Le projet de test qxBlogModelView pr�sent dans le dossier ./test/ du package QxOrm + montre comment cr�er rapidement un mod�le et l'associer au moteur model/view de Qt (d'abord + dans un widget Qt, puis dans une vue QML).
    +
    + 1- Exemple de cr�ation d'un mod�le pour afficher/modifier les donn�es de la table 'author' + (voir le tutoriel qxBlog pour la d�finition de la + classe 'author') dans un QTableView :
    +
    + + + + + + + +
    +
    // Create a model and fetch all data from database
    +qx::IxModel * pModel = new qx::QxModel<author>();
    +pModel->qxFetchAll();
    +
    +// Associate the model to a QTableView and display it
    +QTableView tableView;
    +tableView.setModel(pModel);
    +tableView.show();
    +
    +
    + Ce qui donne le r�sultat suivant � l'ex�cution :
    +
    + qx_model_view_01
    +

    + 2- Voici un autre exemple en QML (en Qt5, le module QxModelView �tant + �galement compatible avec Qt4) :
    +
    + + + + + + + +
    +
    // Create a model and fetch all data from database
    +qx::IxModel * pModel = new qx::QxModel<author>();
    +pModel->qxFetchAll();
    +
    +// Associate the model to a QML view and display it
    +QQuickView qmlView;
    +qmlView.rootContext()->setContextProperty("myModel", pModel);
    +qmlView.setSource(QUrl("qrc:/documents/main.qml"));
    +qmlView.show();
    +
    +
    + Et voici le contenu du fichier 'main.qml' :
    +
    + + + + + + + +
    +
    import QtQuick 2.1
    +import QtQuick.Controls 1.0
    +
    +Item {
    +   width: 400
    +   height: 300
    +   Row {
    +      height: 20
    +      spacing: 20
    +      Button {
    +         text: "Clear"
    +         onClicked: myModel.clear()
    +      }
    +      Button {
    +         text: "Fetch All"
    +         onClicked: myModel.qxFetchAll_()
    +      }
    +      Button {
    +         text: "Save"
    +         onClicked: myModel.qxSave_()
    +      }
    +   }
    +   ListView {
    +      y: 30
    +      height: 270
    +      model: myModel
    +      delegate: Row {
    +         height: 20
    +         spacing: 10
    +         Text { text: "id: " + author_id }
    +         TextField {
    +            text: name
    +            onTextChanged: name = text
    +         }
    +      }
    +   }
    +}
    +
    +
    + Ce qui donne le r�sultat suivant � l'ex�cution :
    +
    + qx_model_view_02
    +
    + Comme on peut le constater dans le fichier 'main.qml', les propri�t�s 'author_id' et + 'name' du mod�le 'author' (variable myModel) sont accessibles automatiquement en + lecture/�criture (car elles ont �t� enregistr�es dans le contexte QxOrm).
    + De plus, l'interface qx::IxModel propose une liste de m�thodes accessibles en QML (utilisation de + Q_INVOKABLE) pour communiquer directement avec la base de donn�es : ainsi, le bouton + 'Save' de l'�cran ci-dessus enregistre le mod�le en base de donn�es depuis QML.
    +
    + Remarque : un plugin de QxEntityEditor permet de g�n�rer automatiquement le code des + mod�les pour la gestion des relations. Il est ainsi possible de travailler avec des mod�les + imbriqu�s.
    +
    +

    +
    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/doc/qxorm_fr/home.html b/doc/qxorm_fr/home.html new file mode 100644 index 0000000..9b65c58 --- /dev/null +++ b/doc/qxorm_fr/home.html @@ -0,0 +1,491 @@ + + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic editor + (data model designer and source code generator) + + + + + + + + + + + + + + + + + + + + + +
    QxOrm + + + + Windows + Linux + Macintosh + C++
    +
    + + + + + + + + + + + + + + + + + + +
    AccueilT�l�chargementExemple rapide + Tutoriel (4) + + + + + + +
    + +
    +
    + Manuel (2) + + + + + + +
    + +
    +
    ForumNos clients
    +
    + + + + + + + + + + + + + + + + + +
    + QxOrm >> Accueil + + + + + + + + + + + + + + +
    + Version courante :  + + QxOrm 1.5.0 - documentation en ligne de la biblioth�que QxOrm - GitHub +
    + + + QxEntityEditor 1.2.8 +
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    + + + + + + + + + +
    +

    QxOrm (le moteur) + QxEntityEditor (l'�diteur graphique) = la meilleure + solution pour g�rer sa couche de donn�es persistante en C++/Qt !

    +
    +
    +
    QxOrm est une biblioth�que C++ de gestion de donn�es. + A partir d'une simple fonction de param�trage (que l'on peut + comparer avec un fichier de mapping Hibernate en Java), vous + aurez acc�s aux fonctionnalit�s suivantes :
    +
      +
    • + Persistance + : support des bases de donn�es SQLite, MySQL, PostgreSQL, Oracle, MS SQL Server, MongoDB (gestion des relations 1-1, 1-n, + n-1 et n-n) ; +
    • +
    • + S�rialisation + des donn�es (flux JSON, binaire et XML) ;
    • +
    • + R�flexion + (ou + introspection + ) pour acc�der dynamiquement aux classes, attributs et invoquer des m�thodes ;
    • +
    • + Serveur web HTTP + + : serveur web compatible HTTP 1.1 autonome, performant, multi-plateforme et simple + d'utilisation ;
    • +
    • + API JSON + : interop�rabilit� avec d'autres technologies que C++/Qt (web services REST, applications + QML, langages de script).
    • +
    +
    +
    qt_ambassador
    + + QxOrm library has been accepted into the Qt Ambassador + Program + +
    +
    + QxEntityEditor est un �diteur graphique pour la biblioth�que QxOrm : QxEntityEditor + permet de g�rer graphiquement le mod�le d'entit�s.
    + QxEntityEditor est multi-plateforme (disponible pour Windows, Linux et Mac OS X) et g�n�re du code + natif pour tous les environnements : bureau (Windows, Linux, Mac OS X), embarqu� et mobile (Android, iOS, + Windows Phone, Raspberry Pi, etc.).
    + Un manuel utilisateur (documentation) d�di� � l'application QxEntityEditor + est disponible.
    +
    + QxEntityEditor est bas� sur un syst�me de plugins et propose diverses fonctionnalit�s pour + importer/exporter le mod�le de donn�es : + + QxEntityEditor +

    +
    +
    + La biblioth�que QxOrm a pour objectif de simplifier le code C++ tout en offrant un maximum de + fonctionnalit�s.
    + Voici les principaux avantages de QxOrm : +
      +
    • non intrusif : la fonction de param�trage se trouve � + l'ext�rieur des classes. La signature des classes n'est donc pas + modifi�e. QxOrm peut donc �tre utilis� dans des projets + existants ; +
    • +
    • aucune n�cessit� d'appartenir � une hi�rarchie de + classes : les classes n'ont pas besoin de d�river d'un 'super + objet' ; +
    • +
    • pas de mapping XML ; +
    • +
    • 1 seul fichier <QxOrm.h> � inclure dans l'en-t�te + pr�compil� (precompiled-header recommand� pour optimiser les temps + de compilation) ; +
    • +
    • aucune n�cessit� d'utiliser le pr�-compilateur 'moc' fourni + par Qt ; +
    • +
    • v�rification des types � la compilation (utilisation intensive + de m�ta-programmation) ; +
    • +
    • compatible avec Visual C++ sous Windows, + GCC sous Linux, Clang sous Mac OS X, et MinGW sous Windows (d'autres environnements + et + plateformes seront test�s prochainement : mobiles, etc...) ; +
    • +
    • compatible avec le d�veloppement de biblioth�ques partag�es + (shared library, *.dll pour Windows, *.so pour Linux, + etc...). +
    • +
    +
    +

    QxOrm est bas� sur la biblioth�que Qt (compatible Qt4, Qt5, Qt6) :

    + +

    + + + + + + + + + +
    QtQt : biblioth�que compl�te : IHM + (QtGui), r�seau (QtNetwork), XML (QtXml), base de donn�es + (QtSql), etc...
    + La documentation est excellente et le code C++ �crit � partir + de cette biblioth�que est � la fois performant et simple de + compr�hension.
    + Depuis le rachat par Nokia puis Digia et sa nouvelle licence LGPL, Qt est + sans contexte la biblioth�que phare du moment.
    + QxOrm est compatible avec les principaux objets d�finis par Qt + : QObject, QString, QDate, QTime, QDateTime, QList, QHash, + QSharedPointer, QScopedPointer, etc...
    + Il est conseill� d'installer et d'utiliser la derni�re version + de Qt disponible � l'adresse suivante : http://www.qt.io/
    +

    + +
    +

    QxOrm est divis� en sous-modules, voici un descriptif rapide :

    +
      +
    • +

      QxDao : bas� sur le moteur QtSql de Qt, ce module + permet de communiquer (s�lection, modification, suppression, + transaction, etc...) avec de nombreuses bases de donn�es en mappant + chaque champ d'une table avec les propri�t�s d'une classe C++. Les + relations de type 1-1, 1-n, n-1 et n-n sont tr�s simples � + mettre en place dans le code C++. Ce module supporte la + programmation objet : h�ritage, polymorphisme, composition, + association ainsi que les collections (support des collections de + stl, boost et Qt : std::vector, std::list, + std::unordered_map, QList, QHash, etc...). +

      +
    • +
    • +

      QxRegister, QxDataMember et QxFunction : m�me si + ce n'est pas le but 1er de QxOrm, il est n�cessaire d'�muler le + m�canisme de reflection (ou introspection) pr�sent nativement dans d'autres langages + (Java, C#, etc...) pour utiliser toutes les fonctionnalit�s de + QxOrm.

      +
    • +
    • +

      QxModelView : + toute classe enregistr�e dans le contexte QxOrm peut �tre utilis�e par le moteur model/view de Qt (Qt widgets + et/ou vues QML). + L'interface qx::IxModel expose + automatiquement au moteur QML toutes les propri�t�s d�finies dans le contexte QxOrm. + Ce module permet ainsi de faciliter l'int�raction entre QML et les bases de donn�es.

      +
    • +
    • +

      QxService : bas� sur le + moteur QtNetwork de Qt, + ce module + permet de cr�er de mani�re simple et performante un serveur d'applications en C++ (notion de + services avec demande du client et r�ponse du serveur). QxService utilise la + serialization et l'introspection des donn�es + et fonctions pour pouvoir faire transiter sur le r�seau n'importe quelle classe ou structure. Pour plus + d'informations sur ce module, un tutoriel est disponible en cliquant + ici. +

      +
    • +
    • +

      QxCollection<Key, + Value> : + + cette collection thread-safe permet de cumuler + les avantages d'un std::vector<T> (conserve l'ordre + d'insertion + acc�s rapide par index) et d'un + std::unordered_map<Key, Value> ou encore QHash<Key, + Value> (acc�s rapide par cl� : hash-map). Ce type de collection + est particuli�rement adapt� pour contenir les �l�ments issus d'une + base de donn�es. +

      +
    • +
    • +

      QxSerialize : + + Toute classe d�finie par QxOrm + peut �tre s�rializ�e dans un flux binaire, XML et JSON. Ce module permet + �galement de cloner toutes les instances d'objet. Les principaux + objets de la biblioth�que Qt (QObject, QString, QDate, QTime, + QDateTime, QList, QHash, etc...) sont compatibles avec le module + QxSerialize. Ce module assure une compatibilit� ascendante avec + prise en compte d'un n� de version par classe. +

      +
    • +
    • +

      QxFactory : toute instance + d'objet d�finie par QxOrm + peut �tre cr��e en fonction du nom de la classe.

      +
    • +
    • +

      QxTraits : ce module + contient une liste de classes de + traits non d�finies dans la biblioth�que standard et qui sont utiles pour + l'utilisation de QxOrm. Ces classes de traits se veulent g�n�riques + et peuvent donc �tre utilis�es dans d'autres contextes que celui de + la biblioth�que QxOrm.

      +
    • +
    • +

      QxCache : le 'cache' de + QxOrm peut contenir tous types + d'objets. Il peut permettre par exemple de m�moriser des donn�es + issues d'une base de donn�es pour �viter des requ�tes trop + fr�quentes. Ce cache est g�n�rique et peut �tre utilis� dans d'autres contextes que + celui de QxOrm.

      +
    • +
    • +

      QxValidator : ce module + permet d'ajouter des contraintes + sur les propri�t�s enregistr�es dans le contexte QxOrm. Ces contraintes sont d�finies dans la m�thode de + mapping : void qx::register_class. + Si pour une instance de classe donn�e, au moins une contrainte n'est pas respect�e, alors l'instance est + consid�r�e comme invalide : + l'objet ne peut alors pas �tre sauvegard� en base de donn�es (INSERT ou UPDATE). Pour plus + d'informations sur le module QxValidator, + rendez-vous sur le manuel utilisateur de la biblioth�que QxOrm. +

      +
    • +
    • +

      QxMemLeak : permet une + d�tection rapide des fuites + m�moire en mode Debug une fois l'ex�cution du programme termin�e + (avec indication du fichier et de la ligne => style MFC de + Microsoft). Ce module a �t� d�velopp� par Wu + Yongwei et a subi + quelques modifications pour �tre int�gr� dans QxOrm. Si un autre + outil est d�j� utilis� dans vos projets (Valgrind par exemple), + cette fonctionnalit� ne doit pas �tre activ�e. + Par d�faut, le module QxMemLeak est d�sactiv�.

      +
    • +
    +

    +
    +

    Vous pouvez t�l�charger la derni�re version de QxOrm et QxEntityEditor en cliquant ici.
    + Un manuel utilisateur pour apprendre � travailler avec la biblioth�que QxOrm est disponible en cliquant ici.
    + Un exemple rapide d'utilisation de QxOrm montrant les fonctionnalit�s de base de la biblioth�que + est disponible en cliquant ici.
    + Un forum (en anglais) d�di� � QxOrm et QxEntityEditor est disponible en cliquant ici.
    + Vous pouvez �galement retrouver la communaut� fran�aise de QxOrm et QxEntityEditor sur le forum de Developpez.com.
    + Une documentation en ligne du code source de la biblioth�que QxOrm est disponible en cliquant ici.
    +

    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/doc/qxorm_fr/link.html b/doc/qxorm_fr/link.html new file mode 100644 index 0000000..149582e --- /dev/null +++ b/doc/qxorm_fr/link.html @@ -0,0 +1,265 @@ + + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic editor + (data model designer and source code generator) + + + + + + + + + + + + + + + + + + + + + +
    QxOrm + + + + Windows + Linux + Macintosh + C++
    +
    + + + + + + + + + + + + + + + + + + +
    AccueilT�l�chargementExemple rapide + Tutoriel (4) + + + + + + +
    + +
    +
    + Manuel (2) + + + + + + +
    + +
    +
    ForumNos clients
    +
    + + + + + + + + + + + + + + + + + +
    + QxOrm >> Forum + + + + + + + + + + + + + + +
    + Version courante :  + + QxOrm 1.5.0 - documentation en ligne de la biblioth�que QxOrm - GitHub +
    + + + QxEntityEditor 1.2.8 +
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    + + + + + + + + + +
    + Si vous trouvez un bug ou si vous avez une question concernant le fonctionnement de la biblioth�que + QxOrm ou de l'application QxEntityEditor : + + + + + + + + + + +
    + + + QxOrm forum + + QxEntityEditor forum +
    +
    + Pour toutes questions relatives aux librairies boost et Qt : + + + + + + + + + + +
    + + + Qt forum + + boost forum +
    +
    qt_ambassador
    + + QxOrm library has been accepted into the Qt Ambassador + Program + +
    +
    + Voici une liste de sites fran�ais concernant tous les domaines du d�veloppement logiciel avec forums, + tutoriaux, documentations, etc... + +
    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/doc/qxorm_fr/manual.html b/doc/qxorm_fr/manual.html new file mode 100644 index 0000000..377c668 --- /dev/null +++ b/doc/qxorm_fr/manual.html @@ -0,0 +1,13103 @@ + + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic + editor (data model designer and source code generator) + + + + + + + + + + + + + + + + + + + + + +
    QxOrm + + + Windows + Linux + Macintosh + C++
    +
    + + + + + + + + + + + + + + + + + + +
    AccueilT�l�chargementExemple rapide + Tutoriel (4) + + + + + + +
    + +
    +
    + Manuel (2) + + + + + + +
    + +
    +
    ForumNos clients
    +
    + + + + + + + + + + + + + + + + + +
    + QxOrm >> Manuel d'utilisation de la biblioth�que QxOrm + + + + + + + + + + + + + + + +
    + Version courante :  + + QxOrm 1.5.0 - documentation en ligne de la biblioth�que QxOrm - GitHub +
    + + + QxEntityEditor 1.2.8 +
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    + + + + + + + + + + +
    + S�lection du manuel : + + Manuel QxOrm + Manuel QxEntityEditor +
    +
    +
    + + + + + + + + + +
    +

    Manuel d'utilisation de la biblioth�que QxOrm - Table des + mati�res

    +
    +
      +
    1. + Introduction +
        +
      1. + Biblioth�que QxOrm +
      2. +
      3. + Aper�u rapide de l'application QxEntityEditor +
      4. +
      5. + Convention d'�criture C++ utilis�e par la biblioth�que + QxOrm +
      6. +
      +
    2. +
    3. + Installation +
        +
      1. + D�pendance � Qt +
      2. +
      3. + D�pendance � boost (optionnel) +
      4. +
      5. + Fichier de configuration QxOrm.pri (ou QxOrm.cmake) +
      6. +
      7. + Compiler la biblioth�que QxOrm (avec qmake ou CMake) +
      8. +
      9. + Pilotes SQL fournis par Qt (drivers) +
      10. +
      +
    4. +
    5. + Persistance - Object Relational Mapping (ORM) +
        +
      1. + D�finir une classe dans le contexte QxOrm (mapping) +
          +
        1. + Cl� primaire autre que le type par d�faut + "long" +
        2. +
        3. + Cl� primaire sur plusieurs colonnes (composite + key) +
        4. +
        5. + Donn�es membres public/protected/private +
        6. +
        7. + Espace de nom (namespace) +
        8. +
        9. + Types C++ support�s par QxOrm +
        10. +
        11. + D�finir une donn�e membre transient +
        12. +
        +
      2. +
      3. + Connexion � la base de donn�es +
      4. +
      5. + Sauvegarder une instance C++ en base de donn�es + (insert/update) +
      6. +
      7. + Supprimer une instance C++ de la base de donn�es + (delete) +
          +
        1. + Suppression logique (soft delete) +
        2. +
        +
      8. +
      9. + R�cup�rer une instance C++ de la base de donn�es + (fetch) +
      10. +
      11. + Requ�tes SQL +
          +
        1. + Utilisation de la classe qx::QxSqlQuery (ou son + alias qx_query) +
        2. +
        3. + Appel de proc�dure stock�e ou requ�te SQL + personnalis�e +
        4. +
        +
      12. +
      13. + Transactions (commit, rollback, session) +
      14. +
      15. + Moteur de relations +
          +
        1. + one-to-many (1-n) +
        2. +
        3. + many-to-one (n-1) +
        4. +
        5. + many-to-many (n-n) +
        6. +
        7. + one-to-one (1-1) +
        8. +
        9. + Requ�te SQL avec relations +
        10. +
        11. + S�lectionner les colonnes des relations � + r�cup�rer et d�finition des alias SQL +
        12. +
        13. + Ajout SQL dans les clauses LEFT OUTER JOIN / + INNER JOIN +
        14. +
        +
      16. +
      17. + Collections support�es par QxOrm +
          +
        1. + Collections de Qt +
        2. +
        3. + Collections de boost +
        4. +
        5. + Collections fournies par l'espace de nom standard + std +
        6. +
        7. + qx::QxCollection +
        8. +
        +
      18. +
      19. + Pointeurs intelligents support�s par QxOrm + (smart-pointers) +
          +
        1. + Pointeurs intelligents de Qt +
        2. +
        3. + Pointeurs intelligents de boost +
        4. +
        5. + Pointeurs intelligents fournis par l'espace de + nom standard std +
        6. +
        7. + qx::dao::ptr +
        8. +
        +
      20. +
      21. + D�clencheurs (triggers) +
      22. +
      23. + Validation d'une instance C++ (validators) +
      24. +
      25. + G�rer la valeur NULL de la base de donn�es +
          +
        1. + boost::optional +
        2. +
        3. + QVariant +
        4. +
        +
      26. +
      27. + H�ritage et polymorphisme +
      28. +
      29. + Interface qx::IxPersistable (classe abstraite) +
      30. +
      31. + Utiliser le pattern C++ PIMPL (Private Implementation + idiom ou d-pointer) +
      32. +
      33. + Persister des types personnalis�s +
      34. +
      35. + G�n�rer le sch�ma DDL SQL de la base de donn�es +
      36. +
      37. + Associer un type SQL � une classe C++ +
      38. +
      39. + Effectuer des requ�tes asynchrones � la base de + donn�es +
      40. +
      41. + Gestion du cache pour sauvegarder des instances C++ + (module QxCache) +
      42. +
      43. + Travailler avec plusieurs bases de donn�es +
      44. +
      45. + D�clarer une classe abstraite dans le contexte QxOrm +
      46. +
      47. + D�clarer automatiquement les m�ta-propri�t�s de Qt + (macro Q_PROPERTY) +
      48. +
      +
    6. +
    7. + S�rialisation +
        +
      1. + N� version pour assurer une compatibilit� ascendante +
      2. +
      3. + Moteur QDataStream de Qt +
      4. +
      5. + Moteur JSON de Qt +
      6. +
      7. + Moteur XML de boost::serialization +
      8. +
      9. + Moteur binaire de boost::serialization +
      10. +
      11. + Autres types de s�rialisation propos�s par boost +
      12. +
      13. + Cloner une instance C++ +
      14. +
      15. + Afficher le d�tail d'une instance C++ (dump au format + XML ou JSON) +
      16. +
      +
    8. +
    9. + Introspection - R�flexion +
        +
      1. + Obtenir dynamiquement la valeur d'une donn�e membre +
      2. +
      3. + Valoriser dynamiquement une donn�e membre +
      4. +
      5. + Appeler dynamiquement une fonction +
      6. +
      7. + Cr�er une instance C++ dynamiquement +
      8. +
      9. + Parcourir la liste des classes/propri�t�s enregistr�es + dans le contexte QxOrm +
      10. +
      +
    10. +
    11. + Services : transf�rer la couche de donn�es persistante sur le + r�seau (module QxService) +
        +
      1. + Param�tres d'entr�e/sortie d'un service + (requ�te/r�ponse) +
      2. +
      3. + D�finir les fonctions publi�es par un service +
      4. +
      5. + Liste des options disponibles c�t� serveur +
      6. +
      7. + Param�trage de la connexion c�t� client +
      8. +
      9. + Gestion de l'authentification dans un service +
      10. +
      11. + Requ�tes client/serveur asynchrones +
      12. +
      +
    12. +
    13. + Moteur mod�le/vue (module QxModelView) +
        +
      1. + D�finir un mod�le "simple" (sans relation) +
      2. +
      3. + Mod�les avec relations (notion de mod�les imbriqu�s) +
      4. +
      5. + Int�raction avec les vues QML +
      6. +
      7. + Int�raction avec les vues QtWidget +
      8. +
      9. + Connexion d'un mod�le au module QxService +
      10. +
      +
    14. +
    15. + QxOrm et MongoDB (C++ ODM Object Document Mapper) +
        +
      1. + Pr�-requis : driver libmongoc et + libbson +
      2. +
      3. + Param�trage du fichier QxOrm.pri (ou + QxOrm.cmake) +
      4. +
      5. + Connexion � la base de donn�es MongoDB +
      6. +
      7. + D�finition d'une classe persistante MongoDB (Collection) + dans le contexte QxOrm (mapping) +
          +
        1. + Gestion des cl�s primaires ObjectId +
        2. +
        +
      8. +
      9. + Ins�rer une instance C++ (Document) dans la base de + donn�es MongoDB (INSERT) +
          +
        1. + Ins�rer une liste d'instances C++ (plusieurs + Documents) dans la base de donn�es MongoDB (INSERT) +
        2. +
        +
      10. +
      11. + Mettre � jour une instance C++ (Document) dans la base + de donn�es MongoDB (UPDATE) +
          +
        1. + Mettre � jour une liste d'instances C++ + (plusieurs Documents) dans la base de donn�es MongoDB (UPDATE) +
        2. +
        +
      12. +
      13. + Supprimer une instance C++ (Document) de la base de + donn�es MongoDB (DELETE) +
          +
        1. + Supprimer une liste d'instances C++ (plusieurs + Documents) de la base de donn�es MongoDB (DELETE) +
        2. +
        +
      14. +
      15. + R�cup�rer une instance C++ (Document) de la base de + donn�es MongoDB (FETCH) +
          +
        1. + R�cup�rer une liste d'instances C++ (plusieurs + Documents) de la base de donn�es MongoDB (FETCH) +
        2. +
        +
      16. +
      17. + Requ�tes JSON +
          +
        1. + Utilisation de la classe qx::QxSqlQuery (ou son + alias qx_query) +
        2. +
        3. + Utiliser le moteur d'aggregation MongoDB +
        4. +
        5. + Ajouter des propri�t�s � la requ�te de type : + 'sort', 'limit', 'skip', etc... +
        6. +
        7. + Ex�cuter une requ�te personnalis�e +
        8. +
        +
      18. +
      19. + Moteur de relations (n�cessite une version MongoDB 3.6 + ou +) +
          +
        1. + Relations : Embedded vs Referenced +
        2. +
        +
      20. +
      21. + Cr�ation automatique des index +
      22. +
      +
    16. +
    17. + Serveur web HTTP/HTTPS (module QxHttpServer) +
        +
      1. + Hello World ! +
      2. +
      3. + Param�trage du serveur web HTTP +
          +
        1. + Connexions s�curis�es SSL/TLS +
        2. +
        +
      4. +
      5. + Routage des URL (d�finir les endpoints / dispatcher) +
          +
        1. + Routage dynamique des URL +
        2. +
        +
      6. +
      7. + R�cup�rer les param�tres de la requ�te HTTP +
      8. +
      9. + G�n�rer la r�ponse HTTP +
      10. +
      11. + Sessions (stockage par client c�t� serveur) +
      12. +
      13. + Cookies +
      14. +
      15. + Gestion des fichiers statiques +
      16. +
      17. + Encodage de transfert en bloc (chunked responses) +
      18. +
      19. + Requ�tes par les API JSON (module QxRestApi) +
      20. +
      21. + WebSocket +
      22. +
      23. + Performance (test� avec Apache Benchmark) +
          +
        1. + Am�liorer les performances avec epoll dispatcher + sous Linux +
        2. +
        +
      24. +
      +
    18. +
    19. + API REST JSON (module QxRestApi) +
        +
      1. + Principe de fonctionnement +
          +
        1. + Cas d'utilisation +
        2. +
        +
      2. +
      3. + Projet de test qxBlogRestApi (QML et serveur web + HTTP) +
      4. +
      5. + R�cup�ration de donn�es (fetch/count/exist) +
          +
        1. + fetch_all +
        2. +
        3. + fetch_by_id +
        4. +
        5. + fetch_by_query +
        6. +
        7. + count +
        8. +
        9. + exist +
        10. +
        +
      6. +
      7. + Ajout de donn�es (insert) +
      8. +
      9. + Mise � jour de donn�es (update) +
      10. +
      11. + Sauvegarde de donn�es (save) +
      12. +
      13. + Suppression de donn�es (delete) +
          +
        1. + delete_all / destroy_all +
        2. +
        3. + delete_by_query / destroy_by_query +
        4. +
        5. + delete_by_id / destroy_by_id +
        6. +
        +
      14. +
      15. + Validation de donn�es (validate) +
      16. +
      17. + Appel RAW SQL ou proc�dure stock�e +
      18. +
      19. + Appel fonctions natives C++ +
      20. +
      21. + Meta-data (structure des classes C++ enregistr�es dans + le contexte QxOrm) +
      22. +
      23. + Envoyer une liste de requ�tes JSON +
      24. +
      +
    20. +
    +
    +
    qt_ambassador
    + + QxOrm library has been accepted into the Qt + Ambassador Program + +
    +
    +
    +
    + +

    Introduction

    +
    + L'objectif de ce manuel utilisateur est de pr�senter de mani�re structur�e l'ensemble des + fonctionnalit�s propos�es par la biblioth�que QxOrm. + Ce manuel est destin� aux d�veloppeurs et architectes logiciel qui souhaitent g�rer une couche de + donn�es persistante en C++/Qt. + Des comp�tences techniques en C++ et base de donn�es sont requises pour la bonne compr�hension de + ce document. +

    + Remarque : la plupart des fonctionnalit�s pr�sent�es dans ce manuel peuvent �tre d�finies + rapidement et facilement avec l'application QxEntityEditor (l'�diteur graphique de la + biblioth�que QxOrm). + Une documentation d�di�e � l'application QxEntityEditor est + �galement disponible. +

    + Autre remarque : ce manuel est bas� en grande partie sur l'ancienne FAQ + du site QxOrm, toujours accessible en cliquant ici. +

    +

    Biblioth�que QxOrm +

    +
    + QxOrm est une biblioth�que C++ open source de gestion de donn�es (Object Relational Mapping, + ORM).
    + QxOrm est d�velopp� XDL Teamarty, Ing�nieur en d�veloppement logiciel depuis 2003.
    +
    + � partir d'une simple fonction de param�trage (que l'on peut comparer avec un fichier de + mapping XML Hibernate), vous aurez + acc�s aux fonctionnalit�s suivantes : +
      +
    • + Persistance + + : support des bases de donn�es SQLite, MySQL, PostgreSQL, Oracle, MS SQL Server, MongoDB (gestion des relations 1-1, + 1-n, n-1 et n-n) ; +
    • +
    • + S�rialisation + des donn�es (flux JSON, binaire et XML) ;
    • +
    • + R�flexion + + (ou + introspection + ) pour acc�der dynamiquement aux classes, attributs et invoquer des m�thodes ;
    • +
    • + Serveur web + HTTP + : serveur web compatible HTTP 1.1 autonome, performant, multi-plateforme et simple + d'utilisation ;
    • +
    • + API JSON + + : interop�rabilit� avec d'autres technologies que C++/Qt (web services REST, + applications QML, langages de script).
    • +
    + QxOrm est d�pendant des excellentes biblioth�ques Qt (compatible � partir de la version 4.5.0) et boost (compatible � partir de la + version 1.38, par d�faut seuls les fichiers d'en-t�te *.hpp sont n�cessaires).
    + La biblioth�que QxOrm a �t� retenue pour faire partie du programme Qt + Ambassador. +

    + Si vous trouvez un bug ou si vous avez une question concernant le fonctionnement de la + biblioth�que QxOrm, + vous pouvez envoyer un mail � : support@qxorm.com.
    + Un forum (en anglais) d�di� � QxOrm est disponible en cliquant ici.
    + Vous pouvez �galement retrouver la communaut� fran�aise de QxOrm sur le forum de Developpez.com. +

    +
    +

    Aper�u rapide de + l'application QxEntityEditor

    +
    + QxEntityEditor est un �diteur graphique pour la biblioth�que QxOrm : + QxEntityEditor permet de g�rer graphiquement le mod�le d'entit�s.
    + QxEntityEditor est multi-plateforme (disponible pour Windows, Linux et Mac OS X) et + g�n�re du code natif pour tous les environnements : bureau (Windows, Linux, Mac OS X), embarqu� + et mobile (Android, iOS, Windows Phone, Raspberry Pi, etc.).
    + Une vid�o de pr�sentation de l'application QxEntityEditor est + disponible.
    +
    + QxEntityEditor est bas� sur un syst�me de plugins et propose diverses fonctionnalit�s + pour importer/exporter le mod�le de donn�es : +
      +
    • g�n�ration automatique du code C++ (classes persistantes enregistr�es dans le contexte + QxOrm) ;
    • +
    • g�n�ration automatique des scripts SQL DDL (sch�ma de base de donn�es) pour les bases + SQLite, MySQL, PostgreSQL, Oracle et MS SQL Server ;
    • +
    • supporte l'�volution du sch�ma de base de donn�es pour chaque version d'un projet + (ALTER TABLE, ADD COLUMN, DROP INDEX, etc.) ;
    • +
    • g�n�ration automatique des classes C++ de services pour transf�rer le mod�le de donn�es + sur le r�seau, en utilisant le module QxService, pour cr�er rapidement des applications client/serveur ; +
    • +
    • importation automatique des structures de bases de donn�es existantes (par connexion + ODBC) pour les bases SQLite, MySQL, PostgreSQL, Oracle et MS SQL Server ;
    • +
    • parce que chaque projet est diff�rent, QxEntityEditor propose plusieurs outils pour + personnaliser les fichiers g�n�r�s (notamment un moteur javascript et un d�bogueur + int�gr�).
    • +
    + QxEntityEditor +

    + QxEntityEditor est d�velopp� XDL Teamarty, Ing�nieur en d�veloppement logiciel depuis + 2003.
    + Un manuel utilisateur d�di� � l'application QxEntityEditor + est disponible.
    +

    +
    +

    Convention d'�criture C++ + utilis�e par la biblioth�que QxOrm

    +
    + La biblioth�que QxOrm utilise les conventions d'�criture de code C++ suivantes : +
      +
    • toutes les classes, fonctions, variables, etc... sont d�finies dans l'espace de nom (namespace) qx ; +
    • +
    • les macro de QxOrm sont �crites sous la forme QX_... ; +
    • +
    • les classes abstraites (ou interfaces) ont le pr�fixe Ix (par exemple : + IxFactory est une interface pour la cr�ation d'instances) ; +
    • +
    • les autres classes ont le pr�fixe Qx (par exemple : QxDataMember) ; +
    • +
    • les collections d'objets ont pour suffixe X (par exemple : QxDataMemberX + est une collection de QxDataMember) ; +
    • +
    • les fonctions pour communiquer avec les bases de donn�es se trouvent sous le namespace qx::dao (par exemple + : qx::dao::fetch_by_id()) ; +
    • +
    • les fonctions pour la serialization des donn�es se trouvent sous le namespace + qx::serialization (par exemple : qx::serialization::xml::to_file()) ; +
    • +
    • le moteur de reflection (ou introspection) est accessible depuis la classe + qx::QxClassX (par exemple qx::QxClassX::invoke() pour invoquer une + m�thode de classe) ; +
    • +
    • les classes de traits se trouvent sous le namespace qx::trait (par + exemple : qx::trait::is_smart_ptr<T>). +
    • +
    +
    +
    + +

    Installation

    +
    + La biblioth�que QxOrm est multi-plateforme et peut �tre install�e sur tous types d'environnement : + Windows, Linux (Unix), Mac OS X, Android, iOS, Windows Phone, etc...
    + Un tutoriel complet (avec captures d'�cran) pour installer un + environnement de d�veloppement avec QxOrm sous Windows est disponible en cliquant ici. +

    + L'objectif de ce chapitre est de pr�senter rapidement les diff�rentes �tapes � suivre pour + installer QxOrm sur tous types d'environnement : + +
    +

    D�pendance � Qt

    +
    + + + + + + + + + +
    QtQt : biblioth�que compl�te : IHM + (QtGui), r�seau (QtNetwork), XML (QtXml), base de donn�es + (QtSql), etc.
    + La documentation est excellente et le code C++ �crit � partir + de cette biblioth�que est � la fois performant et simple de + compr�hension.
    + Depuis le rachat par Nokia puis Digia et sa nouvelle licence LGPL, Qt est + sans contexte la biblioth�que phare du moment.
    + QxOrm est compatible avec les principaux objets d�finis par Qt + : QObject, QString, QDate, QTime, QDateTime, QList, QHash, + QSharedPointer, QScopedPointer, etc.
    + Il est conseill� d'installer et d'utiliser la derni�re version + de Qt disponible � l'adresse suivante : http://www.qt.io/
    +
    + Remarque : par d�faut, la biblioth�que QxOrm d�pend uniquement des modules QtCore et QtSql. + Il est possible d'activer des fonctionnalit�s suppl�mentaires gr�ce au fichier de configuration QxOrm.pri (ou QxOrm.cmake) : + ces nouvelles fonctionnalit�s peuvent alors ajouter des d�pendances � QxOrm. +

    +
    +

    D�pendance � boost + (optionnel)

    +
    + Par d�faut, la biblioth�que QxOrm d�pend uniquement de Qt (QtCore et QtSql). + L'installation de boost est optionnelle et non requise avec la configuration par d�faut. +
    + Remarque : QxOrm propose 2 niveaux de d�pendance � boost en option : +
      +
    • une d�pendance uniquement aux fichiers d'en-t�tes de boost (*.hpp) : option de + compilation _QX_ENABLE_BOOST ;
    • +
    • une d�pendance au module boost serialization : option de compilation + _QX_ENABLE_BOOST_SERIALIZATION. +
    • +
    +
    + + + + + + + + + +
    boostboost : de nombreux modules de la + biblioth�que boost font partie de la nouvelle norme C++.
    + C'est une biblioth�que reconnue pour sa qualit�, son code 'C++ + moderne', sa documentation, sa portabilit�, etc...
    + QxOrm utilise les fonctionnalit�s suivantes de boost : + smart_pointer, type_traits, + multi_index_container, unordered_container, any, tuple, + foreach, function. Toutes ces fonctionnalit�s sont header only, la + d�pendance au module serialization est optionnelle.
    + Il est conseill� d'installer et d'utiliser la derni�re version + de boost disponible � l'adresse suivante : http://www.boost.org/ +
    +
    + Remarque importante : avec l'option de compilation _QX_ENABLE_BOOST, la + biblioth�que QxOrm d�pend uniquement des fichiers d'en-t�te *.hpp de boost (utilisation + des biblioth�ques header only uniquement). + L'installation de boost est donc tr�s simple puisqu'il suffit de d�zipper le package boost (pour + disposer des fichiers d'en-t�te *.hpp). +

    +
    +

    Fichier de configuration + QxOrm.pri (ou QxOrm.cmake)

    +
    + Le fichier de configuration QxOrm.pri (ou QxOrm.cmake) est divis� en plusieurs + sections (chacune �tant comment�e) et regroupe les diff�rents param�trages et options de + compilation disponibles. + Il est fortement recommand� de lire attentivement le fichier de configuration QxOrm.pri + avant de compiler la biblioth�que QxOrm. + Il est possible de conserver le param�trage par d�faut, seule la variable + QX_BOOST_INCLUDE_PATH est n�cessaire si votre projet utilise le framework boost : cette + variable indique o� trouver les fichiers d'en-t�te *.hpp de la biblioth�que boost :
    +
    + + + + + + + +
    +
       isEmpty(QX_BOOST_INCLUDE_PATH) { QX_BOOST_INCLUDE_PATH = $$quote(D:/Dvlp/_Libs/Boost/1_57/include) }   
    +
    +
    + Si vous ne souhaitez pas modifier le fichier de configuration QxOrm.pri, il est possible + de d�finir une variable d'environnement nomm�e BOOST_INCLUDE : cette variable + d'environnement sera alors utilis�e automatiquement pour valoriser QX_BOOST_INCLUDE_PATH + (lire le fichier QxOrm.pri pour plus d'informations). +

    + Voici une liste non exhaustive des diff�rentes options de compilation disponibles (lire le + fichier de configuration QxOrm.pri pour plus de d�tails), aucune n'�tant activ�e par + d�faut : +
      +
    • _QX_ENABLE_BOOST : ajoute une d�pendance aux fichiers d'en-t�tes de boost + (*.hpp), support des classes boost::shared_ptr, boost::optional, + boost::container, etc... ; +
    • +
    • _QX_ENABLE_BOOST_SERIALIZATION : active les fonctionnalit�s + de s�rialisation avec le module boost::serialization. Cette option n�cessite la + compilation du binaire boost::serialization et ajoute donc une d�pendance � QxOrm ;
    • +
      +
    • _QX_ENABLE_QT_GUI : support de la s�rialisation des types du module QtGui : QBrush, + QColor, QFont, QImage, QMatrix, QPicture, QPixmap, QRegion. Cette option ajoute une + d�pendance � QxOrm (QtGui) ;
    • +
    • _QX_ENABLE_QT_NETWORK : active le module QxService pour + transf�rer la couche de donn�es persistante sur le r�seau (application client/serveur). + Cette option ajoute une d�pendance � QxOrm (QtNetwork) ;
    • +
    • _QX_NO_PRECOMPILED_HEADER : d�sactive l'utilisation d'un en-t�te pr�compil� + (permet de r�duire les temps de compilation d'un projet) : cette option est n�cessaire + pour contourner un bug des versions r�centes de MinGW, pour tous les + autres compilateurs il est recommand� de travailler avec un precompiled header ; +
    • +
    • _QX_NO_RTTI : permet de compiler QxOrm et les projets d�pendants sans les + informations de type C++ RTTI ;
    • +
    • _QX_STATIC_BUILD : permet de compiler la biblioth�que QxOrm en mode statique ; +
    • +
    • _QX_UNITY_BUILD : r�duit les temps de compilation de la biblioth�que QxOrm en + utilisant le concept unity build : un seul fichier source all.cpp � + compiler. Il est recommand� d'activer cette option avec CMake (car ne supporte pas nativement les en-t�tes pr�compil�s) ; +
    • +
    • _QX_ENABLE_MONGODB : support de la base de donn�es + MongoDB, la biblioth�que QxOrm devient ainsi un ODM (Object Document Mapper).
    • +
    +
    + Remarque : le fichier de configuration QxOrm.pri (ou QxOrm.cmake) devra + �tre inclus dans tous les projets d�pendants de la biblioth�que QxOrm en ajoutant la ligne + suivante dans le fichier *.pro du projet :
    +
    + + + + + + + +
    +
       include(my_path_to_QxOrm_library/QxOrm.pri)   
    +
    +
    + Autre remarque : � la place de qmake, il est possible d'utiliser l'outil de compilation CMake pour configurer et + construire la biblioth�que QxOrm. + CMake propose un outil graphique afin de visualiser et param�trer les diff�rentes options + disponibles : +

    + QxOrm and CMake +

    +
    +

    Compiler la biblioth�que + QxOrm (avec qmake ou CMake)

    +
    + QxOrm utilise le processus qmake de la biblioth�que Qt pour g�n�rer les + makefile et compiler le projet (il est �galement possible d'utiliser l'outil de compilation CMake, un fichier + CMakeLists.txt �tant fourni avec la biblioth�que QxOrm).
    + qmake est multi-plateforme et fonctionne parfaitement sous Windows, Linux (Unix) et Mac + OS X.
    + Pour compiler QxOrm, il suffit d'ex�cuter les commandes suivantes :
    +
    + + + + + + + +
    +
       qmake
    +   make debug
    +   make release   
    +
    +
    + Sous Windows, des fichiers *.vcproj et *.sln sont disponibles pour les + �diteurs Microsoft Visual C++.
    + Les fichiers *.pro sont lisibles par l'�diteur Qt Creator, et des plugins existent + permettant de s'interfacer avec de nombreux �diteurs C++.
    + Les fichiers mingw_build_all_debug.bat et mingw_build_all_release.bat pr�sents + dans le dossier ./tools/ permettent de compiler rapidement QxOrm ainsi que tous les tests + avec le compilateur MinGW sous Windows.
    + Les fichiers gcc_build_all_debug.sh et gcc_build_all_release.sh pr�sents dans le + dossier ./tools/ permettent de compiler rapidement QxOrm ainsi que tous les tests avec + GCC sous Linux.
    + Enfin, les fichiers osx_build_all_debug.sh et osx_build_all_release.sh pr�sents + dans le dossier ./tools/ permettent de compiler rapidement QxOrm ainsi que tous les tests + sous Mac OS X (merci � Dominique Billet pour l'�criture des scripts).
    +
    +
    +

    Pilotes SQL fournis par + Qt (drivers)

    +
    + QxOrm utilise le moteur QtSql de Qt bas� sur un syst�me de plugin.
    + Une liste d�taill�e des bases de + donn�es support�es est disponible sur le site de Qt.
    + Le plugin ODBC (QODBC) assure une compatibilit� avec de nombreuses bases de + donn�es.
    + Pour des performances optimales, il est conseill� d'utiliser un plugin sp�cifique � une base de + donn�es : +
      +
    • QMYSQL : MySQL ;
    • +
    • QPSQL : PostgreSQL (versions 7.3 and above) ;
    • +
    • QOCI : Oracle Call Interface Driver ;
    • +
    • QSQLITE : SQLite version 3 ;
    • +
    • QDB2 : IBM DB2 (version 7.1 and above) ;
    • +
    • QIBASE : Borland InterBase ;
    • +
    • QTDS : Sybase Adaptive Server.
    • +
    + Remarque : pour se connecter � une base de donn�es Microsoft SQL Server, il est + n�cessaire d'utiliser le pilote ODBC (plugin QODBC). +

    + Autre remarque : la biblioth�que QxOrm supporte �galement la base de + donn�es MongoDB (C++ ODM Object Document Mapper). +

    +
    +
    + +

    Persistance - Object + Relational Mapping (ORM)

    +
    + La biblioth�que QxOrm fournit un moteur de persistance des donn�es bas� sur le module QtSql de Qt. + Ce moteur de persistance utilise la technique de programmation : Object Relational Mapping + (ORM). +

    + D�finition du + site Wikipedia : un mapping objet-relationnel (en anglais object-relational mapping + ou ORM) est une technique de programmation informatique qui cr�e l'illusion d'une base de + donn�es orient�e objet � partir d'une base de donn�es relationnelle en d�finissant des + correspondances entre cette base de donn�es et les objets du langage utilis�. On pourrait le + d�signer par � correspondance entre monde objet et monde relationnel �. + Le mapping objet-relationnel consiste � associer une ou plusieurs classes avec une table, et chaque + attribut de la classe avec un champ de la table. + Les frameworks de mapping objet-relationnel permettent d'�liminer la duplication de code dans les + op�rations CRUD. +

    + Pour effectuer cette correspondance entre le monde objet et le monde relationnel, ainsi pour que + proposer l'ensemble de ses fonctionnalit�s, la biblioth�que QxOrm impose l'enregistrement de + classes C++ dans le contexte QxOrm. + Nous allons donc d�buter ce chapitre de la fa�on suivante : comment enregistrer une classe C++ + dans le contexte QxOrm ? +

    + Remarque : la biblioth�que QxOrm supporte �galement la base de donn�es + MongoDB (C++ ODM Object Document Mapper). +

    +

    D�finir une classe dans + le contexte QxOrm (mapping)

    +
    + Toutes les classes C++ peuvent �tre enregistr�es dans le contexte QxOrm : il n'y a pas besoin de + d�river d'un super objet, et vous pouvez �crire vos m�thodes de classes et accesseurs sans + aucune contrainte. + Enregistrer une classe C++ dans le contexte QxOrm signifie : +
      +
    • dans le fichier en-t�te *.h contenant la d�finition de la classe : utilisation de + la macro QX_REGISTER_HPP(class_name, base_class, class_version) ;
    • +
    • dans le fichier source *.cpp contenant l'impl�mentation de la classe : utilisation + de la macro QX_REGISTER_CPP(class_name) ;
    • +
    • dans le fichier source *.cpp contenant l'impl�mentation de la classe : + sp�cialisation de la fonction template : void + qx::register_class<T>(qx::QxClass<T> & t).
    • +
    + Par exemple, voici comment d�clarer une classe person avec 4 propri�t�s enregistr�es dans + le contexte QxOrm : id, firstName, lastName, birthDate :
    +
    + * Fichier person.h :
    + + + + + + + +
    +
    #ifndef _PERSON_H_
    +#define _PERSON_H_
    +
    +class person
    +{
    +public:
    +   long id;
    +   QString firstName;
    +   QString lastName;
    +   QDateTime birthDate;
    +
    +   person() : id(0) { ; }
    +   virtual ~person() { ; }
    +};
    +
    +QX_REGISTER_HPP_MY_TEST_EXE(person, qx::trait::no_base_class_defined, 0)
    +
    +/* This macro is necessary to register 'person' class in QxOrm context */
    +/* param 1 : the current class to register => 'person' */
    +/* param 2 : the base class, if no base class, use the qx trait => 'qx::trait::no_base_class_defined' */
    +/* param 3 : the class version used by serialization engine to provide 'ascendant compatibility' */
    +
    +#endif // _PERSON_H_
    +
    +
    + * Fichier person.cpp :
    + + + + + + + +
    +
    #include "precompiled.h"   // Precompiled-header with '#include <QxOrm.h>' and '#include "export.h"'
    +#include "person.h"          // Class definition 'person'
    +#include <QxOrm_Impl.h>     // Automatic memory leak detection and boost serialization export macro
    +
    +QX_REGISTER_CPP_MY_TEST_EXE(person)   // This macro is necessary to register 'person' class in QxOrm context
    +
    +namespace qx {
    +template <> void register_class(QxClass<person> & t)
    +{
    +  t.setName("t_person");               // 'person' C++ class is mapped to 't_person' database table
    +
    +  t.id(& person::id, "id");               // Register 'person::id' <=> primary key in your database
    +  t.data(& person::firstName, "first_name");      // Register 'person::firstName' property mapped to 'first_name' database column name
    +  t.data(& person::lastName, "last_name");  // Register 'person::lastName' property mapped to 'last_name' database column name
    +  t.data(& person::birthDate, "birth_date");  // Register 'person::birthDate' property mapped to 'birth_date' database column name
    +}}
    +
    +

    + Remarque : les m�thodes qx::QxClass<T>::id() et qx::QxClass<T>::data() retournent une instance de type : qx::IxDataMember + (classe de base pour l'enregistrement des donn�es membre). + Gr�ce � cette instance, il est possible de personnaliser le comportement par d�faut propos� par + la classe qx::IxDataMember, comme par exemple dans le chapitre : D�finir une donn�e membre transient. +

    + Autre remarque : il est �galement possible d'enregistrer des m�thodes de classe dans le + contexte QxOrm (gestion des m�thodes static et non static) avec les m�thodes qx::QxClass<T>::fct_0(), qx::QxClass<T>::fct_1(), etc... + Cette fonctionnalit� fait partie du moteur d'introspection de la + biblioth�que QxOrm, plus de d�tails dans le chapitre : Appeler + dynamiquement une fonction. +

    +

    Cl� primaire autre + que le type par d�faut "long"

    +
    + Par d�faut, lorsqu'un mapping d'une classe C++ est �crit avec la m�thode void + qx::register_class<T>, l'identifiant associ� � la classe est de type long + (cl� primaire avec auto-incr�mentation dans la base de donn�es).
    +
    + Il est possible de d�finir un identifiant d'un autre type en utilisant la macro + QX_REGISTER_PRIMARY_KEY.
    + Cette macro sp�cialise le template qx::trait::get_primary_key<T> pour associer + un type d'identifiant � une classe C++.
    +
    + Par exemple, pour d�finir un identifiant unique de type QString pour la classe C++ + myClass (mapp�e vers une table de la BDD avec une colonne de type VARCHAR pour + cl� primaire), il suffit d'�crire : + QX_REGISTER_PRIMARY_KEY(myClass, QString)
    +
    + Voici un exemple d'utilisation de la macro QX_REGISTER_PRIMARY_KEY avec une classe + author poss�dant un identifiant de type QString :
    +
    + + + + + + + +
    +
    #ifndef _QX_BLOG_AUTHOR_H_
    +#define _QX_BLOG_AUTHOR_H_
    + 
    +class author
    +{
    +public:
    +// -- propri�t�s
    +   QString  m_id;
    +   QString  m_name;
    +// -- constructeur, destructeur virtuel
    +   author() { ; }
    +   virtual ~author() { ; }
    +};
    +
    +QX_REGISTER_PRIMARY_KEY(author, QString)
    +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0)
    +
    +#endif // _QX_BLOG_AUTHOR_H_
    +
    +

    +
    +

    Cl� primaire sur + plusieurs colonnes (composite key)

    +
    + QxOrm supporte la notion de 'multi-columns primary key'.
    + L'identifiant de la classe doit �tre du type suivant : +
      +
    • QPair ou std::pair pour d�finir deux colonnes ; +
    • +
    • boost::tuple (ou std::tuple) pour d�finir de deux � neuf colonnes. +
    • +
    + Il est n�cessaire d'utiliser la macro QX_REGISTER_PRIMARY_KEY() pour + sp�cialiser le template et ainsi d�finir le type d'identifiant sur plusieurs colonnes.
    + La liste des noms des colonnes doit �tre de la forme suivante : + 'column1|column2|column3|etc.'.
    +
    + Exemple d'utilisation avec la classe 'author' du projet + 'qxBlogCompositeKey', cette classe poss�de un identifiant sur trois colonnes :
    +
    + + + + + + + +
    +
    #ifndef _QX_BLOG_AUTHOR_H_
    +#define _QX_BLOG_AUTHOR_H_
    +
    +class blog;
    +
    +class QX_BLOG_DLL_EXPORT author
    +{
    +
    +   QX_REGISTER_FRIEND_CLASS(author)
    +
    +public:
    +
    +// -- cl� compos�e (cl� primaire d�finie sur plusieurs colonnes dans la base de donn�es)
    +   typedef boost::tuple<QString, long, QString> type_composite_key;
    +   static QString str_composite_key() { return "author_id_0|author_id_1|author_id_2"; }
    +
    +// -- typedef
    +   typedef boost::shared_ptr<blog> blog_ptr;
    +   typedef std::vector<blog_ptr> list_blog;
    +
    +// -- enum
    +   enum enum_sex { male, female, unknown };
    +
    +// -- propri�t�s
    +   type_composite_key   m_id;
    +   QString              m_name;
    +   QDate                m_birthdate;
    +   enum_sex             m_sex;
    +   list_blog            m_blogX;
    +
    +// -- constructeur, destructeur virtuel
    +   author() : m_id("", 0, ""), m_sex(unknown) { ; }
    +   virtual ~author() { ; }
    +
    +// -- m�thodes
    +   int age() const;
    +
    +// -- m�thodes d'acc�s � la cl� compos�e
    +   type_composite_key getId() const    { return m_id; }
    +   QString getId_0() const             { return boost::tuples::get<0>(m_id); }
    +   long getId_1() const                { return boost::tuples::get<1>(m_id); }
    +   QString getId_2() const             { return boost::tuples::get<2>(m_id); }
    +
    +// -- m�thodes de modification de la cl� compos�e
    +   void setId_0(const QString & s)     { boost::tuples::get<0>(m_id) = s; }
    +   void setId_1(long l)                { boost::tuples::get<1>(m_id) = l; }
    +   void setId_2(const QString & s)     { boost::tuples::get<2>(m_id) = s; }
    +
    +};
    +
    +QX_REGISTER_PRIMARY_KEY(author, author::type_composite_key)
    +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0)
    +
    +typedef boost::shared_ptr<author> author_ptr;
    +typedef qx::QxCollection<author::type_composite_key, author_ptr> list_author;
    +
    +#endif // _QX_BLOG_AUTHOR_H_
    +
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +#include "../include/author.h"
    +#include "../include/blog.h"
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_BLOG(author)
    +
    +namespace qx {
    +template <> void register_class(QxClass<author> & t)
    +{
    +   t.id(& author::m_id, author::str_composite_key());
    +
    +   t.data(& author::m_name, "name");
    +   t.data(& author::m_birthdate, "birthdate");
    +   t.data(& author::m_sex, "sex");
    +
    +   t.relationOneToMany(& author::m_blogX, blog::str_composite_key(), author::str_composite_key());
    +
    +   t.fct_0<int>(& author::age, "age");
    +}}
    +
    +int author::age() const
    +{
    +   if (! m_birthdate.isValid()) { return -1; }
    +   return (QDate::currentDate().year() - m_birthdate.year());
    +}
    +
    +
    +

    +
    +

    Donn�es membres + public/protected/private

    +
    + Pour enregistrer des membres private ou protected dans le contexte QxOrm + (fonction qx::register_class<T>), il faut d�clarer les friend class + n�cessaires.
    + Pour simplifier l'�criture avec les template C++, la biblioth�que QxOrm fournit la + macro suivante : QX_REGISTER_FRIEND_CLASS(myClass).
    + Un exemple d'utilisation se trouve dans le dossier ./test/qxDllSample/dll1/ du package + QxOrm avec la classe CPerson :
    +
    + + + + + + + +
    +
    namespace qx {
    +namespace test {
    +
    +class QX_DLL1_EXPORT CPerson : public QObject
    +{
    +
    +   Q_OBJECT
    +   QX_REGISTER_FRIEND_CLASS(qx::test::CPerson)
    +
    +   // etc...
    +
    +};
    +
    +} // namespace test
    +} // namespace qx
    +
    +

    +
    +

    Espace de nom + (namespace)

    +
    + Si une classe est d�finie dans un espace de nom (namespace), alors une erreur de + compilation se produit avec l'utilisation des macros : QX_REGISTER_HPP et + QX_REGISTER_CPP. + Pour �viter ces erreurs de compilation, il est n�cessaire d'utiliser les macros suivantes : + QX_REGISTER_COMPLEX_CLASS_NAME_HPP et QX_REGISTER_COMPLEX_CLASS_NAME_CPP.
    +
    + Vous trouverez un exemple d'utilisation dans le dossier ./test/qxDllSample/dll1/ de la + distribution de QxOrm avec la classe CPerson d�finie dans l'espace de nom + qx::test :
    +
    + + + + + + + +
    +
       QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QX_DLL1(qx::test::CPerson, QObject, 0, qx_test_CPerson)   
    +
    +
    + Les macros QX_REGISTER_COMPLEX_CLASS_NAME... n�cessitent un param�tre suppl�mentaire + (dans l'exemple ci-dessus il s'agit du param�tre qx_test_CPerson) afin de cr�er une + variable globale.
    + Celle-ci est appel�e d�s le lancement de l'application.
    + La construction de cette instance globale d�clare la classe dans le + module QxFactory (mod�le de conception fabrique ou design pattern factory).
    + Un objet C++ ne pouvant pas se nommer avec des caract�res "::", le param�tre + suppl�mentaire de la macro permet de remplacer tous les "::" par des "_". +

    +
    +

    Types C++ support�s + par QxOrm

    +
    + La biblioth�que QxOrm supporte la plupart des types primitifs du standard C++ et du framework + Qt (num�riques, bool�ens, chaines de caract�res, date/heure, collections, pointeurs et + pointeurs intelligents, etc...). + Voici un exemple pr�sentant une liste (non exhaustive) de types C++ support�s ainsi que + l'association par d�faut du type de base de donn�es (format SQLite) : +

    + + + + + + + +
    +
    "bool" <-> "SMALLINT"
    +"qx_bool" <-> "SMALLINT"
    +"short" <-> "SMALLINT"
    +"int" <-> "INTEGER"
    +"long" <-> "INTEGER"
    +"long long" <-> "INTEGER"
    +"float" <-> "FLOAT"
    +"double" <-> "FLOAT"
    +"long double" <-> "FLOAT"
    +"unsigned short" <-> "SMALLINT"
    +"unsigned int" <-> "INTEGER"
    +"unsigned long" <-> "INTEGER"
    +"unsigned long long" <-> "INTEGER"
    +"std::string" <-> "TEXT"
    +"std::wstring" <-> "TEXT"
    +"QString" <-> "TEXT"
    +"QVariant" <-> "TEXT"
    +"QUuid" <-> "TEXT"
    +"QDate" <-> "DATE"
    +"QTime" <-> "TIME"
    +"QDateTime" <-> "TIMESTAMP"
    +"QByteArray" <-> "BLOB"
    +"qx::QxDateNeutral" <-> "TEXT"
    +"qx::QxTimeNeutral" <-> "TEXT"
    +"qx::QxDateTimeNeutral" <-> "TEXT"
    +
    +
    + Remarque : il est �galement possible de persister un type non g�r� par d�faut par la + biblioth�que QxOrm. Rendez-vous au chapitre Persister des types + personnalis�s pour plus de d�tails sur cette fonctionnalit�. +

    + Autre remarque : concernant l'association d'un type C++ avec le type de base de + donn�es associ�, rendez-vous au chapitre Associer un type SQL � + une classe C++ pour plus de d�tails. +

    +
    +

    D�finir une donn�e + membre transient

    +
    + Une donn�e membre transient n'est pas associ�e � une colonne d'une table de la base de + donn�es. + Le module QxDao ignore donc + cette propri�t� pour toutes les requ�tes � la base de donn�es. +

    + A quoi sert l'enregistrement d'une donn�e membre transient dans le contexte QxOrm + ?
    + Enregistrer une donn�e membre transient dans le contexte QxOrm permet de disposer des + autres fonctionnalit�s de la biblioth�que QxOrm sur cette propri�t�, comme par exemple : s�rialisation, introspection, etc... +

    + La m�thode qx::QxClass<T>::data() dispose d'un param�tre optionnel nomm� : + bool bDao (par d�faut, valeur � true). + Par exemple, ajoutons une propri�t� transient nomm�e age � la classe + person (cette propri�t� n'a pas besoin d'�tre stock�e en base de donn�es puisque nous + disposons d�j� de la propri�t� birthDate) : +

    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<person> & t)
    +{
    +  t.id(& person::id, "id");
    +  t.data(& person::firstName, "first_name";);
    +  t.data(& person::lastName, "last_name");
    +  t.data(& person::birthDate, "birth_date");
    +  t.data(& person::age, "age", 0, true, false);
    +}}
    +
    +
    + Voici une autre fa�on de d�finir une propri�t� transient en r�cup�rant l'instance de + type qx::IxDataMember : +

    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<person> & t)
    +{
    +  t.id(& person::id, "id");
    +  t.data(& person::firstName, "first_name";);
    +  t.data(& person::lastName, "last_name");
    +  t.data(& person::birthDate, "birth_date");
    +
    +  IxDataMember * pDataMember = t.data(& person::age, "age");
    +  pDataMember->setDao(false);
    +}}
    +
    +

    +
    +
    +

    Connexion � la base de + donn�es

    +
    + La connexion � la base de donn�es peut �tre param�tr�e avec la classe singleton : qx::QxSqlDatabase.
    + Voici un exemple de param�trage � une base de donn�es SQLite nomm�e test_qxorm.db : +

    + + + + + + + +
    +
       // Init parameters to connect to database
    +   qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE");
    +   qx::QxSqlDatabase::getSingleton()->setDatabaseName("./test_qxorm.db");
    +   qx::QxSqlDatabase::getSingleton()->setHostName("localhost");
    +   qx::QxSqlDatabase::getSingleton()->setUserName("root");
    +   qx::QxSqlDatabase::getSingleton()->setPassword("");
    +
    +
    +
    + Une fois les param�tres de connexion renseign�s dans la classe singleton qx::QxSqlDatabase, toutes les op�rations avec la base de donn�es + effectu�es par la biblioth�que QxOrm utiliserons ces param�tres. + Pour plus d'informations sur les param�tres de connexion � renseigner, il est recommand� de lire + la documentation de la classe + QSqlDatabase du framework Qt. +

    + Remarque : la classe qx::QxSqlDatabase g�re automatiquement les appels � la base de + donn�es dans diff�rents threads (multi-threading). +

    + Autre remarque : il est possible de g�rer son propre pool de connexions � la base de + donn�es, et de travailler �galement avec plusieurs bases de donn�es distinctes : rendez-vous + dans le chapitre Travailler avec plusieurs bases de + donn�es pour plus d'informations sur cette fonctionnalit�. +

    + Autre remarque : suivant le pilote SQL renseign� dans les param�tres de connexion, la + biblioth�que QxOrm associe automatiquement un g�n�rateur SQL. + Ce g�n�rateur SQL permet de g�rer les sp�cificit�s propres � chaque type de base de donn�es. + Tous les g�n�rateurs SQL h�ritent de la classe de base : qx::dao::detail::IxSqlGenerator : + + + + + + + + +
    +
       qx::dao::detail::IxSqlGenerator_ptr pSqlGenerator;
    +   pSqlGenerator.reset(new qx::dao::detail::QxSqlGenerator_MSSQLServer());   
    +   qx::QxSqlDatabase::getSingleton()->setSqlGenerator(pSqlGenerator);   
    +
    +

    +
    +

    Sauvegarder une instance + C++ en base de donn�es (insert/update)

    +
    + Toutes les fonctions li�es � la base de donn�es sont disponibles dans l'espace de nom + qx::dao.
    +
    + Pour sauvegarder une instance C++ (ou une liste d'instances C++) en base de donn�es, la + biblioth�que QxOrm fournit les fonctions suivantes : +
      +
    • qx::dao::insert : ins�re une instance (ou une liste + d'instances) en base de donn�es ;
    • +
    • qx::dao::insert_with_relation : ins�re une instance (ou une + liste d'instances) + ses relations en base de donn�es ;
    • +
    • qx::dao::insert_with_all_relation : ins�re une instance (ou + une liste d'instances) + toutes ses relations en base de donn�es ;
    • +
      +
    • qx::dao::update : met � jour une instance (ou une liste + d'instances) en base de donn�es ;
    • +
    • qx::dao::update_with_relation : met � jour une instance (ou + une liste d'instances) + ses relations en base de donn�es ;
    • +
    • qx::dao::update_with_all_relation : met � jour une instance + (ou une liste d'instances) + toutes ses relations en base de donn�es ;
    • +
    • qx::dao::update_by_query : met � jour une instance (ou une + liste d'instances) en base de donn�es en filtrant avec une requ�te SQL ;
    • +
    • qx::dao::update_by_query_with_relation : met � jour une + instance (ou une liste d'instances) + ses relations en base de donn�es en filtrant avec + une requ�te SQL ;
    • +
    • qx::dao::update_by_query_with_all_relation : met � jour une + instance (ou une liste d'instances) + toutes ses relations en base de donn�es en filtrant + avec une requ�te SQL ;
    • +
    • qx::dao::update_optimized : met � jour uniquement les champs + modifi�s d'une instance (ou d'une liste d'instances) en base de donn�es en utilisant le + pattern is dirty et les fonctionnalit�s de la classe qx::dao::ptr ;
    • +
    • qx::dao::update_optimized_by_query : met � jour uniquement + les champs modifi�s d'une instance (ou d'une liste d'instances) en base de donn�es en + utilisant le pattern is dirty et les fonctionnalit�s de la classe qx::dao::ptr et en filtrant avec une requ�te SQL ;
    • +
      +
    • qx::dao::save : ins�re (si l'�l�ment n'existe pas en base de + donn�es) ou met � jour (si l'�l�ment existe d�j� en base de donn�es) ;
    • +
    • qx::dao::save_with_relation : ins�re (si l'�l�ment n'existe + pas en base de donn�es) ou met � jour (si l'�l�ment existe d�j� en base de donn�es) + ses + relations ;
    • +
    • qx::dao::save_with_all_relation : ins�re (si l'�l�ment + n'existe pas en base de donn�es) ou met � jour (si l'�l�ment existe d�j� en base de + donn�es) + toutes ses relations ;
    • +
    • qx::dao::save_with_relation_recursive : ins�re (si l'�l�ment + n'existe pas en base de donn�es) ou met � jour (si l'�l�ment existe d�j� en base de + donn�es) + toutes les relations sur tous les niveaux : utile pour sauvegarder en 1 + commande une structure en arbre par exemple.
    • +
    +
    + Par exemple :
    + + + + + + + +
    +
       // Create 3 drugs instances
    +   // It is possible to use 'boost' and 'Qt' smart pointer : 'boost::shared_ptr', 'QSharedPointer', etc...
    +   typedef boost::shared_ptr<drug> drug_ptr;
    +   drug_ptr d1; d1.reset(new drug()); d1->name = "name1"; d1->description = "desc1";
    +   drug_ptr d2; d2.reset(new drug()); d2->name = "name2"; d2->description = "desc2";
    +   drug_ptr d3; d3.reset(new drug()); d3->name = "name3"; d3->description = "desc3";
    +
    +   // Insert some drugs into a container
    +   // It is possible to use many containers from 'std', 'boost', 'Qt' and 'qx::QxCollection<Key, Value>'
    +   typedef std::vector<drug_ptr> type_lst_drug;
    +   type_lst_drug lst_drug;
    +   lst_drug.push_back(d1);
    +   lst_drug.push_back(d2);
    +   lst_drug.push_back(d3);
    +
    +   // Insert drugs from container to database
    +   // 'id' property of 'd1', 'd2' and 'd3' are auto-updated
    +   QSqlError daoError = qx::dao::insert(lst_drug);
    +
    +   // Modify and update the second drug into database
    +   d2->name = "name2 modified";
    +   d2->description = "desc2 modified";
    +   daoError = qx::dao::update(d2);
    +
    +
    +

    + Remarque : toutes les fonctions de l'espace de nom qx::dao sont flexibles au niveau des param�tres, elles + peuvent accepter : une instance, une liste d'instances, un pointeur, un pointeur intelligent, + une liste de pointeurs, une liste de pointeurs intelligents, etc... Par exemple : +
      +
    • my_entity t;     /* ... */     + qx::dao::insert(t);
    • +
    • my_entity * t;     /* ... */     + qx::dao::insert(t);
    • +
    • std::shared_ptr<my_entity> t;     /* ... + */     qx::dao::insert(t);
    • +
    • QList<my_entity> lst;     /* ... */     + qx::dao::insert(lst);
    • +
    • QList<std::shared_ptr<my_entity> > lst;     /* ... + */     qx::dao::insert(lst);
    • +
    + Pour connaitre la liste des collections support�es, rendez-vous dans le chapitre : Collections support�es par QxOrm.
    + Pour connaitre la liste des pointeurs intelligents support�s, rendez-vous dans le chapitre : Pointeurs intelligents support�s par QxOrm (smart-pointers).
    +

    +
    +

    Supprimer une instance + C++ de la base de donn�es (delete)

    +
    + Toutes les fonctions li�es � la base de donn�es sont disponibles dans l'espace de nom + qx::dao.
    +
    + Pour supprimer une instance C++ (ou une liste d'instances C++) en base de donn�es, la + biblioth�que QxOrm fournit les fonctions suivantes : + +
    + Par exemple :
    + + + + + + + +
    +
       // Create a drug instance with id '18'
    +   drug d; d.setId(18);
    +
    +   // Delete the drug with id '18' from database
    +   QSqlError daoError = qx::dao::delete_by_id(d);
    +
    +   // Delete all drugs from database
    +   daoError = qx::dao::delete_all<drug>();
    +
    +

    +

    Suppression logique + (soft delete)

    +
    + Une suppression logique permet de ne pas effacer de ligne dans une table d'une base de + donn�es (contrairement � une suppression physique) : une colonne suppl�mentaire est ajout�e � + la d�finition de la table pour indiquer que la ligne est supprim�e ou non.
    + Cette colonne peut contenir soit un bool�en (1 signifie ligne supprim�e, 0 ou vide signifie + ligne non supprim�e), soit la date-heure de suppression de la ligne (si vide, la ligne est + consid�r�e comme non supprim�e).
    + Il est donc � tout moment possible de r�activer une ligne supprim�e en r�initialisant la + valeur � vide dans la table de la base de donn�es.
    +
    + Pour activer le m�canisme de suppression logique avec la biblioth�que QxOrm, il faut utiliser + la classe qx::QxSoftDelete dans la fonction de mapping + qx::register_class<T>.
    + Voici un exemple d'utilisation avec une classe Bar contenant deux propri�t�s + m_id et m_desc :
    +
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<Bar> & t)
    +{
    +   t.setSoftDelete(qx::QxSoftDelete("deleted_at"));
    +
    +   t.id(& Bar::m_id, "id");
    +   t.data(& Bar::m_desc, "desc");
    +}}
    +
    +
    + Les requ�tes SQL g�n�r�es automatiquement par la biblioth�que QxOrm vont prendre en compte ce + param�tre de suppression logique pour ajouter les conditions n�cessaires (ne pas r�cup�rer + les �l�ments supprim�s, ne pas supprimer physiquement une ligne, etc.).
    + Par exemple, si vous ex�cutez les lignes suivantes avec la classe Bar :
    +
    + + + + + + + +
    +
    Bar_ptr pBar; pBar.reset(new Bar());
    +pBar->setId(5);
    +QSqlError daoError = qx::dao::delete_by_id(pBar);     qAssert(! daoError.isValid());
    +qx_bool bDaoExist = qx::dao::exist(pBar);             qAssert(! bDaoExist);
    +daoError = qx::dao::delete_all<Bar>();                qAssert(! daoError.isValid());
    +long lBarCount = qx::dao::count<Bar>();               qAssert(lBarCount == 0);
    +daoError = qx::dao::destroy_all<Bar>();               qAssert(! daoError.isValid());
    +
    +
    + Vous obtiendrez les traces suivantes :
    +
    + + + + + + + +
    +
    [QxOrm] sql query (93 ms) : UPDATE Bar SET deleted_at = '20110617115148615' WHERE id = :id
    +[QxOrm] sql query (0 ms) : SELECT Bar.id AS Bar_id_0, Bar.deleted_at FROM Bar WHERE Bar.id = :id 
    +                                         AND (Bar.deleted_at IS NULL OR Bar.deleted_at = '')
    +[QxOrm] sql query (78 ms) : UPDATE Bar SET deleted_at = '20110617115148724'
    +[QxOrm] sql query (0 ms) : SELECT COUNT(*) FROM Bar WHERE (Bar.deleted_at IS NULL OR Bar.deleted_at = '')
    +[QxOrm] sql query (110 ms) : DELETE FROM Bar
    +
    +
    + Remarque : pour supprimer physiquement une ligne de la base de donn�es, il faut + utiliser les fonctions : qx::dao::destroy_by_id() et + qx::dao::destroy_all().
    +
    + Autre remarque : il peut �tre int�ressant de d�finir au niveau du SGBD un index sur la + colonne deleted_at (ou peu importe le nom que vous donnez) afin d'acc�l�rer + l'ex�cution des requ�tes SQL.
    +
    +
    +
    +

    R�cup�rer une instance + C++ de la base de donn�es (fetch)

    +
    + Toutes les fonctions li�es � la base de donn�es sont disponibles dans l'espace de nom + qx::dao.
    +
    + Pour valoriser automatiquement les propri�t�s d'une instance C++ (ou d'une liste d'instances + C++) en fonction des donn�es d'une table (ou plusieurs tables si des relations sont d�finies) de + la base de donn�es, la biblioth�que QxOrm fournit les fonctions suivantes : +
      +
    • qx::dao::fetch_by_id : r�cup�re de la base de donn�es + l'�l�ment (ou une liste d'�l�ments) associ� � l'id pass� en param�tre ;
    • +
    • qx::dao::fetch_by_id_with_relation : r�cup�re de la base de + donn�es l'�l�ment (ou une liste d'�l�ments) + ses relations en fonction de l'id pass� en + param�tre ;
    • +
    • qx::dao::fetch_by_id_with_all_relation : r�cup�re de la base + de donn�es l'�l�ment (ou une liste d'�l�ments) + toutes ses relations en fonction de l'id + pass� en param�tre ;
    • +
      +
    • qx::dao::fetch_all : r�cup�re toutes les entr�es d'une table + de la base de donn�es ;
    • +
    • qx::dao::fetch_all_with_relation : r�cup�re toutes les + entr�es d'une table + ses relations de la base de donn�es ;
    • +
    • qx::dao::fetch_all_with_all_relation : r�cup�re toutes les + entr�es d'une table + toutes ses relations de la base de donn�es ;
    • +
      +
    • qx::dao::fetch_by_query : r�cup�re toutes les entr�es d'une + table de la base de donn�es en fonction d'une requ�te SQL ;
    • +
    • qx::dao::fetch_by_query_with_relation : r�cup�re toutes les + entr�es d'une table de la base de donn�es + ses relations en fonction d'une requ�te SQL ; +
    • +
    • qx::dao::fetch_by_query_with_all_relation : r�cup�re toutes + les entr�es d'une table de la base de donn�es + toutes ses relations en fonction d'une + requ�te SQL ;
    • +
      +
    • qx::dao::exist : teste l'existence d'un �l�ment (ou d'une + liste d'�l�ments) en base de donn�es en fonction de son identifiant (primary key). +
    • +
    +
    + Par exemple :
    + + + + + + + +
    +
       // Fetch drug with id '3' into a new variable
    +   drug_ptr d; d.reset(new drug());
    +   d->id = 3;
    +   QSqlError daoError = qx::dao::fetch_by_id(d);
    +
    +

    +
    +

    Requ�tes SQL

    +
    + La biblioth�que QxOrm fournit plusieurs outils pour effectuer des requ�tes � la base de donn�es + : + + Remarque : QxOrm �tant bas� sur le module QtSql de Qt, il est + toujours possible de requ�ter la base de donn�es en utilisant la classe QSqlQuery de Qt si les + fonctionnalit�s propos�es par QxOrm ne sont pas suffisantes. +

    +

    Utilisation de la + classe qx::QxSqlQuery (ou son alias qx_query)

    +
    + La classe qx::QxSqlQuery (ou bien son alias qx_query) permet + d'interroger la base de donn�es (trier, filtrer, etc.) de deux mani�res diff�rentes : + + Le principal avantage de la premi�re m�thode (�criture manuelle des requ�tes SQL) est de + pouvoir utiliser certaines optimisations sp�cifiques � chaque base de donn�es.
    + La deuxi�me m�thode (utilisation du code C++ pour g�n�rer la requ�te SQL) permet de mapper + automatiquement les param�tres SQL sans utiliser la fonction + qx::QxSqlQuery::bind().
    +
    + Voici un exemple d'utilisation de la classe qx::QxSqlQuery avec �criture manuelle + d'une requ�te SQL :
    +
    + + + + + + + +
    +
    // Construit une requ�te pour r�cup�rer uniquement les 'author' de type 'female'
    +qx::QxSqlQuery query("WHERE author.sex = :sex");
    +query.bind(":sex", author::female);
    +
    +QList<author> list_of_female;
    +QSqlError daoError = qx::dao::fetch_by_query(query, list_of_female);
    +for (long l = 0; l < list_of_female.count(); l++)
    +{ /* traitement avec la collection issue de la base de donn�es */ }
    +
    +
    + La biblioth�que QxOrm supporte trois syntaxes pour l'�criture des param�tres SQL.
    + Le type de syntaxe peut �tre modifi� de fa�on globale � un projet en utilisant la m�thode + suivante : qx::QxSqlDatabase::getSingleton()->setSqlPlaceHolderStyle().
    + Les trois param�tres possibles pour cette m�thode sont : +
      +
    • ph_style_2_point_name : "WHERE author.sex = :sex" (syntaxe par d�faut) ;
    • +
    • ph_style_at_name : "WHERE author.sex = @sex" ;
    • +
    • ph_style_question_mark : "WHERE author.sex = ?".
    • +
    + Voici le m�me exemple en utilisant les m�thodes C++ de la classe qx::QxSqlQuery (ou + bien son alias qx_query) pour g�n�rer la requ�te automatiquement :
    +
    + + + + + + + +
    +
    // Construit une requ�te pour r�cup�rer uniquement les 'author' de type 'female'
    +qx_query query;
    +query.where("author.sex").isEqualTo(author::female);
    +
    +QList<author> list_of_female;
    +QSqlError daoError = qx::dao::fetch_by_query(query, list_of_female);
    +for (long l = 0; l < list_of_female.count(); l++)
    +{ /* traitement avec la collection issue de la base de donn�es */ }
    +
    +
    + Cette utilisation de la classe qx::QxSqlQuery pr�sente l'avantage de ne pas avoir � + mapper les param�tres de la requ�te, tout en restant tr�s proche de l'�criture manuelle d'une + requ�te SQL.
    + Les param�tres seront automatiquement inject�s en utilisant la syntaxe d�finie de mani�re + globale par la m�thode : + qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle().
    +
    + Voici un exemple pr�sentant diff�rentes m�thodes disponibles avec la classe + qx::QxSqlQuery (ou bien son alias qx_query) :
    +
    + + + + + + + +
    +
    qx_query query;
    +query.where("sex").isEqualTo(author::female)
    +     .and_("age").isGreaterThan(38)
    +     .or_("last_name").isNotEqualTo("Dupont")
    +     .or_("first_name").like("Alfred")
    +     .and_OpenParenthesis("id").isLessThanOrEqualTo(999)
    +     .and_("birth_date").isBetween(date1, date2)
    +     .closeParenthesis()
    +     .or_("id").in(50, 999, 11, 23, 78945)
    +     .and_("is_deleted").isNotNull()
    +     .orderAsc("last_name", "first_name", "sex")
    +     .limit(50, 150);
    +
    +
    + Ce qui produira le code SQL suivant pour les bases de donn�es MySQL, PostgreSQL + et SQLite (pour Oracle et SQLServer, le traitement de la m�thode + limit() est diff�rent) :
    +
    + + + + + + + +
    +
    WHERE sex = :sex_1_0 
    +AND age > :age_3_0 
    +OR last_name <> :last_name_5_0 
    +OR first_name LIKE :first_name_7_0 
    +AND ( id <= :id_10_0 AND birth_date BETWEEN :birth_date_12_0_1 AND :birth_date_12_0_2 ) 
    +OR id IN (:id_15_0_0, :id_15_0_1, :id_15_0_2, :id_15_0_3, :id_15_0_4) 
    +AND is_deleted IS NOT NULL 
    +ORDER BY last_name ASC, first_name ASC, sex ASC 
    +LIMIT :limit_rows_count_19_0 OFFSET :offset_start_row_19_0
    +
    +
    + Voici la liste des fonctions et m�thodes disponibles pour utiliser la classe + qx::QxSqlQuery (ou bien son alias qx_query) :
    +
    + + + + + + + +
    +
    // avec les fonctions du namespace qx::dao
    +qx::dao::count<T>()
    +qx::dao::fetch_by_query<T>()
    +qx::dao::update_by_query<T>()
    +qx::dao::delete_by_query<T>()
    +qx::dao::destroy_by_query<T>()
    +qx::dao::fetch_by_query_with_relation<T>()
    +qx::dao::fetch_by_query_with_all_relation<T>()
    +qx::dao::update_by_query_with_relation<T>()
    +qx::dao::update_by_query_with_all_relation<T>()
    +qx::dao::update_optimized_by_query<T>()
    +
    +// avec la classe qx::QxSession
    +qx::QxSession::count<T>()
    +qx::QxSession::fetchByQuery<T>()
    +qx::QxSession::update<T>()
    +qx::QxSession::deleteByQuery<T>()
    +qx::QxSession::destroyByQuery<T>()
    +
    +// avec la classe qx::QxRepository<T>
    +qx::QxRepository<T>::count()
    +qx::QxRepository<T>::fetchByQuery()
    +qx::QxRepository<T>::update()
    +qx::QxRepository<T>::deleteByQuery()
    +qx::QxRepository<T>::destroyByQuery()
    +
    +
    + Remarque : certaines de ces fonctions ont �galement deux autres param�tres optionnels + : +
      +
    • const QStringList & columns : pour indiquer la liste des colonnes � r�cup�rer + (par d�faut, toutes les colonnes sont r�cup�r�es) ;
    • +
    • const QStringList & relation : pour indiquer les jointures (one-to-one, + one-to-many, many-to-one et many-to-many d�finies dans la fonction + de mapping void qx::register_class<T>()) entre les tables de la base de + donn�es (par d�faut, aucune relation). +
    • +
    +
    +
    +

    Appel de proc�dure + stock�e ou requ�te SQL personnalis�e

    +
    + La biblioth�que QxOrm fournit deux fonctions pour appeler une proc�dure stock�e ou une + requ�te SQL personnalis�e : + + Le premier param�tre de ces deux fonctions, de type qx::QxSqlQuery (ou son alias qx_query), correspond � la + proc�dure stock�e ou � la requ�te SQL personnalis�e.
    + Pour plus d'informations sur la classe qx::QxSqlQuery, rendez-vous sur ce chapitre du manuel + utilisateur : Utilisation de la classe qx::QxSqlQuery (ou son alias + qx_query).
    +
    + La fonction qx::dao::execute_query<T>() est une fonction template : + le type T doit �tre enregistr� dans le contexte QxOrm (fonction + qx::register_class<T>).
    + Toutes les donn�es renvoy�es par la proc�dure stock�e ou la requ�te SQL personnalis�e qui + pourront �tre associ�es aux membres des classes C++ (de type T) seront valoris�es + automatiquement.
    + Une recherche automatique est effectu�e sur le nom des champs associ�s aux donn�es.
    + Voici un exemple d'utilisation (disponible dans le projet qxBlog du package QxOrm) :
    +
    + + + + + + + +
    +
    // Call a custom SQL query or a stored procedure and fetch automatically properties (with a collection of items)
    +qx_query testStoredProcBis("SELECT * FROM author");
    +daoError = qx::dao::execute_query(testStoredProcBis, authorX);
    +qAssert(! daoError.isValid()); qAssert(authorX.count() > 0);
    +qx::dump(authorX);
    +
    +

    + La fonction qx::dao::call_query() n'est pas une fonction template : les + r�sultats de la requ�te doivent �tre parcourus manuellement sur la classe qx::QxSqlQuery (ou qx_query).
    + Pour r�cup�rer un param�tre de sortie (qui doit �tre pass� � la requ�te en tant que + QSql::Out ou QSql::InOut), il suffit d'utiliser la m�thode : QVariant + qx::QxSqlQuery::boundValue(const QString & sKey) const;.
    +
    + Pour parcourir la liste des r�sultats de la requ�te, il faut utiliser les m�thodes suivantes + : +
      +
    • long qx::QxSqlQuery::getSqlResultRowCount() const;
    • +
    • long qx::QxSqlQuery::getSqlResultColumnCount() const;
    • +
    • QVariant qx::QxSqlQuery::getSqlResultAt(long row, long column) const;
    • +
    • QVariant qx::QxSqlQuery::getSqlResultAt(long row, const QString & column) + const;
    • +
    • QVector qx::QxSqlQuery::getSqlResultAllColumns() const;
    • +
    • void qx::QxSqlQuery::dumpSqlResult();
    • +
    + Voici un exemple d'utilisation avec la fonction qx::dao::call_query() + :
    +
    + + + + + + + +
    +
    qx_query query("CALL MyStoredProc(:param1, :param2)");
    +query.bind(":param1", "myValue1");
    +query.bind(":param2", 5024, QSql::InOut);
    +QSqlError daoError = qx::dao::call_query(query);
    +QVariant vNewValue = query.boundValue(":param2");
    +query.dumpSqlResult();
    +
    +

    +
    +
    +

    Transactions (commit, + rollback, session)

    +
    + Une transaction est une suite d'op�rations effectu�es comme une seule + unit� logique de travail.
    + Une fois termin�e, la transaction est : +
      +
    • soit valid�e (commit), alors toutes les modifications sont faites dans la base de + donn�es ;
    • +
    • soit annul�e (rollback), alors toutes les modifications ne sont pas enregistr�e. +
    • +
    + La classe qx::QxSession de la biblioth�que QxOrm permet de g�rer + automatiquement les transactions (validation, annulation) en utilisant le m�canisme C++ RAII :
    +
    + + + + + + + +
    +
    { // Ouverture d'un scope o� une session sera instanci�e
    +
    +  // Cr�ation d'une session : une connexion valide � la BDD est assign�e � la session et une transaction est d�marr�e
    +  qx::QxSession session;
    +
    +  // Ex�cution d'une s�rie d'op�rations avec la BDD (en utilisant l'op�rateur += de la classe qx::QxSession et la connexion de la session)
    +  session += qx::dao::insert(my_object, session.database());
    +  session += qx::dao::update(my_object, session.database());
    +  session += qx::dao::fetch_by_id(my_object, session.database());
    +  session += qx::dao::delete_by_id(my_object, session.database());
    +
    +  // Si la session n'est pas valide (donc une erreur s'est produite) => affichage de la 1�re erreur de la session
    +  if (! session.isValid()) { qDebug("[QxOrm] session error : '%s'", qPrintable(session.firstError().text())); }
    +
    +} // Fermeture du scope : la session est d�truite (transaction => commit ou rollback automatique)
    +
    +
    + Remarque : une session peut d�clencher une exception de type qx::dao::sql_error lorsqu'une erreur se produit (par d�faut, + aucune exception n'est d�clench�e). Il est possible de param�trer ce comportement en utilisant : +
      +
    • soit le constructeur de la classe qx::QxSession (pour une session en particulier) + ;
    • +
    • soit le param�tre du singleton + qx::QxSqlDatabase::getSingleton()->setSessionThrowable(bool b) (pour toutes les + sessions). +
    • +
    + Autre remarque : il est important de ne pas oublier de passer la connexion � la base de + donn�es de la session � chaque fonction qx::dao::xxx (en utilisant la m�thode + session.database()).
    + De plus, il est possible d'initialiser une session avec sa propre connexion (provenant d'un pool + de connexions par exemple) en utilisant le constructeur de la classe qx::QxSession.
    +
    + La classe qx::QxSession propose �galement des m�thodes de persistance (CRUD), ce qui peut + simplifier l'�criture du code C++ suivant les habitudes de programmation.
    + Voici le m�me exemple en utilisant les m�thodes de la classe qx::QxSession � la place des + fonctions du namespace qx::dao :
    +
    + + + + + + + +
    +
    { // Ouverture d'un scope o� une session sera instanci�e
    +
    +  // Cr�ation d'une session : une connexion valide � la BDD est assign�e � la session et une transaction est d�marr�e
    +  qx::QxSession session;
    +
    +  // Ex�cution d'une s�rie d'op�rations avec la BDD
    +  session.insert(my_object);
    +  session.update(my_object);
    +  session.fetchById(my_object);
    +  session.deleteById(my_object);
    +
    +  // Si la session n'est pas valide (donc une erreur s'est produite) => affichage de la 1�re erreur de la session
    +  if (! session.isValid()) { qDebug("[QxOrm] session error : '%s'", qPrintable(session.firstError().text())); }
    +
    +} // Fermeture du scope : la session est d�truite (transaction => commit ou rollback automatique)
    +
    +

    +
    +

    Moteur de relations +

    +
    + La biblioth�que QxOrm fournit un puissant moteur de relations permettant de d�finir facilement : + + Remarque : un tutoriel complet sur les relations bas� sur le + projet de test qxBlog (dont les sources sont pr�sentes dans le package QxOrm) est + disponible. +

    +

    one-to-many (1-n) +

    +
    + Une relation one-to-many (1-n) est d�finie par la m�thode : qx::QxClass<T>::relationOneToMany(). + Cette m�thode renvoie une instance de la classe qx::IxSqlRelation (classe de base pour toutes les relations) et + n�cessite 3 param�tres : +
      +
    • V U::* pData : r�f�rence vers la donn�e membre de la classe ;
    • +
    • const QString & sKey : cl� unique associ�e � la relation ;
    • +
    • const QString & sForeignKey : cl� �trang�re d�finie dans la classe/table li�e. +
    • +
    +
    + Par exemple : prenons l'exemple d'un author (une personne) qui peut r�diger + plusieurs blog + : nous allons ainsi montrer comment d�finir une relation de type + one-to-many.
    + Au niveau base de donn�es, voici les deux tables qui correspondent :
    +
    + qxBlog.table.author
    +
    + Fichier author.h :
    + + + + + + + +
    +
    #ifndef _QX_BLOG_AUTHOR_H_
    +#define _QX_BLOG_AUTHOR_H_
    +
    +class blog;
    +
    +class QX_BLOG_DLL_EXPORT author
    +{
    +public:
    +// -- typedef
    +   typedef boost::shared_ptr<blog> blog_ptr;
    +   typedef std::vector<blog_ptr> list_blog;
    +// -- enum
    +   enum enum_sex { male, female, unknown };
    +// -- propri�t�s
    +   QString     m_id;
    +   QString     m_name;
    +   QDate       m_birthdate;
    +   enum_sex    m_sex;
    +   list_blog   m_blogX;
    +// -- constructeur, destructeur virtuel
    +   author() : m_id(0), m_sex(unknown) { ; }
    +   virtual ~author() { ; }
    +// -- m�thodes
    +   int age() const;
    +};
    +
    +QX_REGISTER_PRIMARY_KEY(author, QString)
    +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0)
    +
    +typedef boost::shared_ptr<author> author_ptr;
    +typedef qx::QxCollection<QString, author_ptr> list_author;
    +
    +#endif // _QX_BLOG_AUTHOR_H_
    +
    +
    +
    + Fichier author.cpp :
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/author.h"
    +#include "../include/blog.h"
    +
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_BLOG(author)
    +
    +namespace qx {
    +template <> void register_class(QxClass<author> & t)
    +{
    +   t.id(& author::m_id, "author_id");
    +
    +   t.data(& author::m_name, "name");
    +   t.data(& author::m_birthdate, "birthdate");
    +   t.data(& author::m_sex, "sex");
    +
    +   t.relationOneToMany(& author::m_blogX, "list_blog", "author_id");
    +
    +   t.fct_0<int>(& author::age, "age");
    +}}
    +
    +int author::age() const
    +{
    +   if (! m_birthdate.isValid()) { return -1; }
    +   return (QDate::currentDate().year() - m_birthdate.year());
    +}
    +
    +
    +

    +
    +

    many-to-one (n-1) +

    +
    + Une relation many-to-one (n-1) est d�finie par la m�thode : qx::QxClass<T>::relationManyToOne(). + Cette m�thode renvoie une instance de la classe qx::IxSqlRelation (classe de base pour toutes les relations) et + n�cessite 2 param�tres : +
      +
    • V U::* pData : r�f�rence vers la donn�e membre de la classe ;
    • +
    • const QString & sKey : cl� unique associ�e � la relation (correspond � une + colonne de la table dans la base de donn�es).
    • +
    +
    + Par exemple : un comment est associ� � un blog et un blog peut + contenir plusieurs comment : nous allons ainsi montrer comment + d�finir une relation de type many-to-one.
    + Au niveau base de donn�es, voici les deux tables qui correspondent :
    +
    + qxBlog.table.comment
    +
    + Fichier comment.h :
    + + + + + + + +
    +
    #ifndef _QX_BLOG_COMMENT_H_
    +#define _QX_BLOG_COMMENT_H_
    +
    +class blog;
    +
    +class QX_BLOG_DLL_EXPORT comment
    +{
    +public:
    +// -- typedef
    +   typedef boost::shared_ptr<blog> blog_ptr;
    +// -- propri�t�s
    +   long        m_id;
    +   QString     m_text;
    +   QDateTime   m_dt_create;
    +   blog_ptr    m_blog;
    +// -- constructeur, destructeur virtuel
    +   comment() : m_id(0) { ; }
    +   virtual ~comment() { ; }
    +};
    +
    +QX_REGISTER_HPP_QX_BLOG(comment, qx::trait::no_base_class_defined, 0)
    +
    +typedef boost::shared_ptr<comment> comment_ptr;
    +typedef QList<comment_ptr> list_comment;
    +
    +#endif // _QX_BLOG_COMMENT_H_
    +
    +
    +
    + Fichier comment.cpp :
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/comment.h"
    +#include "../include/blog.h"
    +
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_BLOG(comment)
    +
    +namespace qx {
    +template <> void register_class(QxClass<comment> & t)
    +{
    +   t.id(& comment::m_id, "comment_id");
    +
    +   t.data(& comment::m_text, "comment_text");
    +   t.data(& comment::m_dt_create, "date_creation");
    +
    +   t.relationManyToOne(& comment::m_blog, "blog_id");
    +}}
    +
    +
    +

    +
    +

    many-to-many + (n-n)

    +
    + Une relation many-to-many (n-n) est d�finie par la m�thode : qx::QxClass<T>::relationManyToMany(). + Cette m�thode renvoie une instance de la classe qx::IxSqlRelation (classe de base pour toutes les relations) et + n�cessite 5 param�tres : +
      +
    • V U::* pData : r�f�rence vers la donn�e membre de la classe ;
    • +
    • const QString & sKey : cl� unique associ�e � la relation ;
    • +
    • const QString & sExtraTable : nom de la table suppl�mentaire permettant de + stocker les id de chaque c�t� des relations ;
    • +
    • const QString & sForeignKeyOwner : cl� �trang�re d�finie dans la table + suppl�mentaire pour repr�senter la classe/table courante ;
    • +
    • const QString & sForeignKeyDataType : cl� �trang�re d�finie dans la table + suppl�mentaire pour repr�senter la classe/table associ�e � la relation.
    • +
    +
    + Par exemple : une category r�f�rence plusieurs blog et un blog + peut + appartenir � plusieurs category : nous allons ainsi montrer + comment d�finir une relation de type many-to-many. + Ce type de relation implique une table suppl�mentaire dans la base de + donn�es pour stocker la liste des id de chaque c�t� des + relations.
    + Au niveau base de donn�es, voici les trois tables qui correspondent :
    +
    + qxBlog.table.category
    +
    + Fichier category.h :
    + + + + + + + +
    +
    #ifndef _QX_BLOG_CATEGORY_H_
    +#define _QX_BLOG_CATEGORY_H_
    +
    +class blog;
    +
    +class QX_BLOG_DLL_EXPORT category
    +{
    +public:
    +// -- typedef
    +   typedef boost::shared_ptr<blog> blog_ptr;
    +   typedef qx::QxCollection<long, blog_ptr> list_blog;
    +// -- propri�t�s
    +   long        m_id;
    +   QString     m_name;
    +   QString     m_desc;
    +   list_blog   m_blogX;
    +// -- constructeur, destructeur virtuel
    +   category() : m_id(0) { ; }
    +   virtual ~category() { ; }
    +};
    +
    +QX_REGISTER_HPP_QX_BLOG(category, qx::trait::no_base_class_defined, 0)
    +
    +typedef QSharedPointer<category> category_ptr;
    +typedef qx::QxCollection<long, category_ptr> list_category;
    +
    +#endif // _QX_BLOG_CATEGORY_H_
    +
    +
    +
    + Fichier category.cpp :
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/category.h"
    +#include "../include/blog.h"
    +
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_BLOG(category)
    +
    +namespace qx {
    +template <> void register_class(QxClass<category> & t)
    +{
    +   t.id(& category::m_id, "category_id");
    +
    +   t.data(& category::m_name, "name");
    +   t.data(& category::m_desc, "description");
    +
    +   t.relationManyToMany(& category::m_blogX, "list_blog", "category_blog", "category_id", "blog_id");
    +}}
    +
    +
    +

    +
    +

    one-to-one (1-1) +

    +
    + Une relation one-to-one (1-1) permet de repr�senter 2 entit�s distinctes qui partagent + le m�me identifiant en base de donn�es. + Une relation one-to-one (1-1) est d�finie par la m�thode : qx::QxClass<T>::relationOneToOne(). + Cette m�thode renvoie une instance de la classe qx::IxSqlRelation (classe de base pour toutes les relations) et + n�cessite 2 param�tres : +
      +
    • V U::* pData : r�f�rence vers la donn�e membre de la classe ;
    • +
    • const QString & sKey : cl� unique associ�e � la relation.
    • +
    +
    + Par exemple : prenons l'exemple d'une table person et d'une autre table + author : un author est �galement une person, les 2 tables pourraient + partager le m�me identifiant en base de donn�es. + Au niveau base de donn�es, voici les 2 tables qui correspondent (person_id == + author_id) :
    +
    + qxBlog.table.person +


    +
    +

    Requ�te SQL avec + relations

    +
    + La biblioth�que QxOrm supporte quatre types de relations pour lier les classes C++ + enregistr�es dans le contexte QxOrm : one-to-one, one-to-many, + many-to-one et many-to-many.
    + Pour plus de d�tails sur la d�finition de ces relations, il est conseill� de lire le tutoriel qxBlog.
    + Nous allons d�tailler dans cette Q&R les diff�rentes m�thodes de r�cup�ration des donn�es + (module QxDao, fonctions du + namespace qx::dao) : + + Le premier param�tre des fonctions fetch_by_id_with_relation, fetch_all_with_relation + et fetch_by_query_with_relation correspond � la liste des relations � + requ�ter.
    + Cette liste de relations peut contenir les �l�ments suivants : +
      +
    • identifiant d'une relation : chaque relation poss�de une cl� d�finie au niveau de la + fonction de param�trage qx::register_class<T> ;
    • +
    • le mot-cl� "*" signifie "r�cup�rer toutes les relations d�finies dans la + fonction de param�trage qx::register_class<T> sur un niveau" ;
    • +
    • le mot-cl� "->" signifie jointure de type "LEFT OUTER JOIN" (jointure + par d�faut de la biblioth�que QxOrm) ;
    • +
    • le mot-cl� ">>" signifie jointure de type "INNER JOIN" entre deux + tables.
    • +
    + Remarque : en utilisant le mot-cl� "*" pour indiquer "toutes les relations sur un + niveau", les appels suivants sont �quivalents : +
      +
    • qx::dao::fetch_by_id_with_relation("*", ...) == + qx::dao::fetch_by_id_with_all_relation(...) ; +
    • +
    • qx::dao::fetch_by_query_with_relation("*", ...) == + qx::dao::fetch_by_query_with_all_relation(...) ; +
    • +
    • qx::dao::fetch_all_with_relation("*", ...) == + qx::dao::fetch_all_with_all_relation(...). +
    • +
    +
    + Exemple : � partir du tutoriel qxBlog, il est possible de r�cup�rer les donn�es + suivantes avec une seule requ�te :
    +
    + 1- r�cup�rer un blog et son author ;
    + 2- pour l'author valoris�, r�cup�rer tous les blog qu'il a �crit ;
    + 3- pour chaque blog que l'author a �crit, r�cup�rer tous les + comment associ�s.
    +
    + + + + + + + +
    +
    blog_ptr my_blog = blog_ptr(new blog(10));
    +QSqlError daoError = qx::dao::fetch_by_id_with_relation("author_id->list_blog->list_comment", my_blog);
    +
    +
    + Ce qui g�n�re la requ�te SQL suivante : +
    +
    SELECT blog.blog_id AS blog_blog_id_0, blog.blog_text AS blog_blog_text_0, blog.date_creation AS blog_date_creation_0, blog.author_id AS blog_author_id_0, 
    +       author_1.author_id AS author_1_author_id_0, author_1.name AS author_1_name_0, author_1.birthdate AS author_1_birthdate_0, author_1.sex AS author_1_sex_0, 
    +       blog_2.blog_id AS blog_2_blog_id_0, blog_2.author_id AS blog_2_author_id_0, blog_2.blog_text AS blog_2_blog_text_0, blog_2.date_creation AS blog_2_date_creation_0, 
    +       comment_4.comment_id AS comment_4_comment_id_0, comment_4.blog_id AS comment_4_blog_id_0, comment_4.comment_text AS comment_4_comment_text_0, comment_4.date_creation AS comment_4_date_creation_0 
    +FROM blog 
    +LEFT OUTER JOIN author author_1 ON author_1.author_id = blog.author_id 
    +LEFT OUTER JOIN blog blog_2 ON blog_2.author_id = author_1.author_id 
    +LEFT OUTER JOIN comment comment_4 ON comment_4.blog_id = blog_2.blog_id 
    +WHERE blog.blog_id = :blog_id
    +
    +

    + Autre exemple : il est �galement possible de cr�er une liste de relations � r�cup�rer, + comme ceci par exemple :
    +
    + + + + + + + +
    +
    blog_ptr my_blog = blog_ptr(new blog(10));
    +QStringList relation;
    +relation << "author_id->list_blog->list_comment";
    +relation << "author_id->list_blog->list_category";
    +relation << "list_comment";
    +relation << "list_category";
    +QSqlError daoError = qx::dao::fetch_by_id_with_relation(relation, my_blog);
    +
    +
    + Ce qui g�n�re la requ�te SQL suivante : +
    +
    SELECT blog.blog_id AS blog_blog_id_0, blog.blog_text AS blog_blog_text_0, blog.date_creation AS blog_date_creation_0, blog.author_id AS blog_author_id_0, 
    +       author_1.author_id AS author_1_author_id_0, author_1.name AS author_1_name_0, author_1.birthdate AS author_1_birthdate_0, author_1.sex AS author_1_sex_0, 
    +       blog_2.blog_id AS blog_2_blog_id_0, blog_2.author_id AS blog_2_author_id_0, blog_2.blog_text AS blog_2_blog_text_0, blog_2.date_creation AS blog_2_date_creation_0, 
    +       category_5.category_id AS category_5_category_id_0, category_5.name AS category_5_name_0, category_5.description AS category_5_description_0, 
    +       comment_6.comment_id AS comment_6_comment_id_0, comment_6.blog_id AS comment_6_blog_id_0, comment_6.comment_text AS comment_6_comment_text_0, comment_6.date_creation AS comment_6_date_creation_0, 
    +       category_7.category_id AS category_7_category_id_0, category_7.name AS category_7_name_0, category_7.description AS category_7_description_0 
    +FROM blog 
    +LEFT OUTER JOIN author author_1 ON author_1.author_id = blog.author_id 
    +LEFT OUTER JOIN blog blog_2 ON blog_2.author_id = author_1.author_id 
    +LEFT OUTER JOIN category_blog category_blog_5 ON blog_2.blog_id = category_blog_5.blog_id 
    +LEFT OUTER JOIN category category_5 ON category_blog_5.category_id = category_5.category_id 
    +LEFT OUTER JOIN comment comment_6 ON comment_6.blog_id = blog.blog_id 
    +LEFT OUTER JOIN category_blog category_blog_7 ON blog.blog_id = category_blog_7.blog_id 
    +LEFT OUTER JOIN category category_7 ON category_blog_7.category_id = category_7.category_id 
    +WHERE blog.blog_id = :blog_id
    +
    +

    + Autre exemple : pour r�cup�rer toutes les relations pour un niveau donn�, il faut + utiliser le mot-cl� "*".
    + Pour r�cup�rer toutes les donn�es de toutes les relations sur trois niveaux, il faut �crire + :
    +
    + + + + + + + +
    +
    blog_ptr my_blog = blog_ptr(new blog(10));
    +QSqlError daoError = qx::dao::fetch_by_id_with_relation("*->*->*", my_blog);
    +
    +
    + Ce qui g�n�re la requ�te SQL suivante : +
    +
    SELECT blog.blog_id AS blog_blog_id_0, blog.blog_text AS blog_blog_text_0, blog.date_creation AS blog_date_creation_0, blog.author_id AS blog_author_id_0, 
    +       author_1.author_id AS author_1_author_id_0, author_1.name AS author_1_name_0, author_1.birthdate AS author_1_birthdate_0, author_1.sex AS author_1_sex_0, 
    +       blog_2.blog_id AS blog_2_blog_id_0, blog_2.author_id AS blog_2_author_id_0, blog_2.blog_text AS blog_2_blog_text_0, blog_2.date_creation AS blog_2_date_creation_0, blog_2.author_id AS blog_2_author_id_0_2, 
    +       author_3.author_id AS author_3_author_id_0, author_3.name AS author_3_name_0, author_3.birthdate AS author_3_birthdate_0, author_3.sex AS author_3_sex_0, 
    +       comment_4.comment_id AS comment_4_comment_id_0, comment_4.blog_id AS comment_4_blog_id_0, comment_4.comment_text AS comment_4_comment_text_0, comment_4.date_creation AS comment_4_date_creation_0, 
    +       category_5.category_id AS category_5_category_id_0, category_5.name AS category_5_name_0, category_5.description AS category_5_description_0, 
    +       comment_6.comment_id AS comment_6_comment_id_0, comment_6.blog_id AS comment_6_blog_id_0, comment_6.comment_text AS comment_6_comment_text_0, comment_6.date_creation AS comment_6_date_creation_0, comment_6.blog_id AS comment_6_blog_id_0_6, 
    +       blog_7.blog_id AS blog_7_blog_id_0, blog_7.blog_text AS blog_7_blog_text_0, blog_7.date_creation AS blog_7_date_creation_0, blog_7.author_id AS blog_7_author_id_0_7, 
    +       author_8.author_id AS author_8_author_id_0, author_8.name AS author_8_name_0, author_8.birthdate AS author_8_birthdate_0, author_8.sex AS author_8_sex_0, 
    +       comment_9.comment_id AS comment_9_comment_id_0, comment_9.blog_id AS comment_9_blog_id_0, comment_9.comment_text AS comment_9_comment_text_0, comment_9.date_creation AS comment_9_date_creation_0, 
    +       category_10.category_id AS category_10_category_id_0, category_10.name AS category_10_name_0, category_10.description AS category_10_description_0, 
    +       category_11.category_id AS category_11_category_id_0, category_11.name AS category_11_name_0, category_11.description AS category_11_description_0, 
    +       blog_12.blog_id AS blog_12_blog_id_0, blog_12.blog_text AS blog_12_blog_text_0, blog_12.date_creation AS blog_12_date_creation_0, blog_12.author_id AS blog_12_author_id_0_12, 
    +       author_13.author_id AS author_13_author_id_0, author_13.name AS author_13_name_0, author_13.birthdate AS author_13_birthdate_0, author_13.sex AS author_13_sex_0, 
    +       comment_14.comment_id AS comment_14_comment_id_0, comment_14.blog_id AS comment_14_blog_id_0, comment_14.comment_text AS comment_14_comment_text_0, comment_14.date_creation AS comment_14_date_creation_0, 
    +       category_15.category_id AS category_15_category_id_0, category_15.name AS category_15_name_0, category_15.description AS category_15_description_0 
    +FROM blog 
    +LEFT OUTER JOIN author author_1 ON author_1.author_id = blog.author_id 
    +LEFT OUTER JOIN blog blog_2 ON blog_2.author_id = author_1.author_id 
    +LEFT OUTER JOIN author author_3 ON author_3.author_id = blog_2.author_id 
    +LEFT OUTER JOIN comment comment_4 ON comment_4.blog_id = blog_2.blog_id 
    +LEFT OUTER JOIN category_blog category_blog_5 ON blog_2.blog_id = category_blog_5.blog_id 
    +LEFT OUTER JOIN category category_5 ON category_blog_5.category_id = category_5.category_id 
    +LEFT OUTER JOIN comment comment_6 ON comment_6.blog_id = blog.blog_id 
    +LEFT OUTER JOIN blog blog_7 ON blog_7.blog_id = comment_6.blog_id 
    +LEFT OUTER JOIN author author_8 ON author_8.author_id = blog_7.author_id 
    +LEFT OUTER JOIN comment comment_9 ON comment_9.blog_id = blog_7.blog_id 
    +LEFT OUTER JOIN category_blog category_blog_10 ON blog_7.blog_id = category_blog_10.blog_id 
    +LEFT OUTER JOIN category category_10 ON category_blog_10.category_id = category_10.category_id 
    +LEFT OUTER JOIN category_blog category_blog_11 ON blog.blog_id = category_blog_11.blog_id 
    +LEFT OUTER JOIN category category_11 ON category_blog_11.category_id = category_11.category_id 
    +LEFT OUTER JOIN category_blog category_blog_12 ON category_11.category_id = category_blog_12.category_id 
    +LEFT OUTER JOIN blog blog_12 ON category_blog_12.blog_id = blog_12.blog_id 
    +LEFT OUTER JOIN author author_13 ON author_13.author_id = blog_12.author_id 
    +LEFT OUTER JOIN comment comment_14 ON comment_14.blog_id = blog_12.blog_id 
    +LEFT OUTER JOIN category_blog category_blog_15 ON blog_12.blog_id = category_blog_15.blog_id 
    +LEFT OUTER JOIN category category_15 ON category_blog_15.category_id = category_15.category_id 
    +WHERE blog.blog_id = :blog_id
    +
    +
    +
    +

    S�lectionner les + colonnes des relations � r�cup�rer et d�finition des alias SQL

    +
    + Il est parfois n�cessaire de ne pas requ�ter toutes les colonnes d'une table par soucis + d'optimisation : en effet, s�lectionner les colonnes r�ellement utilis�es par un traitement + permet de limiter les flux r�seau entre la base de donn�es et l'application C++, ce qui + am�liore les performances.
    +
    + Concernant les relations, la biblioth�que QxOrm fournit une syntaxe sp�cifique pour + s�lectionner les colonnes � r�cup�rer, sous la forme : my_relation { col_1, col_2, etc... + }. + Si cette syntaxe n'est pas utilis�e, par d�faut, QxOrm r�cup�re toutes les colonnes.
    +
    + Par exemple : imaginons la requ�te suivante qui permet de r�cup�rer : +
      +
    • uniquement la colonne blog_text de la table blog ;
    • +
    • uniquement les colonnes name et birthdate de la table author ; +
    • +
    • uniquement la colonne comment_text de la table comment.
    • +
    + + + + + + + +
    +
       // Fetch relations defining columns to fetch with syntax { col_1, col_2, etc... }
    +   list_blog lstBlogComplexRelation;
    +   QStringList relations = QStringList() << "{ blog_text }" << "author_id { name, birthdate }" << "list_comment { comment_text }";
    +   QSqlError daoError = qx::dao::fetch_all_with_relation(relations, lstBlogComplexRelation);
    +
    +   qx::dump(lstBlogComplexRelation);
    +   qAssert(lstBlogComplexRelation.size() > 0);
    +   qAssert(lstBlogComplexRelation[0]->m_text != ""); // Fetched
    +   qAssert(lstBlogComplexRelation[0]->m_dt_creation.isNull()); // Not fetched
    +   qAssert(lstBlogComplexRelation[0]->m_author->m_sex == author::unknown); // Not fetched
    +   qAssert(lstBlogComplexRelation[0]->m_author->m_name != ""); // Fetched
    +   qAssert(lstBlogComplexRelation[0]->m_commentX.size() > 0);
    +   qAssert(lstBlogComplexRelation[0]->m_commentX[0]->m_dt_create.isNull()); // Not fetched
    +   qAssert(lstBlogComplexRelation[0]->m_commentX[0]->m_text != ""); // Fetched
    +
    +
    + Remarque : une autre syntaxe est disponible afin de renseigner les colonnes � ne pas + r�cup�rer : my_relation -{ col_1, col_2, etc... }. +

    + Autre remarque : il est �galement possible de d�finir un alias par relation � + utiliser dans la requ�te SQL. + Ceci est utile pour l'�criture des conditions dans la clause WHERE. + Un alias SQL peut �tre d�fini entre les caract�res < >. +

    + Exemple : voici un exemple de fetch avec relations en d�finissant des alias SQL par + relation : +

    + + + + + + + +
    +
    list_blog lstBlogComplexRelation3;
    +QStringList relations;
    +relations << "<blog_alias> { blog_text }";
    +relations << "author_id <author_alias> { name, birthdate }";
    +relations << "list_comment <list_comment_alias> { comment_text } -> blog_id <blog_alias_2> -> * <..._my_alias_suffix>";
    +QSqlError daoError = qx::dao::fetch_all_with_relation(relations, lstBlogComplexRelation3);
    +qx::dump(lstBlogComplexRelation3);
    +
    +

    + Ce qui g�n�re la requ�te SQL suivante : +
    +
    +
    SELECT blog_alias.blog_id AS blog_alias_blog_id_0, blog_alias.blog_text AS blog_alias_blog_text_0, blog_alias.author_id AS blog_alias_author_id_0, author_alias.author_id AS author_alias_author_id_0, author_alias.name AS author_alias_name_0, author_alias.birthdate AS author_alias_birthdate_0, list_comment_alias.comment_id AS list_comment_alias_comment_id_0, list_comment_alias.blog_id AS list_comment_alias_blog_id_0, list_comment_alias.comment_text AS list_comment_alias_comment_text_0, list_comment_alias.blog_id AS list_comment_alias_blog_id_0_2, blog_alias_2.blog_id AS blog_alias_2_blog_id_0, blog_alias_2.blog_text AS blog_alias_2_blog_text_0, blog_alias_2.date_creation AS blog_alias_2_date_creation_0, blog_alias_2.author_id AS blog_alias_2_author_id_0_3, author_my_alias_suffix.author_id AS author_my_alias_suffix_author_id_0, author_my_alias_suffix.name AS author_my_alias_suffix_name_0, author_my_alias_suffix.birthdate AS author_my_alias_suffix_birthdate_0, author_my_alias_suffix.sex AS author_my_alias_suffix_sex_0, comment_my_alias_suffix.comment_id AS comment_my_alias_suffix_comment_id_0, comment_my_alias_suffix.blog_id AS comment_my_alias_suffix_blog_id_0, comment_my_alias_suffix.comment_text AS comment_my_alias_suffix_comment_text_0, comment_my_alias_suffix.date_creation AS comment_my_alias_suffix_date_creation_0, comment_my_alias_suffix.blog_id AS comment_my_alias_suffix_blog_id_0_5, category_my_alias_suffix.category_id AS category_my_alias_suffix_category_id_0, category_my_alias_suffix.name AS category_my_alias_suffix_name_0, category_my_alias_suffix.description AS category_my_alias_suffix_description_0
    +  FROM blog AS blog_alias
    +  LEFT OUTER JOIN author author_alias ON author_alias.author_id = blog_alias.author_id
    +  LEFT OUTER JOIN comment list_comment_alias ON list_comment_alias.blog_id = blog_alias.blog_id
    +  LEFT OUTER JOIN blog blog_alias_2 ON blog_alias_2.blog_id = list_comment_alias.blog_id
    +  LEFT OUTER JOIN author author_my_alias_suffix ON author_my_alias_suffix.author_id = blog_alias_2.author_id
    +  LEFT OUTER JOIN comment comment_my_alias_suffix ON comment_my_alias_suffix.blog_id = blog_alias_2.blog_id
    +  LEFT OUTER JOIN category_blog category_blog_6 ON blog_alias_2.blog_id = category_blog_6.blog_id
    +  LEFT OUTER JOIN category category_my_alias_suffix ON category_blog_6.category_id = category_my_alias_suffix.category_id
    +
    +

    +
    +

    Ajout SQL dans les + clauses LEFT OUTER JOIN / INNER JOIN

    +
    + La classe qx::QxSqlQuery (ou son alias qx_query) dispose + de la m�thode suivante : +

    + + + + + + + +
    +
    QxSqlQuery & QxSqlQuery::addJoinQuery(const QString & relationKeyOrAlias, const QxSqlQuery & joinQuery);
    +
    +

    + La m�thode qx::QxSqlQuery::addJoinQuery() permet d'ins�rer des sous-requ�tes SQL dans + les clauses LEFT OUTER JOIN / INNER JOIN.
    + Par exemple : +

    + + + + + + + +
    +
    // Test to add join SQL sub-queries (inside LEFT OUTER JOIN or INNER JOIN)
    +list_blog lstBlogWithJoinQueries;
    +qx_query query = qx_query().where("blog_alias.blog_text").isEqualTo("update blog_text_1");
    +query.addJoinQuery("list_comment_alias", "AND list_comment_alias.comment_text IS NOT NULL");
    +query.addJoinQuery("author_alias", qx_query().freeText("AND author_alias.sex = :sex", QVariantList() << author::female));
    +daoError = qx::dao::fetch_by_query_with_relation(QStringList() << "<blog_alias> { blog_text }" << "author_id <author_alias> { name, birthdate, sex }" 
    +                                                               << "list_comment <list_comment_alias> { comment_text }", query, lstBlogWithJoinQueries);
    +qx::dump(lstBlogWithJoinQueries);
    +qAssert(lstBlogWithJoinQueries.size() > 0);
    +qAssert(lstBlogWithJoinQueries[0]->m_text == "update blog_text_1");
    +qAssert(lstBlogWithJoinQueries[0]->m_author->m_sex == author::female);
    +
    +

    + Le code C++ ci-dessus va construire la requ�te SQL suivante : +

    +
    +
    SELECT blog_alias.blog_id AS blog_alias_blog_id_0, blog_alias.blog_text AS blog_alias_blog_text_0, blog_alias.author_id AS blog_alias_author_id_0, author_alias.author_id AS author_alias_author_id_0, author_alias.name AS author_alias_name_0, author_alias.birthdate AS author_alias_birthdate_0, author_alias.sex AS author_alias_sex_0, list_comment_alias.comment_id AS list_comment_alias_comment_id_0, list_comment_alias.blog_id AS list_comment_alias_blog_id_0, list_comment_alias.comment_text AS list_comment_alias_comment_text_0
    +  FROM blog AS blog_alias
    +  LEFT OUTER JOIN author author_alias ON (author_alias.author_id = blog_alias.author_id
    +      AND author_alias.sex = :sex)
    +  LEFT OUTER JOIN comment list_comment_alias ON (list_comment_alias.blog_id = blog_alias.blog_id
    +      AND list_comment_alias.comment_text IS NOT NULL)
    +  WHERE blog_alias.blog_text = :blog_alias_blog_text_1_0
    +
    +

    +
    +
    +

    Collections support�es + par QxOrm

    +
    + QxOrm supporte de nombreux conteneurs livr�s avec Qt, boost ou la biblioth�que standard std. + La biblioth�que QxOrm fournit �galement son propre conteneur, nomm� qx::QxCollection, particuli�rement adapt� pour stocker les donn�es + issues d'une base de donn�es. + Le d�veloppeur a donc � sa disposition un large choix : QxOrm n'impose aucune contrainte sur + l'utilisation des collections. +

    +

    Collections de Qt +

    +
    + + + + + + + +
    +
    +  QList<T>  
    +  QVector<T>  
    +  QSet<T>  
    +  QLinkedList<T>  
    +  QHash<Key, Value>  
    +  QMap<Key, Value>  
    +  QMultiHash<Key, Value>  
    +  QMultiMap<Key, Value>  
    +
    +
    +
    +
    +

    Collections de + boost

    +
    + + + + + + + +
    +
    +  boost::unordered_map<Key, Value>  
    +  boost::unordered_set<T>  
    +  boost::unordered_multimap<Key, Value>  
    +  boost::unordered_multiset<T>  
    +
    +
    +
    +
    +

    Collections fournies + par l'espace de nom standard std

    +
    + + + + + + + +
    +
    +  std::list<T>  
    +  std::vector<T>  
    +  std::set<T>  
    +  std::map<Key, Value>  
    +
    +  std::unordered_map<Key, Value>  
    +  std::unordered_set<T>  
    +  std::unordered_multimap<Key, Value>  
    +  std::unordered_multiset<T>  
    +
    +
    +
    +
    +

    qx::QxCollection +

    +
    + Il existe de nombreux container dans les biblioth�ques stl, boost et + Qt.
    + Il est donc l�gitime de se poser cette question : � quoi sert qx::QxCollection<Key, + Value> ?
    + qx::QxCollection<Key, Value> est un nouveau + container (bas� sur l'excellente biblioth�que boost::multi_index_container) qui poss�de les fonctionnalit�s + suivantes : +
      +
    • conserve l'ordre d'insertion des �l�ments dans la liste ; +
    • +
    • acc�s rapide � un �l�ment par son index : �quivaut � std::vector<T> ou + QList<T> par exemple ; +
    • +
    • acc�s rapide � un �l�ment par une cl� (hash-map) : �quivaut � QHash<Key, + Value> ou boost::unordered_map<Key, Value> par exemple ; +
    • +
    • fonctions de tri sur le type Key et sur le type Value ; +
    • +
    • thread-safe. +
    • +
    + Remarque : + qx::QxCollection<Key, Value> est compatible avec la macro foreach fournie + par la biblioth�que Qt ainsi que par la macro BOOST_FOREACH fournie par la biblioth�que boost.
    + Cependant, chaque �l�ment renvoy� par ces deux macros correspond � un objet de type + std::pair<Key, Value>.
    + Pour obtenir un r�sultat 'plus naturel' et plus lisible, il est conseill� d'utiliser la macro + _foreach : cette macro utilise BOOST_FOREACH pour tous les container + sauf pour qx::QxCollection<Key, Value>.
    + Dans ce cas, l'�l�ment renvoy� correspond au type Value (voir par la suite l'exemple + d'utilisation).
    + La macro _foreach est donc compatible avec tous les container (stl, + Qt, boost, etc.) puisqu'elle utilise la macro BOOST_FOREACH.

    + Autre Remarque : + qx::QxCollection<Key, Value> est particuli�rement adapt� pour recevoir des + donn�es issues d'une base de donn�es.
    + En effet, ces donn�es peuvent �tre tri�es (en utilisant ORDER BY dans une requ�te SQL + par exemple), il est donc important de conserver l'ordre d'insertion des �l�ments dans la + liste.
    + De plus, chaque donn�e issue d'une base de donn�es poss�de un identifiant unique. Il est donc + int�ressant de pouvoir acc�der � un �l�ment en fonction de cet identifiant unique de mani�re + extr�mement rapide (hash-map).

    + Exemple d'utilisation de la collection qx::QxCollection<Key, Value> :
    +
    + + + + + + + +
    +
    /* d�finition d'une classe drug avec 3 propri�t�s : 'code', 'name', 'description' */
    +class drug { public: QString code; QString name; QString desc; };
    +
    +/* pointeur intelligent associ� � la classe drug */
    +typedef boost::shared_ptr<drug> drug_ptr;
    +
    +/* collection de drugs (acc�s rapide � un �l�ment de la collection par la propri�t� 'code') */
    +qx::QxCollection<QString, drug_ptr> lstDrugs;
    +
    +/* cr�ation de 3 nouveaux drugs */
    +drug_ptr d1; d1.reset(new drug()); d1->code = "code1"; d1->name = "name1"; d1->desc = "desc1";
    +drug_ptr d2; d2.reset(new drug()); d2->code = "code2"; d2->name = "name2"; d2->desc = "desc2";
    +drug_ptr d3; d3.reset(new drug()); d3->code = "code3"; d3->name = "name3"; d3->desc = "desc3";
    +
    +/* insertion des 3 drugs dans la collection */
    +lstDrugs.insert(d1->code, d1);
    +lstDrugs.insert(d2->code, d2);
    +lstDrugs.insert(d3->code, d3);
    +
    +/* parcours la collection en utilisant le mot-cl� '_foreach' */
    +_foreach(drug_ptr p, lstDrugs)
    +{ qDebug() << qPrintable(p->name) << " " << qPrintable(p->desc); }
    +
    +/* parcours la collection en utilisant une boucle 'for' */
    +for (long l = 0; l < lstDrugs.count(); ++l)
    +{
    +   drug_ptr p = lstDrugs.getByIndex(l);
    +   QString code = lstDrugs.getKeyByIndex(l);
    +   qDebug() << qPrintable(p->name) << " " << qPrintable(p->desc);
    +}
    +
    +/* parcours la collection en utilisant le style Java avec 'QxCollectionIterator' */
    +qx::QxCollectionIterator<QString, drug_ptr> itr(lstDrugs);
    +while (itr.next())
    +{
    +   QString code = itr.key();
    +   qDebug() << qPrintable(itr.value()->name) << " " << qPrintable(itr.value()->desc);
    +}
    +
    +/* effectue un tri croissant par cl� (propri�t� 'code') et d�croissant par valeur */
    +lstDrugs.sortByKey(true);
    +lstDrugs.sortByValue(false);
    +
    +/* acc�s rapide � un drug par son 'code' */
    +drug_ptr p = lstDrugs.getByKey("code2");
    +
    +/* acc�s rapide � un drug par son index (position) dans la collection */
    +drug_ptr p = lstDrugs.getByIndex(2);
    +
    +/* teste si un drug existe dans la collection et si la liste est vide */
    +bool bExist = lstDrugs.exist("code3");
    +bool bEmpty = lstDrugs.empty();
    +
    +/* supprime de la collection le 2�me �l�ment */
    +lstDrugs.removeByIndex(2);
    +
    +/* supprime de la collection l'�l�ment avec le code 'code3' */
    +lstDrugs.removeByKey("code3");
    +
    +/* efface tous les �l�ments de la collection */
    +lstDrugs.clear();
    +
    +

    +
    +
    +

    Pointeurs intelligents + support�s par QxOrm (smart-pointers)

    +
    + QxOrm supporte de nombreux pointeurs intelligents livr�s avec Qt, boost ou la biblioth�que standard std. + La biblioth�que QxOrm fournit �galement son propre pointeur intelligent, nomm� qx::dao::ptr, apportant de nouvelles fonctionnalit�s lorsqu'il est + utilis� avec les fonctions de l'espace de nom qx::dao. + Le d�veloppeur a donc � sa disposition un large choix : QxOrm n'impose aucune contrainte sur + l'utilisation des pointeurs intelligents. +

    +

    Pointeurs + intelligents de Qt

    +
    + + + + + + + +
    +
    +  QSharedPointer<T>  
    +  QScopedPointer<T>  
    +  QWeakPointer<T>  
    +  QSharedDataPointer<T>  
    +
    +
    +
    +
    +

    Pointeurs + intelligents de boost

    +
    + + + + + + + +
    +
    +  boost::shared_ptr<T>  
    +  boost::intrusive_ptr<T>  
    +  boost::scoped_ptr<T>  
    +  boost::weak_ptr<T>  
    +
    +
    +
    +
    +

    Pointeurs + intelligents fournis par l'espace de nom standard std

    +
    + + + + + + + +
    +
    +  std::shared_ptr<T>  
    +  std::unique_ptr<T>  
    +  std::weak_ptr<T>  
    +
    +
    +
    +
    +

    qx::dao::ptr

    +
    + QxOrm est compatible avec les pointeurs intelligents des biblioth�ques boost et + Qt.
    + Le pointeur intelligent d�velopp� par QxOrm est bas� sur QSharedPointer et + apporte de nouvelles fonctionnalit�s s'il est utilis� avec les fonctions + 'qx::dao::...'.
    + qx::dao::ptr<T> conserve automatiquement les valeurs + issues de la base de donn�es.
    + Il est ainsi possible de v�rifier � tout moment si une instance d'objet a subi des + modifications gr�ce � la m�thode 'isDirty()' : cette m�thode peut renvoyer la liste de + toutes les propri�t�s ayant �t� modifi�es.
    + qx::dao::ptr<T> peut �galement �tre utilis� par la fonction + 'qx::dao::update_optimized()' pour mettre � jour en base de donn�es uniquement les + champs modifi�s.
    + qx::dao::ptr<T> peut �tre utilis� avec un objet simple ou bien avec la plupart + des containers : stl, boost, Qt et qx::QxCollection<Key, + Value>.
    +
    + Exemple d'utilisation du pointeur intelligent qx::dao::ptr<T> :
    +
    + + + + + + + +
    +
    // exemple d'utilisation de la m�thode 'isDirty()'
    +qx::dao::ptr<blog> blog_isdirty = qx::dao::ptr<blog>(new blog());
    +blog_isdirty->m_id = blog_1->m_id;
    +daoError = qx::dao::fetch_by_id(blog_isdirty);
    +qAssert(! daoError.isValid() && ! blog_isdirty.isDirty());
    +
    +blog_isdirty->m_text = "blog property 'text' modified => blog is dirty !!!";
    +QStringList lstDiff; bool bDirty = blog_isdirty.isDirty(lstDiff);
    +qAssert(bDirty && (lstDiff.count() == 1) && (lstDiff.at(0) == "blog_text"));
    +if (bDirty) { qDebug("[QxOrm] test dirty 1 : blog is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
    +
    +// met � jour uniquement la propri�t� 'm_text' de l'instance 'blog_isdirty'
    +daoError = qx::dao::update_optimized(blog_isdirty);
    +qAssert(! daoError.isValid() && ! blog_isdirty.isDirty());
    +qx::dump(blog_isdirty);
    +
    +// exemple d'utilisation de la m�thode 'isDirty()' avec une liste d'objets
    +typedef qx::dao::ptr< QList<author_ptr> > type_lst_author_test_is_dirty;
    +
    +type_lst_author_test_is_dirty container_isdirty = type_lst_author_test_is_dirty(new QList<author_ptr>());
    +daoError = qx::dao::fetch_all(container_isdirty);
    +qAssert(! daoError.isValid() && ! container_isdirty.isDirty() && (container_isdirty->count() == 3));
    +
    +author_ptr author_ptr_dirty = container_isdirty->at(1);
    +author_ptr_dirty->m_name = "author name modified at index 1 => container is dirty !!!";
    +bDirty = container_isdirty.isDirty(lstDiff);
    +qAssert(bDirty && (lstDiff.count() == 1));
    +if (bDirty) { qDebug("[QxOrm] test dirty 2 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
    +
    +author_ptr_dirty = container_isdirty->at(2);
    +author_ptr_dirty->m_birthdate = QDate(1998, 03, 06);
    +bDirty = container_isdirty.isDirty(lstDiff);
    +qAssert(bDirty && (lstDiff.count() == 2));
    +if (bDirty) { qDebug("[QxOrm] test dirty 3 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
    +
    +// met � jour la propri�t� 'm_name' en position 1, la propri�t� 'm_birthdate' en position 2 et ne change rien en position 0
    +daoError = qx::dao::update_optimized(container_isdirty);
    +qAssert(! daoError.isValid() && ! container_isdirty.isDirty());
    +qx::dump(container_isdirty);
    +
    +// r�cup�re uniquement la propri�t� 'm_dt_creation' du blog
    +QStringList lstColumns = QStringList() << "date_creation";
    +list_blog lst_blog_with_only_date_creation;
    +daoError = qx::dao::fetch_all(lst_blog_with_only_date_creation, NULL, lstColumns);
    +qAssert(! daoError.isValid() && (lst_blog_with_only_date_creation.size() > 0));
    +
    +if ((lst_blog_with_only_date_creation.size() > 0) && (lst_blog_with_only_date_creation[0] != NULL))
    +{ qAssert(lst_blog_with_only_date_creation[0]->m_text.isEmpty()); }
    +
    +qx::dump(lst_blog_with_only_date_creation);
    +
    +

    +
    +
    +

    D�clencheurs + (triggers)

    +
    + Les Trigger de QxOrm permettent d'effectuer divers traitements avant et/ou apr�s + une insertion, une mise � jour ou bien une suppression dans la base de donn�es.
    + Un exemple d'utilisation se trouve dans le dossier ./test/qxDllSample/dll2/ avec la + classe BaseClassTrigger.
    + Cette classe contient cinq propri�t�s : m_id, m_dateCreation, + m_dateModification, m_userCreation et m_userModification.
    + Ces propri�t�s se mettront � jour automatiquement pour chaque classe h�ritant de + BaseClassTrigger (cf. les classes Foo et Bar du m�me projet).
    + Il est n�cessaire de sp�cialiser le template 'qx::dao::detail::QxDao_Trigger<T>' + pour profiter de cette fonctionnalit�.
    +
    + + + + + + + +
    +
    #ifndef _QX_BASE_CLASS_TRIGGER_H_
    +#define _QX_BASE_CLASS_TRIGGER_H_
    +
    +class QX_DLL2_EXPORT BaseClassTrigger
    +{
    +
    +   QX_REGISTER_FRIEND_CLASS(BaseClassTrigger)
    +
    +protected:
    +
    +   long        m_id;
    +   QDateTime   m_dateCreation;
    +   QDateTime   m_dateModification;
    +   QString     m_userCreation;
    +   QString     m_userModification;
    +
    +public:
    +
    +   BaseClassTrigger() : m_id(0)  { ; }
    +   virtual ~BaseClassTrigger()   { ; }
    +
    +   long getId() const                     { return m_id; }
    +   QDateTime getDateCreation() const      { return m_dateCreation; }
    +   QDateTime getDateModification() const  { return m_dateModification; }
    +   QString getUserCreation() const        { return m_userCreation; }
    +   QString getUserModification() const    { return m_userModification; }
    +
    +   void setId(long l)                              { m_id = l; }
    +   void setDateCreation(const QDateTime & dt)      { m_dateCreation = dt; }
    +   void setDateModification(const QDateTime & dt)  { m_dateModification = dt; }
    +   void setUserCreation(const QString & s)         { m_userCreation = s; }
    +   void setUserModification(const QString & s)     { m_userModification = s; }
    +
    +   void onBeforeInsert(qx::dao::detail::IxDao_Helper * dao);
    +   void onBeforeUpdate(qx::dao::detail::IxDao_Helper * dao);
    +
    +};
    +
    +QX_REGISTER_HPP_QX_DLL2(BaseClassTrigger, qx::trait::no_base_class_defined, 0)
    +
    +namespace qx {
    +namespace dao {
    +namespace detail {
    +
    +template <>
    +struct QxDao_Trigger<BaseClassTrigger>
    +{
    +
    +   static inline void onBeforeInsert(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { if (t) { t->onBeforeInsert(dao); } }
    +   static inline void onBeforeUpdate(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { if (t) { t->onBeforeUpdate(dao); } }
    +   static inline void onBeforeDelete(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onBeforeFetch(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onAfterInsert(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onAfterUpdate(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onAfterDelete(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +   static inline void onAfterFetch(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao)
    +   { Q_UNUSED(t); Q_UNUSED(dao); }
    +
    +};
    +
    +} // namespace detail
    +} // namespace dao
    +} // namespace qx
    +
    +#endif // _QX_BASE_CLASS_TRIGGER_H_
    +
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +#include "../include/BaseClassTrigger.h"
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_DLL2(BaseClassTrigger)
    +
    +namespace qx {
    +template <> void register_class(QxClass<BaseClassTrigger> & t)
    +{
    +   IxDataMember * pData = NULL;
    +
    +   pData = t.id(& BaseClassTrigger::m_id, "id");
    +
    +   pData = t.data(& BaseClassTrigger::m_dateCreation, "date_creation");
    +   pData = t.data(& BaseClassTrigger::m_dateModification, "date_modification");
    +   pData = t.data(& BaseClassTrigger::m_userCreation, "user_creation");
    +   pData = t.data(& BaseClassTrigger::m_userModification, "user_modification");
    +}}
    +
    +void BaseClassTrigger::onBeforeInsert(qx::dao::detail::IxDao_Helper * dao)
    +{
    +   Q_UNUSED(dao);
    +   m_dateCreation = QDateTime::currentDateTime();
    +   m_dateModification = QDateTime::currentDateTime();
    +   m_userCreation = "current_user_1";
    +   m_userModification = "current_user_1";
    +}
    +
    +void BaseClassTrigger::onBeforeUpdate(qx::dao::detail::IxDao_Helper * dao)
    +{
    +   Q_UNUSED(dao);
    +   m_dateModification = QDateTime::currentDateTime();
    +   m_userModification = "current_user_2";
    +}
    +
    +
    +

    +
    +

    Validation d'une instance + C++ (validators)

    +
    + Le module QxValidator de la biblioth�que QxOrm permet d'ajouter des + contraintes sur les propri�t�s enregistr�es dans le contexte QxOrm.
    + Ces contraintes sont d�finies dans la m�thode de mapping : void + qx::register_class<T>.
    + Si pour une instance de classe donn�e, au moins une contrainte n'est pas respect�e, alors + l'instance est consid�r�e comme invalide : l'objet ne peut alors pas �tre sauvegard� en base de + donn�es (INSERT ou UPDATE).
    +
    + Il est �galement possible d'utiliser le module QxValidator pour valider les donn�es au + niveau de la couche pr�sentation de l'application : si les donn�es saisies par un utilisateur ne + sont pas valides, un message d'erreur peut �tre signal�, il n'est alors pas n�cessaire d'essayer + d'enregistrer l'instance courante en base de donn�es.
    + Les r�gles de validation n'ont pas besoin d'�tre dupliqu�es : elles peuvent �tre utilis�es aussi + bien par la couche pr�sentation que par la couche d'acc�s aux donn�es de l'application.
    +
    + Voici la description de quelques classes du module QxValidator : +
      +
    • qx::IxValidator : chaque contrainte d�finie dans la fonction de + mapping void qx::register_class<T> est associ�e � une interface de type + qx::IxValidator ; +
    • +
    • qx::IxValidatorX : pour une classe donn�e, la liste des contraintes + est associ�e � une interface de type qx::IxValidatorX. Cette collection peut �tre + parcourue � l'ex�cution du programme : �a peut �tre int�ressant par exemple pour g�n�rer + le sch�ma DDL SQL et prendre en compte les contraintes au niveau de la base de donn�es + (voir le chapitre suivant du manuel utilisateur : G�n�rer le sch�ma + DDL SQL de la base de donn�es) ;
    • +
    • qx::QxInvalidValueX : au moment du processus de validation, + lorsqu'une instance n'est pas valide, la liste des contraintes non respect�es est + repr�sent�e par une collection de type qx::QxInvalidValueX ;
    • +
    • qx::QxInvalidValue : chaque �l�ment de cette collection est de type + qx::QxInvalidValue et contient un message d'erreur (description expliquant pourquoi + l'instance est invalide). +
    • +
    + Le module QxValidator g�re automatiquement la notion d'h�ritage de classe : si des + contraintes sont d�finies au niveau de la classe de base, alors elles seront automatiquement + v�rifi�es pour chaque validation d'une classe d�riv�e.
    +
    + Voici un exemple d'utilisation du module QxValidator avec une classe 'person' + :
    +
    + * fichier 'person.h' :
    + + + + + + + +
    +
    #ifndef _CLASS_PERSON_H_
    +#define _CLASS_PERSON_H_
    + 
    +class person
    +{
    +
    +public:
    +
    +   enum sex { male, female, unknown };
    +
    +   long        _id;
    +   QString     _firstName;
    +   QString     _lastName;
    +   QDateTime   _birthDate;
    +   sex         _sex;
    +
    +   person() : _id(0), _sex(unknown) { ; }
    +   person(long id) : _id(id), _sex(unknown) { ; }
    +   virtual ~person() { ; }
    +
    +private:
    +
    +   void isValid(qx::QxInvalidValueX & invalidValues);
    +
    +};
    +
    +QX_REGISTER_HPP_MY_EXE(person, qx::trait::no_base_class_defined, 0)
    +
    +#endif // _CLASS_PERSON_H_
    +
    +
    + * fichier 'person.cpp' :
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/person.h"
    +#include "../include/global_validator.h"
    +
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_MY_EXE(person)
    +
    +namespace qx {
    +template <> void register_class(QxClass<person> & t)
    +{
    +   t.id(& person::_id, "id");
    +
    +   t.data(& person::_firstName, "firstName");
    +   t.data(& person::_lastName, "lastName");
    +   t.data(& person::_birthDate, "birthDate");
    +   t.data(& person::_sex, "sex");
    +
    +   QxValidatorX<person> * pAllValidator = t.getAllValidator();
    +   pAllValidator->add_NotEmpty("firstName");
    +   pAllValidator->add_NotEmpty("lastName", "a person must have a lastname");
    +   pAllValidator->add_CustomValidator(& person::isValid);
    +   pAllValidator->add_CustomValidator_QVariant(& validateFirstName, "firstName");
    +   pAllValidator->add_CustomValidator_DataType<QDateTime>(& validateDateTime, "birthDate");
    +}}
    +
    +void person::isValid(qx::QxInvalidValueX & invalidValues)
    +{
    +   // Cette m�thode est appel�e automatiquement par le module 'QxValidator' :
    +   // - avant d'ins�rer ou mettre � jour une instance de type 'person' par les fonctions du namespace 'qx::dao' ;
    +   // - en utilisant la fonction 'qx::validate()' avec pour param�tre une instance de type 'person'.
    +
    +   // L'enregistrement de la m�thode 'person::isValid()' est effectu� dans la fonction de mapping :
    +   // pAllValidator->add_CustomValidator(& person::isValid);
    +
    +   // Dans cette m�thode, il est possible de v�rifier n'importe quelle valeur de l'instance courante
    +   // Si une propri�t� est non valide, il suffit d'ins�rer un �l�ment dans la collection 'invalidValues'
    +
    +   // Remarque : cette m�thode est d�clar�e 'private' pour forcer l'utilisateur � utiliser la fonction 'qx::validate()'
    +   // Mais ce n'est pas une obligation : cette m�thode peut �tre d�clar�e 'public' ou 'protected'
    +
    +   // Par exemple, si on souhaite v�rifier la propri�t� '_sex' d'une personne :
    +   if ((_sex != male) && (_sex != female))
    +   { invalidValues.insert("le sexe de la personne doit �tre d�fini : masculin ou f�minin"); }
    +}
    +
    +
    + * fichier 'global_validator.h' :
    + + + + + + + +
    +
    // Les fonctions suivantes ('validateFirstName()' et 'validateDateTime()') sont globales (non li�es � une classe)
    +// Elles peuvent ainsi �tre utilis�es par plusieurs classes pour valider une propri�t� (par exemple : valider la saisie d'une adresse IP).
    +// Ces fonctions seront appel�es automatiquement par le module 'QxValidator' :
    +// - avant d'ins�rer ou mettre � jour une instance de classe par les fonctions du namespace 'qx::dao' ;
    +// - en utilisant la fonction 'qx::validate()'.
    + 
    +void validateFirstName(const QVariant & value, const qx::IxValidator * validator, qx::QxInvalidValueX & invalidValues)
    +{
    +   // Ici, on peut tester la valeur d'une propri�t� (convertie en type QVariant)
    +   // Si la valeur est invalide, il suffit d'ins�rer un message � la collection 'invalidValues'
    +
    +   // Par exemple, si la valeur ne doit jamais �tre �gale � "admin" :
    +   if (value.toString() == "admin")
    +   { invalidValues.insert("la valeur ne peut pas �tre �gale � 'admin'"); }
    +}
    +
    +void validateDateTime(const QDateTime & value, const qx::IxValidator * validator, qx::QxInvalidValueX & invalidValues)
    +{
    +   // Ici, on peut tester la valeur d'une propri�t� (en conservant son vrai type, ici il s'agit de tester une date-heure de type 'QDateTime')
    +   // Si la valeur est invalide, il suffit d'ins�rer un message � la collection 'invalidValues'
    +
    +   // Par exemple, si la date-heure doit forc�ment �tre renseign�e :
    +   if (! value.isValid())
    +   { invalidValues.insert("la date-heure doit �tre renseign�e et doit �tre valide"); }
    +}
    +
    +
    + * fichier 'main.cpp' :
    + + + + + + + +
    +
    person personValidate;
    +personValidate._lastName = "admin";
    +qx::QxInvalidValueX invalidValues = qx::validate(personValidate);
    +QString sInvalidValues = invalidValues.text();
    +qDebug("[QxOrm] test 'QxValidator' module :\n%s", qPrintable(sInvalidValues));
    +
    +
    + A l'ex�cution de ce bout de code, l'instance 'personValidate' est non valide : la + collection 'invalidValues' contient quatre �l�ments :
    + - "la valeur de la propri�t� 'firstName' ne peut pas �tre vide" ;
    + - "le sexe de la personne doit �tre d�fini : masculin ou f�minin" ;
    + - "la valeur ne peut pas �tre �gale � 'admin'" ;
    + - "la date-heure doit �tre renseign�e et doit �tre valide".
    +
    + Le module QxValidator fournit plusieurs validateurs pour effectuer des v�rifications + basiques : +
      +
    • add_NotNull() : v�rifie que la valeur n'est pas nulle ;
    • +
    • add_NotEmpty() : v�rifie que la cha�ne de caract�res n'est pas vide ;
    • +
    • add_MinValue() : v�rifie que la valeur num�rique n'est pas inf�rieure au param�tre + ;
    • +
    • add_MaxValue() : v�rifie que la valeur num�rique n'est pas sup�rieure au param�tre + ;
    • +
    • add_Range() : v�rifie que la valeur num�rique est comprise entre les deux + param�tres ;
    • +
    • add_MinDecimal() : v�rifie que la valeur d�cimale n'est pas inf�rieure au + param�tre ;
    • +
    • add_MaxDecimal() : v�rifie que la valeur d�cimale n'est pas sup�rieure au + param�tre ;
    • +
    • add_RangeDecimal() : v�rifie que la valeur d�cimale est comprise entre les deux + param�tres ;
    • +
    • add_MinLength() : v�rifie que la cha�ne de caract�res a une taille minimale ;
    • +
    • add_MaxLength() : v�rifie que la cha�ne de caract�res ne d�passe pas un certain + nombre de caract�res ;
    • +
    • add_Size() : v�rifie que la taille de la cha�ne de caract�res est comprise entre + les deux param�tres ;
    • +
    • add_DatePast() : v�rifie que la date-heure est dans le pass� ;
    • +
    • add_DateFuture() : v�rifie que la date-heure est dans le futur ;
    • +
    • add_RegExp() : v�rifie que la cha�ne de caract�res est compatible avec + l'expression r�guli�re pass�e en param�tre ;
    • +
    • add_EMail() : v�rifie que la cha�ne de caract�res correspond � un e-mail.
    • +
    + Comme dans l'exemple de la classe 'person', il est possible de d�finir �galement des + validateurs personnalis�s : ce sont des fonctions ou m�thodes de classe qui seront appel�es + automatiquement par le module QxValidator pour valider une propri�t� ou une instance de + classe.
    + Il existe trois types de validateurs personnalis�s : +
      +
    • add_CustomValidator() : m�thode de classe, la signature de la m�thode doit �tre + "void my_class::my_method(qx::QxInvalidValueX &)" ;
    • +
    • add_CustomValidator_QVariant() : fonction globale avec type QVariant + (propri�t� convertie en QVariant), la signature de la fonction doit �tre "void + my_validator(const QVariant &, const qx::IxValidator *, qx::QxInvalidValueX &)" ; +
    • +
    • add_CustomValidator_DataType() : fonction globale avec le type r�el de la + propri�t�, la signature de la fonction doit �tre "void my_validator(const T &, const + qx::IxValidator *, qx::QxInvalidValueX &)" ;
    • +
    + Remarque : � chaque validateur peut �tre associ� un groupe (param�tre optionnel pour + chaque m�thode add_XXX() de la classe qx::IxValidatorX).
    + Il est ainsi possible de cr�er des groupes de validation suivant le contexte d'ex�cution : par + exemple, valider la saisie d'une personne sur une IHM A ne n�cessite peut-�tre pas les m�mes + v�rifications que valider une personne sur une IHM B.
    + Pour ex�cuter la validation d'une instance pour un groupe donn� (par exemple "myGroup"), + il faut appeler la fonction suivante : "qx::QxInvalidValueX invalidValues = + qx::validate(personValidate, "myGroup");".
    +
    + Autre remarque : le module QxValidator d�finit des messages par d�faut lorsqu'une + contrainte n'est pas v�rifi�e.
    + Il est possible de red�finir ces messages par d�faut en modifiant la collection suivante : + "QHash * lstMessage = QxClassX::getAllValidatorMessage();".
    + Par exemple : "lstMessage->insert("min_value", "la valeur '%NAME%' doit �tre inf�rieure ou + �gale � '%CONSTRAINT%'");".
    + Les champs %NAME% et %CONSTRAINT% seront automatiquement remplac�s par les valeurs + correspondantes.
    + Pour modifier le message pour un validateur donn� (et non de mani�re globale), il faut utiliser + le param�tre optionnel disponible pour les m�thodes add_XXX() de la classe + qx::IxValidatorX.
    +
    +
    +

    G�rer la valeur NULL de + la base de donn�es

    +
    + Les bases de donn�es poss�dent la notion de valeur NULL : pour plus de d�tails sur la valeur + NULL, rendez-vous sur la + page Wikipedia.
    + La biblioth�que QxOrm permet de g�rer la valeur NULL de plusieurs fa�ons diff�rentes : +
      +
    • utilisation de la classe boost::optional fournie par boost ; +
    • +
    • utilisation de la classe QVariant fournie par Qt ;
    • +
    • utilisation de pointeurs ou pointeurs intelligents : un pointeur NULL est associ� � la + valeur NULL en base de donn�es.
    • +
    +
    +

    boost::optional +

    +
    + La classe boost::optional<T> fournie par boost est particuli�rement + adapt�e pour g�rer la notion de valeur NULL en base de donn�es.
    + Pour utiliser boost::optional<T> avec la biblioth�que QxOrm, il est n�cessaire + de d�finir l'option de compilation _QX_ENABLE_BOOST, ou bien d'inclure + l'en-t�te <QxExtras/QxBoostOptionalOnly.h>.
    + Voici un exemple de classe dont toutes les propri�t�s (sauf la cl� primaire) peuvent �tre + NULL en utilisant boost::optional :
    +
    + + + + + + + +
    +
    #ifndef _PERSON_H_
    +#define _PERSON_H_
    +
    +class person
    +{
    +public:
    +   long id;
    +   boost::optional<QString> firstName;
    +   boost::optional<QString> lastName;
    +   boost::optional<QDateTime> birthDate;  
    +
    +   person() : id(0) { ; }
    +   virtual ~person() { ; }
    +};
    +
    +#endif // _PERSON_H_
    +
    +
    + La classe boost::optional<T> se manipule facilement : rendez-vous sur la documentation fournie par boost pour plus de d�tails. +

    +
    +

    QVariant

    +
    + La classe QVariant fournie + par Qt permet �galement de g�rer la notion de valeur NULL en base de donn�es.
    + Voici un exemple de classe dont toutes les propri�t�s (sauf la cl� primaire) peuvent �tre + NULL en utilisant QVariant + :
    +
    + + + + + + + +
    +
    #ifndef _PERSON_H_
    +#define _PERSON_H_
    +
    +class person
    +{
    +public:
    +   long id;
    +   QVariant firstName;
    +   QVariant lastName;
    +   QVariant birthDate;  
    +
    +   person() : id(0) { ; }
    +   virtual ~person() { ; }  
    +};
    +
    +#endif // _PERSON_H_
    +
    +
    + Cette solution a pour d�savantage de perdre le type de donn�e compar� � boost::optional<T>.
    + Il est donc recommand� d'utiliser boost::optional<T> pour g�rer la valeur NULL avec la + biblioth�que QxOrm. +

    +
    +
    +

    H�ritage et + polymorphisme

    +
    + On retrouve g�n�ralement dans les diff�rents outils de type ORM trois diff�rentes + strat�gies pour g�rer la notion d'h�ritage avec la base de donn�es : +
      +
    • Single Table Inheritance (une seule table regroupant toutes + les propri�t�s d'une hi�rarchie d'h�ritage de classes) ; +
    • +
    • Class Table Inheritance (� chaque classe d'une hi�rarchie + d'h�ritage est associ�e une table dans la base de donn�es) ; +
    • +
    • Concrete Table Inheritance (une table par classe concr�te + dans la hi�rarchie d'h�ritage). +
    • +
    + QxOrm utilise par d�faut la strat�gie Concrete Table Inheritance (les autres + strat�gies ne sont pas fonctionnelles � l'heure actuelle).
    + De nombreux tutoriaux et forums sont disponibles sur internet pour plus de d�tails sur cette + notion d'h�ritage.
    + Un exemple d'utilisation avec une classe de base se trouve dans le dossier + ./test/qxDllSample/dll2/ avec la classe BaseClassTrigger. +

    +
    +

    Interface + qx::IxPersistable (classe abstraite)

    +
    + L'interface qx::IxPersistable (ou classe abstraite) dispose uniquement de + m�thodes virtuelles pures.
    + Elle permet d'avoir une classe de base commune pour appeler les fonctions de persistance sans + conna�tre le type r�el de l'instance courante (notion de polymorphisme).
    + La biblioth�que QxOrm n'impose pas de travailler avec une classe de base pour enregistrer un + type persistant dans le contexte QxOrm, cependant il est parfois utile de disposer d'une + interface afin d'�crire des algorithmes g�n�riques.
    +
    + La classe qx::IxPersistable met � disposition les m�thodes virtuelles suivantes (pour + plus d'informations sur ces m�thodes, rendez-vous sur la documentation en ligne de la biblioth�que QxOrm) :
    +
    +
    +
    virtual long qxCount(const qx::QxSqlQuery & query = qx::QxSqlQuery(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxFetchById(const QVariant & id = QVariant(), const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxFetchAll(qx::IxCollection & list, const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxFetchByQuery(const qx::QxSqlQuery & query, qx::IxCollection & list, const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxInsert(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxUpdate(const qx::QxSqlQuery & query = qx::QxSqlQuery(), const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxSave(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDeleteById(const QVariant & id = QVariant(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDeleteAll(QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDeleteByQuery(const qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDestroyById(const QVariant & id = QVariant(), QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDestroyAll(QSqlDatabase * pDatabase = NULL);
    +virtual QSqlError qxDestroyByQuery(const qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL);
    +virtual qx_bool qxExist(const QVariant & id = QVariant(), QSqlDatabase * pDatabase = NULL);
    +virtual qx::QxInvalidValueX qxValidate(const QStringList & groups = QStringList());
    +virtual qx::IxPersistableCollection_ptr qxNewPersistableCollection() const;
    +virtual qx::IxClass * qxClass() const;
    +
    +
    + Par exemple, � partir d'une liste de pointeurs de type qx::IxPersistable, il est possible + d'enregistrer les �l�ments dans plusieurs tables diff�rentes de la base de donn�es de la fa�on + suivante :
    +
    + + + + + + + +
    +
    QList<qx::IxPersistable *> lst = ...;
    +foreach(qx::IxPersistable * p, lst)
    +{
    +   QSqlError daoError = p->qxSave();
    +   if (daoError.isValid()) { /* an error occured */ }
    +   // etc...
    +}
    +
    +
    + Pour impl�menter l'interface qx::IxPersistable, il faut : +
      +
    • faire h�riter la classe persistante du type qx::IxPersistable ;
    • +
    • dans la d�finition de la classe (myClass.h par exemple), ajouter la macro + QX_PERSISTABLE_HPP(myClass) ; +
    • +
    • dans l'impl�mentation de la classe (myClass.cpp par exemple), ajouter la macro + QX_PERSISTABLE_CPP(myClass). +
    • +
    + Par exemple, impl�menter l'interface qx::IxPersistable pour la classe author du tutoriel qxBlog revient � �crire + (les modifications par rapport au code du tutoriel apparaissent en gras) :
    +
    + + + + + + + +
    +
    #ifndef _QX_BLOG_AUTHOR_H_
    +#define _QX_BLOG_AUTHOR_H_
    +
    +class blog;
    +
    +class QX_BLOG_DLL_EXPORT author : public qx::IxPersistable
    +{
    +   QX_PERSISTABLE_HPP(author)
    +public:
    +// -- typedef
    +   typedef boost::shared_ptr<blog> blog_ptr;
    +   typedef std::vector<blog_ptr> list_blog;
    +// -- enum
    +   enum enum_sex { male, female, unknown };
    +// -- propri�t�s
    +   QString     m_id;
    +   QString     m_name;
    +   QDate       m_birthdate;
    +   enum_sex    m_sex;
    +   list_blog   m_blogX;
    +// -- constructeur, destructeur virtuel
    +   author() : m_id(0), m_sex(unknown) { ; }
    +   virtual ~author() { ; }
    +// -- m�thodes
    +   int age() const;
    +};
    +
    +QX_REGISTER_PRIMARY_KEY(author, QString)
    +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0)
    +
    +typedef boost::shared_ptr<author> author_ptr;
    +typedef qx::QxCollection<QString, author_ptr> list_author;
    +
    +#endif // _QX_BLOG_AUTHOR_H_
    +
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/author.h"
    +#include "../include/blog.h"
    +
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_BLOG(author)
    +QX_PERSISTABLE_CPP(author)
    +
    +namespace qx {
    +template <> void register_class(QxClass<author> & t)
    +{
    +   t.id(& author::m_id, "author_id");
    +
    +   t.data(& author::m_name, "name");
    +   t.data(& author::m_birthdate, "birthdate");
    +   t.data(& author::m_sex, "sex");
    +
    +   t.relationOneToMany(& author::m_blogX, "list_blog", "author_id");
    +
    +   t.fct_0<int>(& author::age, "age");
    +}}
    +
    +int author::age() const
    +{
    +   if (! m_birthdate.isValid()) { return -1; }
    +   return (QDate::currentDate().year() - m_birthdate.year());
    +}
    +
    +
    +
    + Remarque : le projet de test ./test/qxDllSample/dll1/ met � disposition une sorte + de 'super classe de base' : la classe qx::QxPersistable impl�mente l'interface qx::IxPersistable et h�rite de QObject.
    + Le m�canisme SIGNAL-SLOT de Qt peut donc �tre utilis� avec cette classe, ce qui + peut �tre int�ressant par exemple pour la notion de d�clencheurs (ou trigger).
    + La classe qx::QxPersistable met �galement � disposition des m�thodes virtuelles qu'il est + possible de surcharger pour g�rer notamment la notion de validation des donn�es avec le module + QxValidator.
    + La classe qx::QxPersistable ne fait pas partie de la distribution de QxOrm, mais il est + possible de la copier-coller dans un projet afin de profiter de ses fonctionnalit�s : + +
    +
    +

    Utiliser le pattern C++ + PIMPL (Private Implementation idiom ou d-pointer)

    +
    + D�finition du site + cppreference : "Pointer to implementation" or "pImpl" is a C++ programming technique that + removes implementation details of a class from its object representation by placing them in a + separate class, accessed through an opaque pointer. + This technique is used to construct C++ library interfaces with stable ABI and to reduce + compile-time dependencies. +

    + Les + avantages � utiliser le pattern PIMPL pour d�finir une classe persistente enregistr�e + dans le contexte QxOrm : +
      +
    • Compilation Firewall : si l'impl�mentation priv�e change, le code client n'a pas + besoin d'�tre recompil� ;
    • +
    • R�duction des temps de compilation : les fichiers d'en-t�tes (*.h, *.hpp) sont + moins volumineux ;
    • +
    • Compatibilit� binaire : vous pouvez d�velopper plusieurs versions d'une + biblioth�que sans casser la compatibilit� ;
    • +
    • R�duction de la taille des ex�cutables g�n�r�s.
    • +
    + Les + inconvients du pattern PIMPL : +
      +
    • L�g�re baisse des performances : n�cessit� d'utiliser un niveau d'indirection + suppl�mentaire avec le pointeur opaque ;
    • +
    • N�cessit� d'allouer un pointeur par instance (peut causer des probl�mes de memory + fragmentation).
    • +
    +
    + La biblioth�que QxOrm fournit un projet de test o� toutes les classes persistantes sont cod�es + en utilisant le pattern PIMPL : qxBlogPImpl (avec gestion des relations).
    + Il est �galement possible d'utiliser l'application + QxEntityEditor pour g�n�rer facilement et automatiquement toutes les + classes persistantes d'un projet C++ avec l'option PIMPL.
    +
    + Exemple de classe persistante C++ enregistr�e dans le contexte QxOrm en utilisant le pattern + PIMPL (avec relations 1-n, n-1 et n-n) :
    +
    + + + + + + + +
    +
    #ifndef _QX_BLOG_BLOG_H_
    +#define _QX_BLOG_BLOG_H_
    +
    +class author;
    +class comment;
    +class category;
    +
    +class QX_BLOG_DLL_EXPORT blog
    +{
    +
    +   QX_REGISTER_FRIEND_CLASS(blog)
    +
    +private:
    +
    +   struct blog_impl;
    +   std::unique_ptr<blog_impl> m_pImpl; //!< Private implementation idiom
    +
    +public:
    +
    +   blog();
    +   virtual ~blog();
    +
    +   blog(const blog & other);
    +   blog & operator=(const blog & other);
    +
    +#ifdef Q_COMPILER_RVALUE_REFS
    +   blog(blog && other) Q_DECL_NOEXCEPT;
    +   blog & operator=(blog && other) Q_DECL_NOEXCEPT;
    +#endif // Q_COMPILER_RVALUE_REFS
    +
    +   long id() const;
    +   QString text() const;
    +   QDateTime dateCreation() const;
    +
    +   void setId(long l);
    +   void setText(const QString & s);
    +   void setDateCreation(const QDateTime & d);
    +
    +   std::shared_ptr<author> & getAuthor();
    +   QList< std::shared_ptr<comment> > & listOfComments();
    +   qx::QxCollection<long, QSharedPointer<category> > & listOfCategories();
    +
    +};
    +
    +QX_REGISTER_HPP_QX_BLOG(blog, qx::trait::no_base_class_defined, 0)
    +
    +typedef std::shared_ptr<blog> blog_ptr;
    +typedef std::vector<blog_ptr> list_blog;
    +
    +#endif // _QX_BLOG_BLOG_H_
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/blog.h"
    +#include "../include/author.h"
    +#include "../include/comment.h"
    +#include "../include/category.h"
    +
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_BLOG(blog)
    +
    +struct Q_DECL_HIDDEN blog::blog_impl
    +{
    +   long           m_id;
    +   QString        m_text;
    +   QDateTime      m_dt_creation;
    +   author_ptr     m_author;
    +   list_comment   m_commentX;
    +   list_category  m_categoryX;
    +
    +   blog_impl() : m_id(0) { ; }
    +   ~blog_impl() { ; }
    +};
    +
    +namespace qx {
    +template <> void register_class(QxClass<blog> & t)
    +{
    +   IxDataMember * pImpl = t.pimpl(& blog::m_pImpl);
    +
    +   t.id(& blog::blog_impl::m_id, "blog_id", 0, pImpl);
    +
    +   t.data(& blog::blog_impl::m_text, "blog_text", 0, true, true, pImpl);
    +   t.data(& blog::blog_impl::m_dt_creation, "date_creation", 0, true, true, pImpl);
    +
    +   t.relationManyToOne(& blog::blog_impl::m_author, "author_id", 0, pImpl);
    +   t.relationOneToMany(& blog::blog_impl::m_commentX, "list_comment", "blog_id", 0, pImpl);
    +   t.relationManyToMany(& blog::blog_impl::m_categoryX, "list_category", "category_blog", "blog_id", "category_id", 0, pImpl);
    +}}
    +
    +blog::blog() : m_pImpl(new blog_impl()) { ; }
    +
    +blog::~blog() { ; }
    +
    +blog::blog(const blog & other) : m_pImpl(new blog_impl(* other.m_pImpl)) { ; }
    +
    +blog & blog::operator=(const blog & other)
    +{
    +   if (this != (& other)) { (* m_pImpl) = (* other.m_pImpl); }
    +   return (* this);
    +}
    +
    +#ifdef Q_COMPILER_RVALUE_REFS
    +blog::blog(blog && other) Q_DECL_NOEXCEPT : m_pImpl(std::move(other.m_pImpl)) { ; }
    +blog & blog::operator=(blog && other) Q_DECL_NOEXCEPT { if (this != (& other)) { m_pImpl = std::move(other.m_pImpl); }; return (* this); }
    +#endif // Q_COMPILER_RVALUE_REFS
    +
    +long blog::id() const { return m_pImpl->m_id; }
    +
    +QString blog::text() const { return m_pImpl->m_text; }
    +
    +QDateTime blog::dateCreation() const { return m_pImpl->m_dt_creation; }
    +
    +void blog::setId(long l) { m_pImpl->m_id = l; }
    +
    +void blog::setText(const QString & s) { m_pImpl->m_text = s; }
    +
    +void blog::setDateCreation(const QDateTime & d) { m_pImpl->m_dt_creation = d; }
    +
    +std::shared_ptr<author> & blog::getAuthor() { return m_pImpl->m_author; }
    +
    +QList< std::shared_ptr<comment> > & blog::listOfComments() { return m_pImpl->m_commentX; }
    +
    +qx::QxCollection<long, QSharedPointer<category> > & blog::listOfCategories() { return m_pImpl->m_categoryX; }
    +
    +

    +
    +

    Persister des types + personnalis�s

    +
    + La biblioth�que QxOrm permet de persister n'importe quel type, m�me si ce dernier n'est pas + enregistr� dans le contexte QxOrm par la m�thode qx::register_class<T>().
    +
    + Il est n�cessaire d'�crire les fonctions de s�rialisation de la biblioth�que boost, en utilisant + la m�thode non intrusive (puisque le code source n'est pas disponible ou ne peut pas �tre + modifi�). + Pour plus d'informations sur la s�rialisation des donn�es avec la biblioth�que boost, + rendez-vous sur le tutoriel de developpez.com.
    +
    + Par exemple, imaginons une classe 'ExtObject3D' provenant d'une biblioth�que tierce et + dont le code source n'est pas disponible ou ne peut pas �tre modifi�. + Voici le code n�cessaire pour pouvoir persister une instance de type 'ExtObject3D' en + base de donn�es :
    +
    + + + + + + + +
    +
    #ifndef _PERSIST_EXTOBJECT3D_H_
    +#define _PERSIST_EXTOBJECT3D_H_
    +
    +#include "ExtObject3D.h"
    +
    +#include <boost/serialization/serialization.hpp>
    +#include <boost/serialization/split_free.hpp>
    +#include <boost/serialization/nvp.hpp>
    + 
    +namespace boost {
    +namespace serialization {
    +
    +template <class Archive>
    +void save(Archive & ar, const ExtObject3D & t, unsigned int version)
    +{
    +   Q_UNUSED(version);
    +   double x(t.getX()), y(t.getY()), z(t.getZ()), angle(t.getAngle());
    +
    +   ar << boost::serialization::make_nvp("x", x);
    +   ar << boost::serialization::make_nvp("y", y);
    +   ar << boost::serialization::make_nvp("z", z);
    +   ar << boost::serialization::make_nvp("angle", angle);
    +}
    +
    +template <class Archive>
    +void load(Archive & ar, ExtObject3D & t, unsigned int version)
    +{
    +   Q_UNUSED(version);
    +   double x(0.0), y(0.0), z(0.0), angle(0.0);
    +
    +   ar >> boost::serialization::make_nvp("x", x);
    +   ar >> boost::serialization::make_nvp("y", y);
    +   ar >> boost::serialization::make_nvp("z", z);
    +   ar >> boost::serialization::make_nvp("angle", angle);
    +
    +   t.setX(x);
    +   t.setY(y);
    +   t.setZ(z);
    +   t.setAngle(angle);
    +}
    +
    +} // namespace serialization
    +} // namespace boost
    + 
    +BOOST_SERIALIZATION_SPLIT_FREE(ExtObject3D)
    +
    +#endif // _PERSIST_EXTOBJECT3D_H_
    +
    +
    + Le code ci-dessus est suffisant pour persister une instance de type 'ExtObject3D' en base + de donn�es : il est ainsi possible d'utiliser une propri�t� de type 'ExtObject3D' dans + une classe persistante enregistr�e dans le contexte QxOrm. + Cette propri�t� peut �tre mapp�e sur une colonne de type TEXT ou VARCHAR en base + de donn�es.
    +
    + Le comportement par d�faut de la biblioth�que QxOrm est le suivant : l'instance est s�rialis�e + au format XML avant d'�tre ins�r�e ou mise � jour en base de donn�es. + Ce comportement par d�faut peut �tre utile par exemple si l'on souhaite enregistrer une + collection d'objets sans vouloir faire de relation (et donc g�rer une autre table dans la base + de donn�es). + Par exemple, si l'on utilise une propri�t� de type std::vector<mon_objet> dans une + classe persistante sans relation associ�e, la liste d'�l�ments sera automatiquement enregistr�e + au format XML en base de donn�es.
    +
    + Remarque : ce comportement par d�faut peut �tre facilement modifi� pour un type donn�. + Le moteur QtSql utilise le type QVariant pour faire le lien entre le code C++ et + la base de donn�es. + Le type QVariant peut contenir du texte, des valeurs num�riques, du binaire, etc. + Il peut donc �tre int�ressant de sp�cialiser le comportement par d�faut (s�rialisation XML) si + l'on souhaite stocker des donn�es au format binaire ou bien optimiser les performances (la + s�rialisation XML peut �tre couteuse en temps d'ex�cution). + Il suffit de proposer (en plus des fonctions de s�rialisation boost) les conversions n�cessaires + en QVariant, par exemple avec la classe 'ExtObject3D' :
    +
    + + + + + + + +
    +
    namespace qx {
    +namespace cvt {
    +namespace detail {
    +
    +template <> struct QxConvert_ToVariant< ExtObject3D > {
    +static inline QVariant toVariant(const ExtObject3D & t, const QString & format, int index, qx::cvt::context::ctx_type ctx)
    +{ /* Ici je convertis ExtObject3D en QVariant */ } };
    +
    +template <> struct QxConvert_FromVariant< ExtObject3D > {
    +static inline qx_bool fromVariant(const QVariant & v, ExtObject3D & t, const QString & format, int index, qx::cvt::context::ctx_type ctx)
    +{ /* Ici je convertis QVariant en ExtObject3D */; return qx_bool(true); } };
    +
    +} // namespace detail
    +} // namespace cvt
    +} // namespace qx
    +
    +

    + Remarque : Voici un template pour cr�er un type personnalis� persistable : +

    + + + + + + + +
    +
    #ifndef _MY_CUSTOM_PERSISTABLE_TYPE_H_
    +#define _MY_CUSTOM_PERSISTABLE_TYPE_H_
    +
    +#ifdef _MSC_VER
    +#pragma once
    +#endif
    +
    +#include <QxOrm.h>
    +
    +class MyPersistableType
    +{
    +   /* What you want here */
    +};
    +
    +QX_REGISTER_CLASS_NAME(MyPersistableType)
    +QX_CLASS_VERSION(MyPersistableType, 0)
    +
    +QDataStream & operator<< (QDataStream & stream, const MyPersistableType & t)
    +{
    +   /* Your implementation here */
    +}
    +
    +QDataStream & operator>> (QDataStream & stream, MyPersistableType & t)
    +{
    +   /* Your implementation here */
    +}
    +
    +namespace qx {
    +namespace cvt {
    +namespace detail {
    +
    +template <> struct QxConvert_ToVariant< MyPersistableType > {
    +static inline QVariant toVariant(const MyPersistableType & t, const QString & format, int index, qx::cvt::context::ctx_type ctx)
    +{
    +   /* Here I convert from MyPersistableType to QVariant */
    +} };
    +
    +template <> struct QxConvert_FromVariant< MyPersistableType > {
    +static inline qx_bool fromVariant(const QVariant & v, MyPersistableType & t, const QString & format, int index, qx::cvt::context::ctx_type ctx)
    +{
    +   /* Here I convert from QVariant to MyPersistableType */
    +   return qx_bool(true);
    +} };
    +
    +} // namespace detail
    +} // namespace cvt
    +} // namespace qx
    +
    +#ifndef _QX_NO_JSON
    +
    +namespace qx {
    +namespace cvt {
    +namespace detail {
    +
    +template <>
    +struct QxConvert_ToJson< MyPersistableType >
    +{
    +   static inline QJsonValue toJson(const MyPersistableType & t, const QString & format)
    +   {
    +      /* Your implementation here */
    +   }
    +};
    +
    +template <>
    +struct QxConvert_FromJson< MyPersistableType >
    +{
    +   static inline qx_bool fromJson(const QJsonValue & j, MyPersistableType & t, const QString & format)
    +   {
    +      /* Your implementation here */
    +   }
    +};
    +
    +} // namespace detail
    +} // namespace cvt
    +} // namespace qx
    +
    +#endif // _QX_NO_JSON
    +
    +// ------------------------------------
    +// If you are using boost serialization, you have also to implement save/load functions like above 'ExtObject3D' example
    +// ------------------------------------
    +
    +#endif // _MY_CUSTOM_PERSISTABLE_TYPE_H_
    +
    +

    +
    +

    G�n�rer le sch�ma DDL SQL + de la base de donn�es

    +
    + + !!! Il est fortement recommand� d'utiliser + l'application QxEntityEditor pour g�rer cette probl�matique !!! +
    +
    + La biblioth�que QxOrm ne fournit pas de m�canisme pour g�rer automatiquement la cr�ation et mise + � jour des tables dans la base de donn�es.
    + En effet, la fonction qx::dao::create_table<T> doit �tre utilis�e uniquement pour + cr�er des prototypes.
    + Il est fortement recommand� d'utiliser un outil sp�cifique � chaque SGBD pour cr�er et maintenir + les tables de la base de donn�es (par exemple Navicat pour MySql, pgAdmin + pour PostgreSQL, SQLite Manager pour SQLite, etc.).
    + De plus, un outil sp�cifique � chaque SGBD permet d'appliquer certaines optimisations (ajout + d'index par exemple).
    +
    + Cependant, il peut �tre int�ressant pour certaines applications de ne pas avoir � g�rer + manuellement les tables de la base de donn�es.
    + Dans ce cas, il est possible de cr�er une fonction C++ pour parcourir la liste des classes + persistantes enregistr�es dans le contexte QxOrm (en utilisant le moteur d'introspection de la + biblioth�que) et ainsi cr�er un script SQL de g�n�ration et mise � jour des tables de la base de + donn�es.
    +
    + La biblioth�que QxOrm fournit une fonction C++ cr��e uniquement � titre d'exemple : elle peut + donc servir de base de travail pour cr�er sa propre fonction de g�n�ration de script SQL.
    + Cette fonction se trouve dans le fichier ./src/QxRegister/QxClassX.cpp et se nomme QString + qx::QxClassX::dumpSqlSchema().
    + Elle g�n�re un script SQL et le renvoie sous forme de QString : il est possible d'adapter + cette fonction pour g�n�rer un fichier contenant le script SQL ou bien appliquer chaque + instruction SQL directement au SGBD.
    +
    + Voici un exemple d'impl�mentation propos� par dodobibi pour + g�rer une base PostgreSQL : cet exemple g�re les �volutions futures de son application + (ajout de colonnes dans une table existante, ajout d'index sur une colonne existante, etc.).
    + Au lancement de l'application, le num�ro de version est indiqu� de la fa�on suivante :
    +
    + + + + + + + +
    +
    QApplication app(argc, argv);
    +app.setProperty("DomainVersion", 1); // Version increment�e � chaque compilation et diffusion de l'application
    +
    +
    + Une table de la base de donn�es permet de stocker le num�ro de version courant.
    + Une classe persistante C++ est mapp�e sur cette table de la fa�on suivante :
    +
    + + + + + + + +
    +
    #ifndef _DATABASE_VERSION_H_
    +#define _DATABASE_VERSION_H_
    + 
    +class MY_DLL_EXPORT DatabaseVersion
    +{
    +public:
    +  QString name;
    +  long version;
    +};
    +
    +QX_REGISTER_HPP_MY_APP(DatabaseVersion, qx::trait::no_base_class_defined, 0)
    +
    +#endif // _DATABASE_VERSION_H_
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +#include "../include/DatabaseVersion.h"
    +#include <QxOrm_Impl.h>
    + 
    +QX_REGISTER_CPP_MY_APP(DatabaseVersion)
    +
    +namespace qx {
    +template <> void register_class(QxClass<DatabaseVersion> & t)
    +{
    +  t.id(& DatabaseVersion::name, "name");
    +  t.data(& DatabaseVersion::version, "version");
    +}}
    +
    +
    + Avec la classe DatabaseVersion, il est possible de v�rifier si la version de la base de + donn�es est � jour.
    + C'est le r�le de la fonction isDatabaseVersionOld() :
    +
    + + + + + + + +
    +
    bool isDatabaseVersionOld()
    +{
    +  DatabaseVersion dbVersion;
    +  dbVersion.name = "MyAppName";
    +  QSqlError err = qx::dao::fetch_by_id(dbVersion);
    +  if (err.isValid()) { qAssert(false); return false; }
    +  return (dbVersion.version < qApp->property("DomainVersion").toInt());
    +}
    +
    +
    + Si au lancement de l'application, la fonction isDatabaseVersionOld() renvoie true, + alors la mise � jour de la base de donn�es est effectu�e de la fa�on suivante :
    +
    + + + + + + + +
    +
    void updateDatabaseVersion()
    +{
    +  try
    +  {
    +    int domainVersion = qApp->property("DomainVersion").toInt();
    +
    +    // On se connecte avec un utilisateur de la base de donn�es qui a les droits de modifications du sch�ma
    +    QSqlDatabase db = qx::QxSqlDatabase::getSingleton()->getDatabaseCloned();
    +    db.setUserName("MyAdminLogin");
    +    db.setPassword("MyAdminPassword");
    +
    +    // On s'assure que la session d�marre une transaction et l�ve une exception � la moindre erreur
    +    qx::QxSession session(db, true, true);
    +
    +    // On "fetch" la version de la base de donn�es avec un verrou pour �viter les modifications concurrentes !
    +    // Si plusieurs utilisateurs lancent l'application en m�me temps et qu'une mise � jour
    +    // est n�cessaire, le premier fera la mise � jour, et les autres seront en attente
    +    DatabaseVersion dbVersion;
    +    session.fetchByQuery(qx_query("WHERE name='MyAppName' FOR UPDATE"), dbVersion);
    +
    +    // Pour les autres utilisateurs, une fois le verrou lev�, on v�rifie si la mise � jour est toujours n�cessaire
    +    if (dbVersion.version >= domainVersion) { return; }
    +
    +    // On ex�cute chaque instruction SQL avec la variable "query"
    +    QSqlQuery query(db);
    +
    +    // On r�cup�re toutes les classes persistantes C++ enregistr�es dans le contexte QxOrm
    +    qx::QxCollection<QString, qx::IxClass *> * pAllClasses = qx::QxClassX::getAllClasses();
    +    if (! pAllClasses) { qAssert(false); return; }
    +
    +    // on r�cup�re la liste des tables existantes dans la base (fonction de Qt)
    +    QStringList tables = db.tables();
    +
    +    for (long k = 0; k < pAllClasses->count(); k++)
    +    {
    +      qx::IxClass * pClass = pAllClasses->getByIndex(k);
    +      if (! pClass) { continue; }
    +
    +      // Filtre les classes non persistantes
    +      if (pClass->isKindOf("qx::service::IxParameter") || pClass->isKindOf("qx::service::IxService")) { continue; }
    +
    +      // Filtre les classes � jour : si la version de pClass est <= � la version enregistr�e dans la base, la mise � jour n'est pas n�cessaire
    +      if (pClass->getVersion() <= dbVersion.version) { continue; }
    +
    +      // On cr�e la table si elle n'existe pas, et on d�finit son propri�taire
    +      if (! tables.contains(pClass->getName()))
    +      {
    +        query.exec("CREATE TABLE " + pClass->getName() + " ( ) WITH (OIDS = FALSE);"
    +                   "ALTER TABLE " + pClass->getName() + " OWNER TO \"MyAdminLogin\";");
    +        session += query.lastError();
    +      }
    +
    +      // On ajoute les colonnes � la table si elles n'existent pas
    +      qx::IxDataMemberX * pDataMemberX = pClass->getDataMemberX();
    +      for (long l = 0; (pDataMemberX && (l < pDataMemberX->count_WithDaoStrategy())); l++)
    +      {
    +        qx::IxDataMember * p = pDataMemberX->get_WithDaoStrategy(l);
    +        if (! p || (p->getVersion() <= dbVersion.version)) { continue; }
    +
    +        query.exec("ALTER TABLE " + pClass->getName() + " ADD COLUMN " + p->getName() + " " + p->getSqlType() + ";");
    +        session += query.lastError();
    +
    +        if (p->getIsPrimaryKey()) // PRIMARY KEY
    +        {
    +          query.exec("ALTER TABLE " + pClass->getName() + " ADD PRIMARY KEY (" + p->getName() + ");");
    +          session += query.lastError();
    +        }
    +
    +        if (p->getAllPropertyBagKeys().contains("INDEX")) // INDEX
    +        {
    +          query.exec("CREATE INDEX " + pClass->getName() + "_" + p->getName() + "_idx" + 
    +                     " ON " + pClass->getName() + " USING " + p->getPropertyBag("INDEX").toString() + " (" + p->getName() + ");");
    +          session += query.lastError();
    +        }
    +
    +        if (p->getNotNull()) // NOT NULL
    +        {
    +          query.exec("ALTER TABLE " + pClass->getName() + " ALTER COLUMN " + p->getName() + " SET NOT NULL;");
    +          session += query.lastError();
    +        }
    +
    +        if (p->getAutoIncrement()) // AUTO INCREMENT
    +        {
    +          query.exec("CREATE SEQUENCE " + pClass->getName() + "_" + p->getName() + "_seq" + "; "
    +                     "ALTER TABLE " + pClass->getName() + "_" + p->getName() + "_seq" + " OWNER TO \"MyAdminLogin\"; "
    +                     "ALTER TABLE " + pClass->getName() + " ALTER COLUMN " + p->getName() + " " +
    +                     "SET DEFAULT nextval('" + pClass->getName() + "_" + p->getName() + "_seq" + "'::regclass);");
    +          session += query.lastError();
    +        }
    +
    +        if (p->getDescription() != "") // DESCRIPTION
    +        {
    +          // $$ceci est un texte ne n�cessitant pas de caract�res d'�chappement dans postgres grace aux doubles dolars$$
    +          query.exec("COMMENT ON COLUMN " + pClass->getName() + "." + p->getName() + " IS $$" + p->getDescription() + "$$ ;");
    +          session += query.lastError();
    +        }
    +      }
    +    }
    +
    +    // On enregistre la version courante de la base de donn�es
    +    dbVersion.version = domainVersion;
    +    session.save(dbVersion);
    +
    +    // Fin du block "try" : la session est d�truite => commit ou rollback automatique
    +    // De plus, un commit ou rollback sur la transaction l�ve automatiquement le verrou pos� pr�c�demment
    +  }
    +  catch (const qx::dao::sql_error & err)
    +  {
    +    QSqlError sqlError = err.get();
    +    qDebug() << sqlError.databaseText();
    +    qDebug() << sqlError.driverText();
    +    qDebug() << sqlError.number();
    +    qDebug() << sqlError.type();
    +  }
    +}
    +
    +
    + Remarque : le code pr�c�dent (tout comme la fonction qx::QxClassX::dumpSqlSchema()) peut �tre modifi� pour s'adapter + aux besoins sp�cifiques d'une application.
    + Par exemple, il pourrait �tre int�ressant de cr�er par d�faut une seconde table (en plus de la + table DatabaseVersion) pour enregistrer la liste des classes persistantes enregistr�es + dans le contexte QxOrm : ainsi, au lieu d'utiliser la fonction propos�e par Qt + "db.tables()", il serait possible de r�cup�rer toutes les tables mapp�es sur des classes + persistantes avec des informations suppl�mentaires (num�ro de version pour chaque table, nombre + de colonnes enregistr�es dans le contexte QxOrm, description de chaque table, etc.). +

    +
    +

    Associer un type SQL � + une classe C++

    +
    + Chaque base de donn�es propose des types SQL diff�rents pour stocker l'information.
    + La biblioth�que QxOrm propose une association par d�faut pour les classes C++ les plus + fr�quemment utilis�es dans un programme.
    + Voici cette association par d�faut :
    +
    + + + + + + + +
    +
    "bool" <-> "SMALLINT"
    +"qx_bool" <-> "SMALLINT"
    +"short" <-> "SMALLINT"
    +"int" <-> "INTEGER"
    +"long" <-> "INTEGER"
    +"long long" <-> "INTEGER"
    +"float" <-> "FLOAT"
    +"double" <-> "FLOAT"
    +"long double" <-> "FLOAT"
    +"unsigned short" <-> "SMALLINT"
    +"unsigned int" <-> "INTEGER"
    +"unsigned long" <-> "INTEGER"
    +"unsigned long long" <-> "INTEGER"
    +"std::string" <-> "TEXT"
    +"std::wstring" <-> "TEXT"
    +"QString" <-> "TEXT"
    +"QVariant" <-> "TEXT"
    +"QUuid" <-> "TEXT"
    +"QDate" <-> "DATE"
    +"QTime" <-> "TIME"
    +"QDateTime" <-> "TIMESTAMP"
    +"QByteArray" <-> "BLOB"
    +"qx::QxDateNeutral" <-> "TEXT"
    +"qx::QxTimeNeutral" <-> "TEXT"
    +"qx::QxDateTimeNeutral" <-> "TEXT"
    +
    +
    + Si le type SQL propos� par d�faut par la biblioth�que QxOrm ne correspond pas � la base de + donn�es utilis�e, il peut facilement �tre modifi� (de mani�re globale � toute l'application) en + utilisant la collection suivante :
    +
    + + + + + + + +
    +
    QHash<QString, QString> * lstSqlType = qx::QxClassX::getAllSqlTypeByClassName();
    +lstSqlType->insert("QString", "VARCHAR(255)");
    +lstSqlType->insert("std::string", "VARCHAR(255)");
    +// etc.
    +
    +
    + Pour modifier le type SQL de mani�re sp�cifique pour une colonne d'une table de la base de + donn�es, il faut d�finir le type SQL dans la fonction de mapping de QxOrm :
    +
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<MyClass> & t)
    +{
    +  //...
    +  IxDataMember * p =  t.data(& MyClass::m_MyProperty, "my_property");
    +  p->setSqlType("VARCHAR(255)");
    +  //...
    +}}
    +
    +
    + Pour les classes non support�es par d�faut par la biblioth�que QxOrm (voir ce chapitre du manuel + utilisateur : Persister des types personnalis�s), il est + possible d'associer un type SQL par d�faut en utilisant la macro suivante (en dehors de tout + namespace) :
    +
    + + + + + + + +
    +
    QX_REGISTER_TRAIT_GET_SQL_TYPE(MyClass, "my_sql_type")
    +
    +

    +
    +

    Effectuer des requ�tes + asynchrones � la base de donn�es

    +
    + Il peut �tre parfois int�ressant d'ex�cuter certaines requ�tes � la base de donn�es de mani�re + asynchrone (multi-thread), par exemple pour �viter de bloquer une IHM si une requ�te est trop + longue � s'ex�cuter.
    + Pour simplifier les requ�tes asynchrones, la biblioth�que QxOrm fournit la classe qx::QxDaoAsync.
    + Cette classe ex�cute une requ�te dans un thread d�di� et renvoie un SIGNAL + queryFinished() lorsque la requ�te est termin�e.
    + Pour utiliser la classe qx::QxDaoAsync, il suffit de : +
      +
    • v�rifier que la requ�te fait appel � une classe qui impl�mente l'interface qx::IxPersistable ;
    • +
    • cr�er une instance de type qx::QxDaoAsync (par exemple, une propri�t� membre d'une + classe d�rivant du type QWidget) ;
    • +
    • connecter un SLOT au SIGNAL qx::QxDaoAsync::queryFinished() (par + exemple, un SLOT d�fini dans une classe d�rivant du type QWidget) ;
    • +
    • ex�cuter une requ�te � la base de donn�es en utilisant l'une des m�thodes commen�ant par + async : qx::QxDaoAsync::asyncXXXX(). +
    • +
    + Voici un exemple d'utilisation avec une classe nomm�e MyWidget :
    +
    + + + + + + + +
    +
    class MyWidget : public QWidget
    +{ Q_OBJECT
    +
    +   //...
    +   qx::QxDaoAsync m_daoAsync;
    +   //...
    +Q_SLOTS:
    +   void onQueryFinished(const QSqlError & daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams);
    +   //...
    +
    +};
    +
    +
    + Et voici l'impl�mentation de la classe MyWidget :
    +
    + + + + + + + +
    +
    MyWidget::MyWidget() : QObject()
    +{
    +   //...
    +   QObject::connect((& m_daoAsync), SIGNAL(queryFinished(const QSqlError &, qx::dao::detail::QxDaoAsyncParams_ptr)), 
    +                    this, SLOT(onQueryFinished(const QSqlError &, qx::dao::detail::QxDaoAsyncParams_ptr)));
    +   //...
    +}
    +
    +void MyWidget::onQueryFinished(const QSqlError & daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams)
    +{
    +   if (! pDaoParams) { return; }
    +   qx::QxSqlQuery query = pDaoParams->query;
    +   if (! daoError.isValid()) { ; }
    +   // If the async query is associated to a simple object, just use 'pDaoParams->pInstance' method
    +   qx::IxPersistable_ptr ptr = pDaoParams->pInstance;
    +   // If the async query is associated to a list of objects, just use 'pDaoParams->pListOfInstances' method
    +   qx::IxPersistableCollection_ptr lst = pDaoParams->pListOfInstances;
    +   //...
    +}
    +
    +

    +
    +

    Gestion du cache pour + sauvegarder des instances C++ (module QxCache)

    +
    + Le cache propos� par la biblioth�que QxOrm (module QxCache) est thread-safe et permet de stocker + facilement n'importe quel type de donn�es.
    + Les fonctions pour acc�der au cache se trouvent dans le namespace + qx::cache.
    + Le cache permet une optimisation du programme : il est possible par exemple de stocker des + �l�ments issus d'une requ�te effectu�e en base de donn�es.
    +
    + Chaque �l�ment stock� dans le cache est associ� � une cl� de type QString : cette cl� + permet de retrouver rapidement un �l�ment du cache.
    + Si un nouvel �l�ment est stock� dans le cache avec une cl� qui existe d�j�, alors l'ancien + �l�ment associ� � cette cl� est effac� automatiquement du cache.
    +
    + Le cache de la biblioth�que QxOrm ne g�re pas la dur�e de vie des objets : il n'y a aucun + delete effectu� par le cache.
    + C'est pourquoi il est fortement recommand� (mais ce n'est pas une obligation) de privil�gier le + stockage de pointeurs intelligents : par exemple, boost::shared_ptr<T> pour la biblioth�que boost ou + bien QSharedPointer<T> pour la biblioth�que Qt.
    +
    + Le cache peut avoir un co�t relatif maximum pour �viter une utilisation de la m�moire trop + importante : chaque �l�ment ins�r� dans le cache peut indiquer un co�t repr�sentant une + estimation de sa taille m�moire (par exemple, le nombre d'�l�ments d'une collection).
    + Lorsque le co�t maximum du cache est atteint, les premiers �l�ments ins�r�s dans le cache sont + supprim�s (en respectant l'ordre d'insertion dans le cache) jusqu'� ce que la limite du cache ne + soit plus d�pass�e.
    +
    + Il est possible d'associer � chaque �l�ment du cache une date-heure d'insertion.
    + Si aucune date-heure n'est renseign�e, alors la date-heure courante est prise en compte.
    + Ce m�canisme permet de v�rifier si un �l�ment stock� dans le cache n�cessite une mise � jour ou + non.
    +
    + Voici un exemple d'utilisation du cache de la biblioth�que QxOrm (fonctions du namespace + qx::cache) :
    +
    + + + + + + + +
    +
    // D�fini le co�t maximum du cache � 500
    +qx::cache::max_cost(500);
    +
    +// R�cup�re une liste de 'author' de la base de donn�es
    +boost::shared_ptr< QList<author> > list_author;
    +QSqlError daoError = qx::dao::fetch_all(list_author);
    +
    +// Ins�re la liste de 'author' dans le cache
    +qx::cache::set("list_author", list_author);
    +
    +// R�cup�re une liste de 'blog' de la base de donn�es
    +QSharedPointer< std::vector<blog> > list_blog;
    +daoError = qx::dao::fetch_all(list_blog);
    +
    +// Ins�re la liste de 'blog' dans le cache (co�t = nombre de 'blog')
    +qx::cache::set("list_blog", list_blog, list_blog.count());
    +
    +// Pointeur vers un objet de type 'comment'
    +comment_ptr my_comment;
    +my_comment.reset(new comment(50));
    +daoError = qx::dao::fetch_by_id(my_comment);
    +
    +// Ins�re le 'comment' dans le cache en pr�cisant une date-heure d'insertion
    +qx::cache::set("comment", my_comment, 1, my_comment->dateModif());
    +
    +// R�cup�re la liste de 'blog' stock�e dans le cache
    +list_blog = qx::cache::get< QSharedPointer< std::vector<blog> > >("list_blog");
    +
    +// R�cup�re la liste de 'blog' sans pr�ciser le type
    +qx_bool bGetOk = qx::cache::get("list_blog", list_blog);
    +
    +// Supprime du cache la liste de 'author'
    +bool bRemoveOk = qx::cache::remove("list_author");
    +
    +// Compte le nombre d'�l�ments du cache
    +long lCount = qx::cache::count();
    +
    +// R�cup�re le co�t actuel des �l�ments stock�s dans le cache
    +long lCurrentCost = qx::cache::current_cost();
    +
    +// V�rifie qu'un �l�ment associ� � la cl� "comment" existe dans le cache
    +bool bExist = qx::cache::exist("comment");
    +
    +// R�cup�re le 'comment' stock� dans le cache avec sa date-heure d'insertion
    +QDateTime dt;
    +bGetOk = qx::cache::get("comment", my_comment, dt);
    +
    +// Vide le cache
    +qx::cache::clear();
    +
    +

    +
    +

    Travailler avec plusieurs + bases de donn�es

    +
    + Dans le chapitre Connexion � la base de donn�es, nous avons vu comment + param�trer la connexion par d�faut avec la classe singleton : qx::QxSqlDatabase. + La biblioth�que QxOrm �tant bas�e sur le moteur QtSql de Qt, elle + utilise en interne la classe QSqlDatabase de Qt. + Toutes les fonctions d'acc�s � la base de donn�es (namespace qx::dao, classe + qx::QxSession, etc...) + ont un param�tre optionnel : QSqlDatabase * pDatabase = NULL : +
      +
    • si la valeur de ce param�tre est � NULL (valeur par d�faut) : alors la biblioth�que QxOrm + utilise la classe singleton qx::QxSqlDatabase pour se connecter � la base de donn�es (avec + gestion automatique du multi-threading) ;
    • +
    • si la valeur est non nulle : alors la biblioth�que QxOrm utilise la connexion fournie par + le pointeur QSqlDatabase * pDatabase.
    • +
    + Ce param�tre permet donc de g�rer son propre pool de connexions � une ou plusieurs bases de + donn�es. +

    +
    +

    D�clarer une classe + abstraite dans le contexte QxOrm

    +
    + Une classe abstraite C++ (contenant au moins une m�thode virtuelle pure) ne peut pas �tre mapp�e + avec une table d'une base de donn�es (puisqu'elle ne peut pas �tre instanci�e).
    + Cependant, il peut �tre int�ressant de d�finir une classe abstraite contenant une liste de + propri�t�s utilis�es par plusieurs objets persistants.
    + Un exemple de classe abstraite se trouve dans le dossier ./test/qxDllSample/dll2/ de la + distribution de QxOrm avec la classe BaseClassTrigger.
    + QxOrm propose le m�canisme suivant pour d�finir une classe abstraite dans le contexte QxOrm : +
      +
    • d�clarer la classe avec la m�thode 'void register_class' comme n'importe qu'elle + autre classe ;
    • +
    • utiliser la macro QX_REGISTER_ABSTRACT_CLASS(className) juste apr�s la d�finition + de la classe.
    • +
    +
    +
    +

    D�clarer automatiquement + les m�ta-propri�t�s de Qt (macro Q_PROPERTY)

    +
    + Toute classe h�ritant du type QObject peut d�clarer ses propri�t�s avec la macro Q_PROPERTY : + les propri�t�s deviennent alors des m�ta-propri�t�s. + Ce m�canisme permet au framework Qt de proposer un moteur d'introspection gr�ce au + pr�-compilateur moc. + Les m�ta-propri�t�s peuvent alors �tre utilis�es par exemple par le moteur QML, + QtScript, etc.
    +
    + La biblioth�que QxOrm n�cessite une d�claration de chacune des propri�t�s d'une classe + dans la fonction de mapping void qx::register_class<T>() afin de proposer + l'ensemble de ses fonctionnalit�s (persistance des donn�es, s�rialisation XML, JSON et binaire, + etc.). + Il est possible de d�clarer automatiquement dans le contexte QxOrm l'ensemble des + m�ta-propri�t�s sans maintenir une fonction de mapping void qx::register_class<T>() + : la macro QX_REGISTER_ALL_QT_PROPERTIES() utilise le moteur d'introspection de Qt pour + parcourir la liste des m�ta-propri�t�s.
    +
    + Voici un exemple d'utilisation avec la classe TestQtProperty se trouvant dans le dossier + ./test/qxDllSample/dll1/include/ de la distribution QxOrm :
    +
    + + + + + + + +
    +
    #ifndef _QX_TEST_QT_META_PROPERTY_H_
    +#define _QX_TEST_QT_META_PROPERTY_H_
    + 
    +class QX_DLL1_EXPORT TestQtProperty : public QObject
    +{
    +
    +   Q_OBJECT
    +   Q_PROPERTY(int id READ id WRITE setId)
    +   Q_PROPERTY(long number READ number WRITE setNumber)
    +   Q_PROPERTY(QString desc READ desc WRITE setDesc)
    +   Q_PROPERTY(QDateTime birthDate READ birthDate WRITE setBirthDate)
    +   Q_PROPERTY(QVariant photo READ photo WRITE setPhoto)
    +
    +protected:
    +
    +   int         m_id;
    +   long        m_number;
    +   QString     m_desc;
    +   QDateTime   m_birthDate;
    +   QVariant    m_photo;
    +
    +public:
    +
    +   TestQtProperty() : QObject(), m_id(0), m_number(0) { ; }
    +   virtual ~TestQtProperty() { ; }
    +
    +   int id() const                { return m_id; }
    +   long number() const           { return m_number; }
    +   QString desc() const          { return m_desc; }
    +   QDateTime birthDate() const   { return m_birthDate; }
    +   QVariant photo() const        { return m_photo; }
    +
    +   void setId(int i)                         { m_id = i; }
    +   void setNumber(long l)                    { m_number = l; }
    +   void setDesc(const QString & s)           { m_desc = s; }
    +   void setBirthDate(const QDateTime & dt)   { m_birthDate = dt; }
    +   void setPhoto(const QVariant & v)         { m_photo = v; }
    + 
    +};
    +
    +QX_REGISTER_HPP_QX_DLL1(TestQtProperty, QObject, 0)
    +
    +#endif // _QX_TEST_QT_META_PROPERTY_H_
    +
    +
    + + + + + + + +
    +
    #include "../include/precompiled.h"
    +
    +#include "../include/TestQtProperty.h"
    +
    +#include <QxOrm_Impl.h>
    + 
    +QX_REGISTER_CPP_QX_DLL1(TestQtProperty)
    +QX_REGISTER_ALL_QT_PROPERTIES(TestQtProperty, "id")
    +
    +
    + Pour ceux qui ne souhaitent pas utiliser la macro QX_REGISTER_ALL_QT_PROPERTIES, il est + possible d'�crire � la place les quatre lignes de code suivantes :
    +
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<TestQtProperty> & t)
    +{ qx::register_all_qt_properties<TestQtProperty>(t, "id"); }
    +} // namespace qx
    +
    +
    + Remarque : le deuxi�me param�tre de la macro QX_REGISTER_ALL_QT_PROPERTIES permet + d'indiquer la propri�t� qui servira de cl� primaire dans la base de donn�es. + Si ce param�tre est vide, cela signifie que la classe ne poss�de pas de cl� primaire ou bien que + celle-ci est d�finie dans une classe de base.
    +
    + Toute propri�t� d�finie avec la macro Q_PROPERTY peut s'enregistrer dans le contexte + QxOrm de deux mani�res diff�rentes :
    + 1- par la m�thode classique : t.data(& MyQObject::my_property, "my_property", + 0);
    + 2- ou bien sans mentionner le pointeur vers la donn�e membre de la classe : + t.data("my_property", 0);
    +
    + Peu importe la m�thode d'enregistrement des propri�t�s dans le contexte QxOrm, elles seront + accessibles par la m�me interface qx::IxDataMember et proposent donc les m�mes fonctionnalit�s. + Il est possible d'utiliser les deux m�thodes dans une m�me fonction de mapping void + qx::register_class<T>(). + Chaque m�thode d'enregistrement pr�sente des avantages et inconv�nients.
    +
    + Voici la liste des avantages de la deuxi�me m�thode d'enregistrement des propri�t�s dans le + contexte QxOrm : +
      +
    • temps de compilation du projet beaucoup plus rapide ;
    • +
    • taille de l'ex�cutable g�n�r� plus petite ;
    • +
    • forte int�gration avec le moteur d'introspection du framework Qt ;
    • +
    • pas besoin de maintenir la fonction de mapping en utilisant la macro + QX_REGISTER_ALL_QT_PROPERTIES. +
    • +
    + Voici les inconv�nients par rapport � la m�thode classique d'enregistrement des propri�t�s : +
      +
    • n�cessite un h�ritage de la classe QObject pour pouvoir utiliser la macro + Q_PROPERTY ; +
    • +
    • ex�cution du programme plus lente (utilisation du type QVariant � la place des + template C++) ; +
    • +
    • ne supporte pas la notion de relation entre tables de la base de donn�es + (one-to-one, one-to-many, many-to-one et many-to-many) ;
    • +
    • pas d'acc�s au pointeur sur la donn�e membre de la classe (conversion n�cessaire au type + QVariant pour acc�der et modifier une valeur). +
    • +
    +
    +
    + +

    S�rialisation

    +
    + La s�rialisation est un m�canisme permettant de sauvegarder l'�tat d'une instance d'objet + dans un flux (fichier, r�seau, etc...) sous un certain format (binaire, XML, JSON, texte, etc...). + La d�s�rialisation est le processus inverse permettant de restaurer l'�tat d'un objet � + partir d'un flux. + Pour plus d'informations sur la notion de s�rialisation : rendez-vous sur la page + Wikipedia. +

    + Toute classe C++ enregistr�e dans le contexte QxOrm peut �tre s�rialis�e dans diff�rents formats : + + Remarque : le moteur de s�rialisation de la biblioth�que QxOrm permet de proposer des + fonctionnalit�s suppl�mentaires comme le clonage d'entit�s, le dump d'entit�s (format XML ou JSON) ou encore le + module QxService. +

    + Autre remarque : par d�faut, toutes les propri�t�s enregistr�es dans le contexte QxOrm sont + s�rialisables. Pour supprimer une propri�t� du moteur de s�rialisation, il est possible d'�crire + :
    +
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<person> & t)
    +{
    +  IxDataMember * pDataMember = t.data(& person::age, "age");
    +  pDataMember->setSerialize(false);
    +}}
    +
    +

    +

    N� version pour assurer + une compatibilit� ascendante

    +
    + La compatibilit� ascendante permet � une application de pouvoir d�s�rialiser un flux provenant + d'une version ant�rieure. + La biblioth�que QxOrm impose un num�ro de version par classe ainsi qu'un num�ro de version pour + chaque propri�t� enregistr�e dans le contexte QxOrm afin de pouvoir assurer une compatibilit� + ascendante automatiquement. +

    + Par exemple, imaginons une classe person cr��e dans une application en version A : + nous renseignons dans la macro QX_REGISTER_HPP une n� de version � 0 (correspond � la + 1�re version de notre classe person), ainsi qu'un n� de version � 0 pour chacune des + propri�t�s de la classe (si param�tre non renseign�, 0 est la valeur par d�faut). + Ce qui donne le r�sultat suivant : +

    + * Fichier person.h :
    + + + + + + + +
    +
    #ifndef _PERSON_H_
    +#define _PERSON_H_
    +
    +class person
    +{
    +public:
    +   long id;
    +   QString firstName;
    +   QString lastName;
    +   QDateTime birthDate;
    +
    +   person() : id(0) { ; }
    +   virtual ~person() { ; }
    +};
    +
    +QX_REGISTER_HPP_MY_TEST_EXE(person, qx::trait::no_base_class_defined, 0)
    +
    +#endif // _PERSON_H_
    +
    +
    + * Fichier person.cpp :
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<person> & t)
    +{
    +  t.id(& person::id, "id");
    +  t.data(& person::firstName, "first_name", 0);
    +  t.data(& person::lastName, "last_name", 0);
    +  t.data(& person::birthDate, "birth_date", 0);
    +}}
    +
    +

    + Dans la version B de notre application, nous modifions la classe person pour + ajouter 2 nouvelles propri�t�s : sex et address. Notre classe ayant �volu�e, il + faut donc incr�menter son n� de version, et les nouvelles propri�t�s doivent avoir un n� de + version � 1, ce qui donne : +

    + * Fichier person.h :
    + + + + + + + +
    +
    #ifndef _PERSON_H_
    +#define _PERSON_H_
    +
    +class person
    +{
    +public:
    +   long id;
    +   QString firstName;
    +   QString lastName;
    +   QDateTime birthDate;
    +   QString sex;
    +   QString address;
    +
    +   person() : id(0) { ; }
    +   virtual ~person() { ; }
    +};
    +
    +QX_REGISTER_HPP_MY_TEST_EXE(person, qx::trait::no_base_class_defined, 1)
    +
    +#endif // _PERSON_H_
    +
    +
    + * Fichier person.cpp :
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<person> & t)
    +{
    +  t.id(& person::id, "id");
    +  t.data(& person::firstName, "first_name", 0);
    +  t.data(& person::lastName, "last_name", 0);
    +  t.data(& person::birthDate, "birth_date", 0);
    +  t.data(& person::sex, "sex", 1);
    +  t.data(& person::address, "address", 1);
    +}}
    +
    +

    + Remarque : en proc�dant ainsi, la biblioth�que QxOrm peut s�rialiser une instance de la + classe person dans une application en version A, puis d�s�rialiser � partir de ce flux + issu de la version A afin de recr�er une instance de la classe person dans une version B + de l'application. +

    + Autre remarque : la suppression d'une propri�t� casse la compatibilit� ascendante. + Il est donc recommand� de ne jamais supprimer de propri�t� pour utiliser le moteur de + s�rialisation : il est possible par exemple de mettre une visibilit� � private et de + supprimer les accesseurs get/set, la propri�t� devenant ainsi inaccessible � l'ext�rieur + de la classe, elle peut alors �tre consid�r�e comme �tant obsol�te. +

    +
    +

    Moteur QDataStream de + Qt

    +
    + Toute classe C++ enregistr�e dans le contexte QxOrm peut �tre s�rialis�e en utilisant le moteur QDataStream de + Qt. + Les fonctions pour utiliser ce type de s�rialisation sont disponibles dans l'espace de nom : namespace + qx::serialization::qt. + + Remarque : la s�rialisation QDataStream est portable (s�rialisation/d�s�rialisation compatible sur + tous types d'environnement : Windows, Linux, Mac OS X, etc...). + Le flux s�rialis� est au format binaire : la taille du flux est donc r�duite (compar� � un flux + XML par exemple). + La s�rialisation QDataStream �tant bas�e sur le moteur d'introspection de la biblioth�que + QxOrm, elle est moins performante que les s�rialisations bas�es sur le moteur boost::serialization. +

    + Par exemple :
    + + + + + + + +
    +
       // Fetch a drug with id '3' in a new variable
    +   // drug is a C++ class registered in QxOrm context
    +   drug d;
    +   d.id = 3;
    +   QSqlError daoError = qx::dao::fetch_by_id(d);
    +
    +   // Serialize the drug to a file
    +   qx::serialization::qt::to_file(d, "export_drug.txt");
    +
    +   // Import drug from file in a new instance
    +   drug d2;
    +   qx::serialization::qt::from_file(d2, "export_drug.txt");
    +
    +   // Check if d == d2
    +   qAssert(d == d2);
    +
    +
    + Remarque : dans l'exemple ci-dessus, nous s�rialisons une instance C++. + Toutes les fonctions du namespace qx::serialization peuvent �galement s�rialiser des listes d'instances + C++. + Pour connaitre la liste des collections support�es, rendez-vous dans le chapitre : Collections support�es par QxOrm. +

    +
    +

    Moteur JSON de Qt

    +
    + Toute classe C++ enregistr�e dans le contexte QxOrm peut �tre s�rialis�e en utilisant le moteur QJson de Qt (n�cessite + Qt5). + Les fonctions pour utiliser ce type de s�rialisation sont disponibles dans l'espace de nom : namespace + qx::serialization::json. + + Remarque : le moteur de s�rialisation JSON est le plus permissif (compar� au moteur + XML par exemple) : en effet, les propri�t�s d'une instance peuvent �tre d�finies dans + n'importe quel ordre, les propri�t�s peuvent �tre supprim�es ou ajout�es. + La d�s�rialisation JSON ne retourne jamais d'erreur : elle ignore tout si le format des donn�es + est incorrect (le flux JSON doit par contre �tre valide) ou bien si des propri�t�s sont absentes + : le moteur JSON est donc beaucoup plus flexible que le moteur XML. +

    + Autre remarque : la s�rialisation JSON est bas�e sur le moteur d'introspection de la biblioth�que QxOrm, + elle est moins performante que les s�rialisations bas�es sur le moteur boost::serialization. +

    + Par exemple :
    + + + + + + + +
    +
       // Fetch a list of authors from database and serialize them to a JSON file
    +   list_author list_of_author;
    +   qx::dao::fetch_all(list_of_author);
    +   qx::serialization::json::to_file(list_of_author, "list_of_author.json");
    +
    +
    + L'exemple ci-dessus g�n�re le flux JSON suivant :
    +
    +
    {
    +    "author_id_2": {
    +        "author_id": "author_id_2",
    +        "birthdate": "2016-03-24",
    +        "list_blog": [
    +        ],
    +        "name": "author_2",
    +        "sex": 1
    +    },
    +    "author_id_3": {
    +        "author_id": "author_id_3",
    +        "birthdate": "2016-03-24",
    +        "list_blog": [
    +        ],
    +        "name": "author_3",
    +        "sex": 1
    +    }
    +}
    +
    +

    + Remarque : le module QxRestApi de la biblioth�que QxOrm est bas� + sur le moteur de s�rialisation JSON. +

    + Autre remarque : il est possible de personnaliser le format de sortie JSON (filtrer les + propri�t�s du flux JSON g�n�r� par la s�rialisation). + Les fonctions de s�rialisation JSON dispose d'un param�tre optionnel de type QString + nomm� format. + Les pr�-requis pour utiliser le param�tre format sont : +
      +
    • le param�tre format doit �tre pr�fix� par : filter: ;
    • +
    • les propri�t�s � exporter sont d�finies entre { } ;
    • +
    • les relations sont s�par�es par le caract�re | ;
    • +
    • il est possible d'utiliser le caract�re * pour d�finir : toutes les + relations sur 1 niveau ;
    • +
    • le caract�re - devant les { } signifie : toutes les + propri�t�s sauf.
    • +
    +
    + Exemple : voici un exemple de s�rialisation JSON en d�finissant un format de sortie pour + filtrer certaines propri�t�s : +

    + + + + + + + +
    +
    // Serialize a C++ instance to a JSON string
    +QString jsonFormat = "filter: { blog_text } | author_id { name, birthdate } | list_comment { comment_text } -> blog_id -> *";
    +QString outputJsonFiltered = qx::serialization::json::to_string(blog, 1, jsonFormat);
    +qDebug("[QxOrm] custom JSON serialization process (filtered) : \n%s", qPrintable(outputJsonFiltered));
    +
    +// Fill a C++ instance based on a JSON string
    +blog_ptr blogFromJsonFiltered; blogFromJsonFiltered.reset(new blog());
    +qx::serialization::json::from_string(blogFromJsonFiltered, outputJsonFiltered, 1, jsonFormat);
    +qx::dump(blogFromJsonFiltered);
    +qAssert(blogFromJsonFiltered->m_text != ""); // Fetched
    +qAssert(blogFromJsonFiltered->m_dt_creation.isNull()); // Not fetched
    +qAssert(blogFromJsonFiltered->m_author->m_sex == author::unknown); // Not fetched
    +qAssert(blogFromJsonFiltered->m_author->m_name != ""); // Fetched
    +qAssert(blogFromJsonFiltered->m_commentX.size() > 0);
    +qAssert(blogFromJsonFiltered->m_commentX[0]->m_dt_create.isNull()); // Not fetched
    +qAssert(blogFromJsonFiltered->m_commentX[0]->m_text != ""); // Fetched
    +qAssert(blogFromJsonFiltered->m_commentX[0]->m_blog);
    +
    +

    +
    +

    Moteur XML de + boost::serialization

    +
    + Le moteur XML de boost::serialization n'est pas activ� par d�faut : pour activer cette + fonctionnalit�, il est n�cessaire de d�finir les options de compilation + _QX_ENABLE_BOOST_SERIALIZATION et _QX_ENABLE_BOOST_SERIALIZATION_XML dans le fichier de configuration QxOrm.pri (ou QxOrm.cmake). + Il est �galement n�cessaire de compiler le binaire boost::serialization (ce module de boost n'�tant pas header only), + et de renseigner le chemin d'acc�s � ce binaire dans les variables QX_BOOST_LIB_PATH, + QX_BOOST_LIB_SERIALIZATION_DEBUG et QX_BOOST_LIB_SERIALIZATION_RELEASE du fichier de configuration QxOrm.pri (ou QxOrm.cmake). +

    + Toute classe C++ enregistr�e dans le contexte QxOrm peut �tre s�rialis�e en utilisant le moteur XML de boost::serialization. + Les fonctions pour utiliser ce type de s�rialisation sont disponibles dans l'espace de nom : namespace + qx::serialization::xml (m�mes fonctions que dans l'espace de nom + qx::serialization::qt). +

    + Ce type de s�rialisation poss�de les caract�ristiques suivantes : +
      +
    • portable : compatible sur tous types d'environnement : Windows, Linux, Mac OS X, + etc... ;
    • +
    • slowest : plus lente que les s�rialisations binary et text ;
    • +
    • largest : taille des flux g�n�r�s plus importante que les s�rialisations + binary et text ; +
    • +
    • human-readable : un flux XML peut facilement �tre analys� et lu par un �diteur + externe ou un �tre humain.
    • +
    +
    +
    +

    Moteur binaire de + boost::serialization

    +
    + Le moteur binaire de boost::serialization n'est pas activ� par d�faut : pour activer cette + fonctionnalit�, il est n�cessaire de d�finir les options de compilation + _QX_ENABLE_BOOST_SERIALIZATION et _QX_ENABLE_BOOST_SERIALIZATION_BINARY dans le fichier de configuration QxOrm.pri (ou QxOrm.cmake). + Il est �galement n�cessaire de compiler le binaire boost::serialization (ce module de boost n'�tant pas header only), + et de renseigner le chemin d'acc�s � ce binaire dans les variables QX_BOOST_LIB_PATH, + QX_BOOST_LIB_SERIALIZATION_DEBUG et QX_BOOST_LIB_SERIALIZATION_RELEASE du fichier de configuration QxOrm.pri (ou QxOrm.cmake). +

    + Toute classe C++ enregistr�e dans le contexte QxOrm peut �tre s�rialis�e en utilisant le moteur binaire de boost::serialization. + Les fonctions pour utiliser ce type de s�rialisation sont disponibles dans l'espace de nom : namespace + qx::serialization::binary (m�mes fonctions que dans l'espace de nom + qx::serialization::qt). +

    + Ce type de s�rialisation poss�de les caract�ristiques suivantes : +
      +
    • non-portable : un flux s�rialis� sur un environnement Windows peut �tre + incompatible si d�s�rialisation sur un environnement Linux par exemple : il est donc + fortement recommand� de rester sur le m�me environnement ;
    • +
    • fastest : plus rapide que les s�rialisations XML et text ;
    • +
    • smallest : taille des flux g�n�r�s r�duite compar� aux s�rialisations XML + et text ;
    • +
    • non-human-readable : un flux binaire n'est pas lisible (pas de log possible par + exemple).
    • +
    +
    +
    +

    Autres types de + s�rialisation propos�s par boost

    +
    + Le moteur boost::serialization propose d'autres types de s�rialisation. + Ces diff�rents types ne sont pas activ�s par d�faut, pour utiliser ces fonctionnalit�s (m�mes + fonctions que dans l'espace de nom qx::serialization::qt), il est + n�cessaire de d�finir les options de compilation suivantes dans le fichier + de configuration QxOrm.pri (ou QxOrm.cmake) : + +
    +
    +

    Cloner une instance + C++

    +
    + Toute classe C++ enregistr�e dans le contexte QxOrm peut �tre clon�e en utilisant une des + fonctions suivantes : + + Par exemple : +

    + + + + + + + +
    +
       drug_ptr d1;
    +   d1.reset(new drug());
    +   d1->name = "name1";
    +   d1->description = "desc1";
    +
    +   // Clone a drug
    +   drug_ptr d_clone = qx::clone(* d1);
    +
    +   // Check if (d1 == d_clone)
    +   qAssert((* d1) == (* d_clone));
    +
    +
    + Remarque importante : il faut faire attention lorsqu'on clone un pointeur intelligent + (boost::shared_ptr ou QSharedPointer par exemple) dont l'�l�ment parent + (root) peut �tre r�f�renc� plusieurs fois dans sa hi�rarchie (cas d'une structure en + arbre par exemple). + Dans ce cas, afin de prot�ger le pointeur parent d'une double suppression (2 pointeurs + intelligents qui pointent sur le m�me pointeur brut), il est conseill� de cloner de cette fa�on + : +

    + + + + + + + +
    +
    // 'pOther' type is boost::shared_ptr<myClass> (smart-pointer)
    +boost::shared_ptr<myClass> * pCloneTemp = qx::clone_to_nude_ptr(pOther);
    +boost::shared_ptr<myClass> pClone = (pCloneTemp ? (* pCloneTemp) : boost::shared_ptr<myClass>());
    +if (pCloneTemp) { delete pCloneTemp; pCloneTemp = NULL; }
    +// Now use 'pClone' ...
    +
    +

    +
    +

    Afficher le d�tail d'une + instance C++ (dump au format XML ou JSON)

    +
    + Toute instance C++ enregistr�e dans le contexte QxOrm peut �tre affich�e au format JSON. + Si le moteur XML de boost::serialization est activ�, alors il est + �galement possible d'afficher un dump sous format XML (param�tre d'entr�e de la fonction + qx::dump). + Cette fonctionnalit� peut �tre utile pour faire du d�bogage par exemple, ou bien pour g�n�rer + des logs. +

    + + + + + + + +
    +
       blog_ptr b;
    +   b.reset(new blog());
    +   b->id = 36;
    +   qx::dao::fetch_by_id_with_all_relation(b);
    +
    +   // Dump 'b' instance result from database (XML or JSON serialization)
    +   // Second parameter is optional : 'true' = JSON format, 'false' = XML format
    +   qx::dump(b, false);
    +
    +
    + Ce qui g�n�re le flux XML suivant : +

    +
    +
    +[QxOrm] start dump 'boost::shared_ptr<blog>'
    +<boost.shared_ptr-blog- class_id="0" tracking_level="0" version="1">
    +	<px class_id="1" tracking_level="1" version="0" object_id="_0">
    +		<blog_id>113</blog_id>
    +		<blog_text class_id="2" tracking_level="0" version="0">update blog_text_1</blog_text>
    +		<date_creation class_id="3" tracking_level="0" version="0">20100409162612000</date_creation>
    +		<author_id class_id="4" tracking_level="0" version="1">
    +			<px class_id="5" tracking_level="1" version="0" object_id="_1">
    +				<author_id>author_id_2</author_id>
    +				<name>author_2</name>
    +				<birthdate class_id="6" tracking_level="0" version="0">20100409</birthdate>
    +				<sex>1</sex>
    +				<list_blog class_id="7" tracking_level="0" version="0">
    +					<count>0</count>
    +					<item_version>1</item_version>
    +				</list_blog>
    +			</px>
    +		</author_id>
    +		<list_comment class_id="8" tracking_level="0" version="0">
    +			<count>2</count>
    +			<item class_id="9" tracking_level="0" version="1">
    +				<px class_id="10" tracking_level="1" version="0" object_id="_2">
    +					<comment_id>209</comment_id>
    +					<comment_text>comment_1 text</comment_text>
    +					<date_creation>20100409162612000</date_creation>
    +					<blog_id>
    +						<px class_id_reference="1" object_id="_3">
    +							<blog_id>113</blog_id>
    +							<blog_text></blog_text>
    +							<date_creation></date_creation>
    +							<author_id>
    +								<px class_id="-1"></px>
    +							</author_id>
    +							<list_comment>
    +								<count>0</count>
    +							</list_comment>
    +							<list_category class_id="11" tracking_level="0" version="0">
    +								<count>0</count>
    +							</list_category>
    +						</px>
    +					</blog_id>
    +				</px>
    +			</item>
    +			<item>
    +				<px class_id_reference="10" object_id="_4">
    +					<comment_id>210</comment_id>
    +					<comment_text>comment_2 text</comment_text>
    +					<date_creation>20100409162612000</date_creation>
    +					<blog_id>
    +						<px class_id_reference="1" object_id="_5">
    +							<blog_id>113</blog_id>
    +							<blog_text></blog_text>
    +							<date_creation></date_creation>
    +							<author_id>
    +								<px class_id="-1"></px>
    +							</author_id>
    +							<list_comment>
    +								<count>0</count>
    +							</list_comment>
    +							<list_category>
    +								<count>0</count>
    +							</list_category>
    +						</px>
    +					</blog_id>
    +				</px>
    +			</item>
    +		</list_comment>
    +		<list_category>
    +			<count>2</count>
    +			<item class_id="12" tracking_level="0" version="0">
    +				<first>355</first>
    +				<second class_id="13" tracking_level="0" version="0">
    +					<qt_shared_ptr class_id="14" tracking_level="1" version="0" object_id="_6">
    +						<category_id>355</category_id>
    +						<name>category_1</name>
    +						<description>desc_1</description>
    +						<list_blog class_id="15" tracking_level="0" version="0">
    +							<count>0</count>
    +						</list_blog>
    +					</qt_shared_ptr>
    +				</second>
    +			</item>
    +			<item>
    +				<first>357</first>
    +				<second>
    +					<qt_shared_ptr class_id_reference="14" object_id="_7">
    +						<category_id>357</category_id>
    +						<name>category_3</name>
    +						<description>desc_3</description>
    +						<list_blog>
    +							<count>0</count>
    +						</list_blog>
    +					</qt_shared_ptr>
    +				</second>
    +			</item>
    +		</list_category>
    +	</px>
    +</boost.shared_ptr-blog->
    +[QxOrm] end dump 'boost::shared_ptr<blog>'
    +
    +
    +
    +
    +
    + +

    Introspection - R�flexion +

    +
    + Toute classe enregistr�e dans le contexte QxOrm par la m�thode qx::register_class<T>() + peut �tre utilis�e par le moteur d'introspection (ou r�flexion) de la biblioth�que QxOrm. + Le moteur d'introspection permet d'obtenir de fa�on dynamique (donc pendant l'ex�cution du + programme) des informations propres � un type. + Ces informations correspondent � des m�ta-donn�es et d�crivent de fa�on exhaustive les + caract�ristiques d'une classe (propri�t�s, m�thodes, etc.). + De nombreux langages de programmation (par exemple Java ou C#) int�grent nativement ce m�canisme, + ce n'est pas le cas du C++, c'est pourquoi la biblioth�que QxOrm �mule un moteur d'introspection. + Pour plus de d�tails sur l'introspection (ou r�flexion), rendez-vous sur la page Wikipedia. +

    + Voici la liste des classes disponibles pour acc�der aux m�ta-donn�es : +
      +
    • qx::QxClassX : + singleton permettant de parcourir l'ensemble des classes enregistr�es dans le contexte QxOrm + par la m�thode qx::register_class<T>() ;
    • +
    • qx::IxClass : + interface pour une classe enregistr�e dans le contexte QxOrm ;
    • +
    • qx::IxDataMemberX : liste des propri�t�s associ�es � une classe ;
    • +
    • qx::IxDataMember : interface pour une propri�t� d'une classe ;
    • +
    • qx::IxFunctionX : + liste des m�thodes associ�es � une classe ;
    • +
    • qx::IxFunction : + interface pour une m�thode d'une classe (static ou non static).
    • +
    + Une instance de type qx::IxClass poss�de la liste des propri�t�s d'une classe (qx::IxDataMemberX) ainsi que la liste des m�thodes d'une classe (qx::IxFunctionX).
    +
    + Le moteur d'introspection de la biblioth�que QxOrm permet par exemple de : + +
    + Remarque : le module QxService de la biblioth�que QxOrm (cliquez ici pour acc�der au tutoriel) permettant de cr�er un serveur + d'applications C++ est bas� sur le moteur d'introspection pour appeler dynamiquement les m�thodes + de type service (demande du client) sur le serveur, ainsi que pour cr�er dynamiquement les + instances des classes de param�tre (entr�e/sortie).
    +
    + Autre remarque : il est possible d'ajouter de nouvelles informations au moteur + d'introspection en utilisant la notion de property bag. + En effet, les classes qx::IxClass, qx::IxDataMember et qx::IxFunction + poss�dent chacune une liste d'�l�ments de type QVariant accessibles par cl� de type + QString (voir la + classe qx::QxPropertyBag pour plus de d�tails sur cette notion). +

    + Autre remarque : afin d'initialiser le moteur d'introspection QxOrm, il est recommand� + d'appeler la fonction suivante en d�but de programme (main par exemple) : +

    + + + + + + + +
    +
    // Following command is recommanded to initialize QxOrm introspection engine
    +qx::QxClassX::registerAllClasses(true);
    +
    +

    +

    Obtenir dynamiquement la + valeur d'une donn�e membre

    +
    + Pour obtenir dynamiquement la valeur d'une donn�e membre en utilisant le moteur d'introspection + de la biblioth�que QxOrm, il est n�cessaire de passer par la classe : qx::IxDataMember. + La classe qx::IxDataMember fournit plusieurs m�thodes pour obtenir la valeur d'une + donn�e membre (chacune prenant en param�tre un pointeur g�n�rique de type void * + correspondant � l'adresse de l'instance courante) : +
      +
    • toVariant() + : retourne la valeur de la donn�e membre convertie en QVariant ;
    • +
    • getValue<T>() : retourne la valeur avec son type r�el pr�cis� + dans le param�tre template T ;
    • +
    • getValueAnyPtr() : retourne la valeur encapsul�e par le type qx::any (possibilit� de caster ensuite en utilisant : qx::any_cast).
    • +
    + Par exemple : imaginons un pointeur g�n�rique de type void * vers une classe + person. Nous pouvons obtenir la valeur de la propri�t� firstName de type + QString de la fa�on suivante : +

    + + + + + + + +
    +
    // Generic pointer of type void * : we know that p is of type 'person'
    +void * p = ...;
    +
    +// Get a pointer to the registered data member 'firstName' of class 'person'
    +qx::IxDataMember * pDataMember = qx::QxClassX::getDataMember("person", "firstName");
    +
    +// First method to get the data member value with the real type
    +QString sFirstName = pDataMember->getValue<QString>(p);
    +
    +// Second method to get the data member value converted in QVariant
    +QVariant vFirstName = pDataMember->toVariant(p);
    +
    +// Third method to get the value encapsulated in qx::any type
    +boost::any aFirstName = pDataMember->getValueAnyPtr(p);
    +
    +// Check if all values are equals
    +qAssert((sFirstName == vFirstName.toString()) && (sFirstName == (* boost::any_cast<QString *>(aFirstName))));
    +
    +

    +
    +

    Valoriser dynamiquement + une donn�e membre

    +
    + De la m�me fa�on que pour obtenir la valeur d'une donn�e membre, la classe qx::IxDataMember + permet de valoriser une donn�e membre (modifier sa valeur). + La classe qx::IxDataMember fournit les 2 m�thodes suivantes (chacune prend en + param�tre un pointeur de type void * correspondant � l'adresse de l'instance courante, + ainsi que la nouvelle valeur � positionner) : +
      +
    • fromVariant() : valorise la donn�e membre en fonction du param�tre + de type QVariant ;
    • +
    • setValue<T>() : valorise la donn�e membre avec un param�tre + du type r�el T de la donn�e membre.
    • +
    + Par exemple : imaginons un pointeur g�n�rique de type void * vers une classe + person. Nous pouvons modifier la valeur de la propri�t� firstName de type + QString de la fa�on suivante : +

    + + + + + + + +
    +
    // Generic pointer of type void * : we know that p is of type 'person'
    +void * p = ...;
    +
    +// Get a pointer to the registered data member 'firstName' of class 'person'
    +qx::IxDataMember * pDataMember = qx::QxClassX::getDataMember("person", "firstName");
    +
    +// First method to change the data member value
    +QVariant vFirstName = QVariant("my new firstname 1");
    +pDataMember->fromVariant(p, vFirstName);
    +
    +// Other method to change the data member value (using real type)
    +QString sFirstName = "other firstname 2";
    +pDataMember->setValue<QString>(p, sFirstName);
    +
    +

    +
    +

    Appeler dynamiquement une + fonction

    +
    + Tout comme les donn�es membre (propri�t�s), il est possible d'enregistrer des m�thodes membre + (fonctions) dans le contexte QxOrm (support des m�thodes static et non static). + Le moteur d'introspection de la biblioth�que QxOrm permet d'invoquer dynamiquement des m�thodes + de classe. + Toutes les fonctions enregistr�es dans le contexte QxOrm sont associ�es � une instance de la + classe : qx::IxFunction. + Pour enregistrer des m�thodes dans le contexte QxOrm, il faut utiliser : + + Par exemple : on souhaite enregistrer dans le contexte QxOrm plusieurs m�thodes d'une + classe person : +

    + * Fichier person.h :
    + + + + + + + +
    +
    #ifndef _PERSON_H_
    +#define _PERSON_H_
    +
    +class person
    +{
    +public:
    +   long id;
    +   QString firstName;
    +   QString lastName;
    +   QDateTime birthDate;
    +
    +   person() : id(0) { ; }
    +   virtual ~person() { ; }
    +
    +   long getId() const;
    +   void myMethodWith2Params(int param1, const QString & param2);
    +
    +   static double myStaticMethodWith1Param(long param1);
    +
    +};
    +
    +QX_REGISTER_HPP_MY_TEST_EXE(person, qx::trait::no_base_class_defined, 0)
    +
    +#endif // _PERSON_H_
    +
    +
    + * Fichier person.cpp :
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<person> & t)
    +{
    +  t.id(& person::id, "id");
    +  t.data(& person::firstName, "first_name");
    +  t.data(& person::lastName, "last_name");
    +  t.data(& person::birthDate, "birth_date");
    +
    +  t.fct_0<long>(& person::getId, "getId");
    +  t.fct_2<void, int, const QString &>(& person::myMethodWith2Params, "myMethodWith2Params");
    +
    +  t.fctStatic_1<double, long>(& person::myStaticMethodWith1Param, "myStaticMethodWith1Param");
    +}}
    +
    +

    + Une fois enregistr�es dans le contexte QxOrm, il est possible d'appeler dynamiquement ces + fonctions avec les m�thodes qx::QxClassX::invoke() et qx::QxClassX::invokeStatic() : +

    + + + + + + + +
    +
       // Generic pointer of type void * : we know that p is of type 'person'
    +   void * p = ...;
    +
    +   // Call method 'long getId() const' and get return value
    +   boost::any returnValue;
    +   qx::QxClassX::invoke("person", "getId", p, "", (& returnValue));
    +   long lId = boost::any_cast<long>(returnValue);
    +
    +   // Call method 'myMethodWith2Params' with 2 parameters encapsulated in a string (default separator for parameters is character '|')
    +   // This way to pass parameters to the function works only if parameters are numeric or string
    +   // If parameters are more complex, then you have to encapsulate parameters in a list of qx::any, as shown below
    +   qx::QxClassX::invoke("person", "myMethodWith2Params", p, "36|my string param 2");
    +
    +   // Call method 'myMethodWith2Params' with 2 parameters encapsulated in a list of qx::any : std::vector<qx::any>
    +   std::vector<boost::any> lstParams;
    +   int iParam1 = 36; lstParams.push_back(iParam1); // Parameter at position 1
    +   QString sParam2 = "my string param 2"; lstParams.push_back(sParam2); // Parameter at position 2
    +   qx::QxClassX::invoke("person", "myMethodWith2Params", p, lstParams);
    +
    +   // Call static method 'myStaticMethodWith1Param' with 1 parameter and get return value
    +   qx::QxClassX::invokeStatic("person", "myStaticMethodWith1Param", "19", (& returnValue));
    +   double dValue = boost::any_cast<double>(returnValue);
    +
    +

    +
    +

    Cr�er une instance C++ + dynamiquement

    +
    + Le moteur d'introspection de la biblioth�que QxOrm permet de cr�er dynamiquement des instances + de classe (module + QxFactory, mod�le de conception fabrique ou design pattern factory) avec les m�thodes + suivantes : +
      +
    • qx::create(const + QString & sKey) : cr�ation d'une instance de type sKey sous la forme qx::any (contenant un pointeur intelligent de type + std::shared_ptr, alias de boost::shared_ptr par d�faut) ; +
    • +
    • qx::create_nude_ptr<T>(const QString & sKey) : cr�ation d'une + instance de type sKey sous la forme d'un pointeur nu de type T * (attention + � lib�rer la m�moire de ce pointeur pour �viter les fuites m�moire) ;
    • +
    • qx::create_void_ptr(const QString & sKey) : cr�ation d'une instance + de type sKey sous la forme d'un pointeur nu de type void * (attention � + lib�rer la m�moire de ce pointeur pour �viter les fuites m�moire).
    • +
    + Par exemple : le module QxService de la biblioth�que QxOrm + utilise ce m�canisme pour cr�er dynamiquement les instances de classe de service pour ex�cuter + les routines c�t� serveur : +

    + + + + + + + +
    +
       qx::service::IxService * ptr = qx::create_nude_ptr<qx::service::IxService>(m_sServiceName);   
    +
    +

    +
    +

    Parcourir la liste des + classes/propri�t�s enregistr�es dans le contexte QxOrm

    +
    + Voici un exemple d'utilisation du moteur d'introspection de la biblioth�que QxOrm : comment + lister toutes les classes, propri�t�s et m�thodes enregistr�es dans le contexte QxOrm ?
    +
    + + + + + + + +
    +
    QString QxClassX::dumpAllClasses()
    +{
    +   QxClassX::registerAllClasses();
    +   QxCollection<QString, IxClass *> * pAllClasses = QxClassX::getAllClasses();
    +   if (! pAllClasses) { qAssert(false); return ""; }
    +
    +   QString sDump;
    +   long lCount = pAllClasses->count();
    +   qDebug("[QxOrm] start dump all registered classes (%ld)", lCount);
    +   _foreach(IxClass * pClass, (* pAllClasses))
    +   { if (pClass) { sDump += pClass->dumpClass(); } }
    +   qDebug("[QxOrm] %s", "end dump all registered classes");
    +
    +   return sDump;
    +}
    +
    +QString IxClass::dumpClass() const
    +{
    +   QString sDump;
    +   sDump += "-- class '" + m_sKey + "' (name '" + m_sName + "', ";
    +   sDump += "description '" + m_sDescription + "', version '" + QString::number(m_lVersion) + "', ";
    +   sDump += "base class '" + (getBaseClass() ? getBaseClass()->getKey() : "") + "')\n";
    +
    +   long lCount = (m_pDataMemberX ? m_pDataMemberX->count() : 0);
    +   sDump += "\t* list of registered properties (" + QString::number(lCount) + ")\n";
    +   if (m_pDataMemberX)
    +   {
    +      IxDataMember * pId = this->getId();
    +      for (long l = 0; l < lCount; l++)
    +      {
    +         IxDataMember * p = m_pDataMemberX->get(l); if (! p) { continue; }
    +         IxSqlRelation * pRelation = p->getSqlRelation();
    +         QString sInfos = p->getKey() + ((p == pId) ? QString(" (id)") : QString());
    +         sInfos += (pRelation ? (QString(" (") + pRelation->getDescription() + QString(")")) : QString());
    +         sDump += "\t\t" + sInfos + "\n";
    +      }
    +   }
    +
    +   lCount = (m_pFctMemberX ? m_pFctMemberX->count() : 0);
    +   sDump += "\t* list of registered functions (" + QString::number(lCount) + ")\n";
    +   if (m_pFctMemberX)
    +   {
    +      _foreach_if(IxFunction_ptr p, (* m_pFctMemberX), (p))
    +      { QString sKey = p->getKey(); sDump += "\t\t" + sKey + "\n"; }
    +   }
    +
    +   qDebug("%s", qPrintable(sDump));
    +   return sDump;
    +}
    +
    +
    + Si on utilise la m�thode qx::QxClassX::dumpAllClasses() avec le tutoriel qxBlog, voici le r�sultat obtenu :
    +
    + + + + + + + +
    +
    [QxOrm] start dump all registered classes (4)
    +-- class 'author' (name 'author', description '', version '0', base class '')
    +	* list of registered properties (5)
    +		author_id (id)
    +		name
    +		birthdate
    +		sex
    +		list_blog (relation one-to-many)
    +	* list of registered functions (1)
    +		age
    +
    +-- class 'blog' (name 'blog', description '', version '0', base class '')
    +	* list of registered properties (6)
    +		blog_id (id)
    +		blog_text
    +		date_creation
    +		author_id (relation many-to-one)
    +		list_comment (relation one-to-many)
    +		list_category (relation many-to-many)
    +	* list of registered functions (0)
    +
    +-- class 'comment' (name 'comment', description '', version '0', base class '')
    +	* list of registered properties (4)
    +		comment_id (id)
    +		comment_text
    +		date_creation
    +		blog_id (relation many-to-one)
    +	* list of registered functions (0)
    +
    +-- class 'category' (name 'category', description '', version '0', base class '')
    +	* list of registered properties (4)
    +		category_id (id)
    +		name
    +		description
    +		list_blog (relation many-to-many)
    +	* list of registered functions (0)
    +
    +[QxOrm] end dump all registered classes
    +
    +
    +
    +
    + +

    Services : transf�rer la + couche de donn�es persistante sur le r�seau (module QxService)

    +
    + Le module QxService de + la biblioth�que QxOrm permet de cr�er rapidement un serveur d'applications C++ + performant (notion de services avec demande du client et r�ponse du serveur). + Un tutoriel est disponible sur + le site QxOrm afin de pr�senter un exemple d'utilisation du module QxService. + Le module QxService est bas� + sur le moteur d'introspection ainsi que le moteur de + s�rialisation de la biblioth�que QxOrm afin de transf�rer la couche de donn�es persistante + sur le r�seau et ex�cuter automatiquement les routines c�t� serveur. +

    + Remarque : pour activer le module QxService, il faut d�finir l'option de compilation + _QX_ENABLE_QT_NETWORK dans le fichier de configuration QxOrm.pri (ou QxOrm.cmake). + Cette option de compilation ajoute une d�pendance au binaire QtNetwork fourni avec la + biblioth�que Qt. +

    + Autre remarque : l'application QxEntityEditor est livr�e avec le plugin + QxEECppServicesExport : ce plugin g�n�re automatiquement le code source n�cessaire pour + transf�rer l'ensemble des entit�s d'un projet sur le r�seau. + Une liste de m�thodes client/serveur est g�n�r�e automatiquement : +
      +
    • count() : requ�te client/serveur pour compter le nombre d'�l�ments (avec possibilit� + d'utiliser un filtre SQL) ;
    • +
    • fetchById() : requ�te client/serveur pour alimenter les propri�t�s d'une entit� en + fonction de son identifiant ;
    • +
    • fetchAll() : requ�te client/serveur pour alimenter les propri�t�s de toutes les + entit�s d'une table ;
    • +
    • fetchByQuery() : requ�te client/serveur pour alimenter les propri�t�s des entit�s + filtr�es par une requ�te SQL ;
    • +
    • insert() : requ�te client/serveur pour ins�rer les donn�es d'une entit� ;
    • +
    • update() : requ�te client/serveur pour mettre � jour les donn�es d'une entit� ;
    • +
    • save() : requ�te client/serveur pour sauvegarder les donn�es d'une entit� (insertion + ou mise � jour) ;
    • +
    • deleteById() : requ�te client/serveur pour supprimer une entit� en fonction de son + identifiant ;
    • +
    • deleteAll() : requ�te client/serveur pour supprimer tous les �l�ments de la table + mapp�e � une entit� ;
    • +
    • deleteByQuery() : requ�te client/serveur pour supprimer tous les �l�ments en fonction + d'une requ�te SQL ;
    • +
    • destroyById() : requ�te client/serveur pour supprimer une entit� en fonction de son + identifiant (avec prise en compte de la suppression logique) ; +
    • +
    • destroyAll() : requ�te client/serveur pour supprimer tous les �l�ments de la table + mapp�e � une entit� (avec prise en compte de la suppression + logique) ;
    • +
    • destroyByQuery() : requ�te client/serveur pour supprimer tous les �l�ments en + fonction d'une requ�te SQL (avec prise en compte de la suppression + logique) ;
    • +
    • executeQuery() : requ�te client/serveur pour ex�cuter une + requ�te SQL personnalis�e ou proc�dure stock�e ;
    • +
    • exist() : requ�te client/serveur pour tester l'existence d'une entit� en fonction de + son identifiant ;
    • +
    • isValid() : requ�te client/serveur pour tester la validit� d'une entit� (module QxValidator).
    • +
    + Il est possible d'ajouter de nouveaux services ou de personnaliser les services g�n�r�s + automatiquement par l'application QxEntityEditor. +

    + L'objectif de ce chapite est de pr�senter les concepts � mettre en oeuvre pour utiliser le module + QxService : + +
    +

    Param�tres + d'entr�e/sortie d'un service (requ�te/r�ponse)

    +
    + Chaque fonction publi�e par un service dispose de param�tres d'entr�e (demande du client) et de + param�tres de sortie (r�ponse du serveur). + Ces param�tres d'entr�e/sortie doivent h�riter de l'interface qx::service::IxParameter et doivent �tre enregistr�es dans le contexte + QxOrm (par la fonction void qx::register_class<T>). +

    + Par exemple : voici un exemple de param�tres d'entr�e/sortie g�n�r�s automatiquement par + l'application QxEntityEditor bas� sur la classe blog du tutoriel qxBlog : +

    + * Fichier blog.services.gen.h :
    + + + + + + + +
    +
    namespace services {
    +
    +typedef boost::shared_ptr<blog> blog_ptr;
    +typedef qx::QxCollection<long, blog_ptr> list_of_blog;
    +typedef boost::shared_ptr<list_of_blog> list_of_blog_ptr;
    +
    +/* -- Service Input Parameters -- */
    +
    +class QXBLOG_SERVICES_EXPORT blog_input : public qx::service::IxParameter
    +{
    +
    +public:
    +
    +   blog_input();
    +   virtual ~blog_input();
    +
    +   long id;                   //!< Id to fetch or delete
    +   blog_ptr instance;         //!< Single instance to fetch, insert, update, delete or validate
    +   list_of_blog_ptr list;     //!< List of instances to fetch, insert, update, delete or validate
    +   qx_query query;            //!< Query to execute when fetching, updating or deleting
    +   QStringList columns;       //!< List of columns to fetch or update
    +   QStringList relations;     //!< List of relations to fetch
    +
    +};
    +
    +typedef boost::shared_ptr<services::blog_input> blog_input_ptr;
    +
    +/* -- Service Output Parameters -- */
    +
    +class QXBLOG_SERVICES_EXPORT blog_output : public qx::service::IxParameter
    +{
    +
    +public:
    +
    +   blog_output();
    +   virtual ~blog_output();
    +
    +   blog_ptr instance;            //!< Single instance from server
    +   list_of_blog_ptr list;        //!< List of instances from server
    +   QSqlError error;              //!< If a SQL error occurred, this output parameter is not empty
    +   qx::QxInvalidValueX invalid;  //!< Check if a single instance (or a list of instances) is valid
    +   qx_query query;               //!< Query which contains all results
    +   long count;                   //!< Count how many items in database using a query or not
    +   qx_bool exist;                //!< Check if a single instance (or a list of instances) exist in database
    +
    +};
    +
    +typedef boost::shared_ptr<services::blog_output> blog_output_ptr;
    +
    +} // namespace services
    +
    +QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QXBLOG_SERVICES(services::blog_input, qx::service::IxParameter, 0, services_blog_input)
    +QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QXBLOG_SERVICES(services::blog_output, qx::service::IxParameter, 0, services_blog_output)
    +
    +
    + * Fichier blog.services.gen.cpp :
    + + + + + + + +
    +
    QX_REGISTER_COMPLEX_CLASS_NAME_CPP_QXBLOG_SERVICES(services::blog_input, services_blog_input)
    +QX_REGISTER_COMPLEX_CLASS_NAME_CPP_QXBLOG_SERVICES(services::blog_output, services_blog_output)
    +
    +namespace qx {
    +
    +template <>
    +void register_class(QxClass<services::blog_input> & t)
    +{
    +   t.data(& services::blog_input::id, "id");
    +   t.data(& services::blog_input::instance, "instance");
    +   t.data(& services::blog_input::list, "list");
    +   t.data(& services::blog_input::query, "query");
    +   t.data(& services::blog_input::columns, "columns");
    +   t.data(& services::blog_input::relations, "relations");
    +}
    +
    +template <>
    +void register_class(QxClass<services::blog_output> & t)
    +{
    +   t.data(& services::blog_output::instance, "instance");
    +   t.data(& services::blog_output::list, "list");
    +   t.data(& services::blog_output::error, "error");
    +   t.data(& services::blog_output::invalid, "invalid");
    +   t.data(& services::blog_output::query, "query");
    +   t.data(& services::blog_output::count, "count");
    +   t.data(& services::blog_output::exist, "exist");
    +}
    +
    +} // namespace qx
    +
    +

    + Remarque : comme on peut le constater sur l'exemple ci-dessus, les param�tres + d'entr�e/sortie peuvent contenir des types complexes (des collections, des pointeurs, etc...). + Il est donc possible et tr�s simple de transf�rer des structures complexes sur le r�seau avec le + module QxService. +

    +
    +

    D�finir les fonctions + publi�es par un service

    +
    + Chaque service enregistr� dans le module QxService publie une liste de fonctions accessibles c�t� client (requ�tes + client/serveur). + Les services doivent h�riter de la classe de base qx::service::QxService<INPUT, OUTPUT> (les param�tres template + INPUT et OUTPUT correspondant aux param�tres + d'entr�e/sortie) et doivent �tre enregistr�s dans le contexte QxOrm (par la fonction + void qx::register_class<T>). +

    + Par exemple : voici un exemple de service g�n�r� automatiquement par l'application + QxEntityEditor bas� sur la classe blog du tutoriel qxBlog : +

    + * Fichier blog.services.gen.h :
    + + + + + + + +
    +
    namespace services {
    +
    +/* -- Service Definition -- */
    +
    +typedef qx::service::QxService< blog_input, blog_output > blog_base_class;
    +class QXBLOG_SERVICES_EXPORT blog_services : public blog_base_class
    +{
    +
    +   QX_REGISTER_FRIEND_CLASS(services::blog_services)
    +
    +public:
    +
    +   blog_services();
    +   virtual ~blog_services();
    +
    +protected:
    +
    +   void fetchById_();
    +   void fetchAll_();
    +   void fetchByQuery_();
    +
    +   void insert_();
    +   void update_();
    +   void save_();
    +   void deleteById_();
    +   void deleteAll_();
    +   void deleteByQuery_();
    +   void destroyById_();
    +   void destroyAll_();
    +   void destroyByQuery_();
    +
    +   void executeQuery_();
    +   void callQuery_();
    +   void exist_();
    +   void count_();
    +   void isValid_();
    +
    +#ifdef _QXBLOG_SERVICES_MODE_CLIENT
    +
    +public:
    +
    +   blog_ptr fetchById(long id, const QStringList & columns = QStringList(), const QStringList & relations = QStringList());
    +   QSqlError fetchById(blog_ptr & p, const QStringList & columns = QStringList(), const QStringList & relations = QStringList());
    +   QSqlError fetchById(list_of_blog_ptr & lst, const QStringList & columns = QStringList(), const QStringList & relations = QStringList());
    +   QSqlError fetchAll(list_of_blog_ptr & lst, const QStringList & columns = QStringList(), const QStringList & relations = QStringList());
    +   QSqlError fetchByQuery(const qx_query & query, list_of_blog_ptr & lst, const QStringList & columns = QStringList(),
    +                                       const QStringList & relations = QStringList());
    +
    +   QSqlError insert(blog_ptr & p, const QStringList & relations = QStringList());
    +   QSqlError insert(list_of_blog_ptr & lst, const QStringList & relations = QStringList());
    +   QSqlError update(blog_ptr & p, const qx_query & query = qx_query(),
    +                              const QStringList & columns = QStringList(), const QStringList & relations = QStringList());
    +   QSqlError update(list_of_blog_ptr & lst, const qx_query & query = qx_query(),
    +                              const QStringList & columns = QStringList(), const QStringList & relations = QStringList());
    +   QSqlError save(blog_ptr & p, const QStringList & relations = QStringList());
    +   QSqlError save(list_of_blog_ptr & lst, const QStringList & relations = QStringList());
    +
    +   QSqlError deleteById(long id);
    +   QSqlError deleteById(blog_ptr & p);
    +   QSqlError deleteById(list_of_blog_ptr & lst);
    +   QSqlError deleteAll();
    +   QSqlError deleteByQuery(const qx_query & query);
    +   QSqlError destroyById(long id);
    +   QSqlError destroyById(blog_ptr & p);
    +   QSqlError destroyById(list_of_blog_ptr & lst);
    +   QSqlError destroyAll();
    +   QSqlError destroyByQuery(const qx_query & query);
    +
    +   QSqlError executeQuery(qx_query & query, blog_ptr & p);
    +   QSqlError executeQuery(qx_query & query, list_of_blog_ptr & lst);
    +   QSqlError callQuery(qx_query & query);
    +   qx_bool exist(blog_ptr & p);
    +   qx_bool exist(list_of_blog_ptr & lst);
    +   QSqlError count(long & lCount, const qx_query & query = qx_query());
    +   qx::QxInvalidValueX isValid(blog_ptr & p);
    +   qx::QxInvalidValueX isValid(list_of_blog_ptr & lst);
    +
    +#endif // _QXBLOG_SERVICES_MODE_CLIENT
    +
    +};
    +
    +typedef boost::shared_ptr<services::blog_services> blog_services_ptr;
    +
    +} // namespace services
    +
    +QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QXBLOG_SERVICES(services::blog_services, qx::service::IxService, 0, services_blog_services)
    +
    +
    + * Fichier blog.services.gen.cpp :
    + + + + + + + +
    +
    QX_REGISTER_COMPLEX_CLASS_NAME_CPP_QXBLOG_SERVICES(services::blog_services, services_blog_services)
    +
    +namespace qx {
    +
    +template <>
    +void register_class(QxClass<services::blog_services> & t)
    +{
    +   t.fct_0<void>(& services::blog_services::fetchById_, "fetchById");
    +   t.fct_0<void>(& services::blog_services::fetchAll_, "fetchAll");
    +   t.fct_0<void>(& services::blog_services::fetchByQuery_, "fetchByQuery");
    +
    +   t.fct_0<void>(& services::blog_services::insert_, "insert");
    +   t.fct_0<void>(& services::blog_services::update_, "update");
    +   t.fct_0<void>(& services::blog_services::save_, "save");
    +   t.fct_0<void>(& services::blog_services::deleteById_, "deleteById");
    +   t.fct_0<void>(& services::blog_services::deleteAll_, "deleteAll");
    +   t.fct_0<void>(& services::blog_services::deleteByQuery_, "deleteByQuery");
    +   t.fct_0<void>(& services::blog_services::destroyById_, "destroyById");
    +   t.fct_0<void>(& services::blog_services::destroyAll_, "destroyAll");
    +   t.fct_0<void>(& services::blog_services::destroyByQuery_, "destroyByQuery");
    +
    +   t.fct_0<void>(& services::blog_services::executeQuery_, "executeQuery");
    +   t.fct_0<void>(& services::blog_services::callQuery_, "callQuery");
    +   t.fct_0<void>(& services::blog_services::exist_, "exist");
    +   t.fct_0<void>(& services::blog_services::count_, "count");
    +   t.fct_0<void>(& services::blog_services::isValid_, "isValid");
    +}
    +
    +} // namespace qx
    +
    +// Then there is the implementation of all functions provided by the service...
    +
    +

    + Remarque : une fois d�finies dans le contexte QxOrm, le client peut appeler les fonctions + publi�es par le service : les routines c�t� serveur sont alors ex�cut�es automatiquement. + La s�rialisation des donn�es ainsi que la gestion de la couche r�seau pour le transfert des + donn�es sont g�r�es de mani�re transparente par le module QxService. +

    +
    +

    Liste des options + disponibles c�t� serveur

    +
    + Le serveur d'application C++ bas� sur le module QxService dispose de + plusieurs param�tres accessibles par la classe singleton qx::service::QxConnect : +
      +
    • setPort() : port d'�coute pour recevoir les requ�tes du client et envoyer les + r�ponses du serveur ;
    • +
    • setThreadCount() : nombre de threads disponibles c�t� serveur pour traiter les + demandes du client ;
    • +
    • setSerializationType() : type de s�rialisation utilis� + pour envoyer les r�ponses du serveur ;
    • +
    • setCompressData() : permet de d�finir si les donn�es renvoy�es par le serveur sont + compress�es ou non ;
    • +
    • setEncryptData() : permet de d�finir si les donn�es renvoy�es par le serveur sont + crypt�es ou non (avec possibilit� de renseigner une cl� de cryptage).
    • +
    +
    +
    +

    Param�trage de la + connexion c�t� client

    +
    + La couche cliente bas�e sur le module QxService dispose de plusieurs param�tres accessibles par la classe + singleton qx::service::QxConnect : +
      +
    • setIp() : adresse IP du serveur d'application C++ ;
    • +
    • setPort() : port utilis� par le serveur d'application C++ ;
    • +
    • setSerializationType() : type de s�rialisation utilis� + par la couche cliente pour envoyer les requ�tes du client au serveur ;
    • +
    • setCompressData() : permet de d�finir si les donn�es envoy�es au serveur sont + compress�es ou non ;
    • +
    • setEncryptData() : permet de d�finir si les donn�es envoy�es au serveur sont + crypt�es ou non (avec possibilit� de renseigner une cl� de cryptage).
    • +
    +
    +
    +

    Gestion de + l'authentification dans un service

    +
    + Il est classique d'impl�menter un contr�le au niveau du serveur pour v�rifier l'utilisateur + connect� � la couche cliente. + L'interface qx::service::IxService (classe de base de tous les services enregistr�s + par le module QxService) + fournit des m�thodes virtuelles qui peuvent �tre surcharg�es pour g�rer cette probl�matique : +
      +
    • onBeforeProcess() : m�thode virtuelle appel�e syst�matiquement avant ex�cution de + la routine serveur ;
    • +
    • onAfterProcess() : m�thode virtuelle appel�e syst�matiquement apr�s ex�cution de + la routine serveur.
    • +
    +
    + Par exemple : voici une classe de base nomm�e ParameterAuthentication qui peut + �tre utilis�e par tous les param�tres d'entr�e/sortie, cette classe fournit 3 propri�t�s + login, password et token : +

    + * Fichier ParameterAuthentication.h :
    + + + + + + + +
    +
    class MY_DLL_EXPORT ParameterAuthentication : public qx::service::IxParameter
    +{
    + 
    +public:
    + 
    +   ParameterAuthentication();
    +   virtual ~ParameterAuthentication();
    + 
    +   QString login;
    +   QString password;
    +   QString token;
    +   // etc..., put here all properties required by the authentication process
    + 
    +};
    + 
    +typedef boost::shared_ptr<ParameterAuthentication> ParameterAuthentication_ptr;
    + 
    +QX_REGISTER_COMPLEX_CLASS_NAME_HPP_MY_DLL(ParameterAuthentication, qx::service::IxParameter, 0, ParameterAuthentication)
    +
    +
    + * Fichier ParameterAuthentication.cpp :
    + + + + + + + +
    +
    QX_REGISTER_COMPLEX_CLASS_NAME_CPP_MY_DLL(ParameterAuthentication, ParameterAuthentication)
    + 
    +namespace qx {
    + 
    +template <>
    +void register_class(QxClass<ParameterAuthentication> & t)
    +{
    +   t.data(& ParameterAuthentication::login, "login");
    +   t.data(& ParameterAuthentication::password, "password");
    +   t.data(& ParameterAuthentication::token, "token");
    +}
    + 
    +} // namespace qx
    +
    +

    + Maintenant que l'on dispose d'une classe de base pour nos param�tres + (ParameterAuthentication), nous allons cr�er une classe de base utilis�e par tous nos + services nomm�e ServiceAuthentication<INPUT, OUTPUT>. + Cette classe de base des services va surcharger la m�thode virtuelle onBeforeProcess() + afin de g�rer l'authentification avant ex�cution de la routine serveur : +

    + * Fichier ServiceAuthentication.h :
    + + + + + + + +
    +
    #include "ParameterAuthentication.h"
    + 
    +template <class INPUT, class OUTPUT>
    +class ServiceAuthentication : public qx::service::QxService<INPUT, OUTPUT>
    +{
    + 
    +public:
    + 
    +   ServiceAuthentication(const QString & sServiceName) : qx::service::QxService<INPUT, OUTPUT>(sServiceName) { ; }
    +   virtual ~ServiceAuthentication() { ; }
    + 
    +   virtual void onBeforeProcess()
    +   {
    +      // Here you can implement your own authentication control (checking login/password for example)
    +      // You can get input authentication parameters like this :
    +      ParameterAuthentication_ptr pParams = getInputParameter();
    +      pParams->login, pParams->password, etc...
    + 
    +      // If authentication is not valid, then you can throw an exception (and stop process before executing service function)
    +      throw qx::exception("Authentication error !");
    +   }
    + 
    +};
    +
    +

    + A pr�sent, nous disposons des classes de base ParameterAuthentication et + ServiceAuthentication<INPUT, OUTPUT> : toutes les classes de param�tres et toutes + les classes de services doivent h�riter de ces classes de base pour g�rer automatiquement + l'authentification, et retourner une erreur au client si les param�tres de l'utilisateur ne sont + pas valides. +

    + Remarque : de la m�me fa�on que pour g�rer l'authentification, il est possible de mettre + en place des logs automatiques en surchargeant les m�thodes virtuelles onBeforeProcess() + et onAfterProcess(). +

    +
    +

    Requ�tes client/serveur + asynchrones

    +
    + Par d�faut, les requ�tes client/serveur sont synchrones : ce qui signifie que la couche cliente + attend la r�ponse du serveur pour continuer son ex�cution. + Dans une interface graphique utilisateur (GUI), une requ�te client/serveur bloque + l'application (freeze) si elle est ex�cut�e dans le thread principal : si le serveur met + du temps pour renvoyer sa r�ponse, l'utilisateur peut alors penser qu'il s'agit d'un crash de + l'application. + La module QxService + propose une solution simple pour effectuer des requ�tes asynchrones (qui ne bloquent donc pas + l'interface graphique de l'utilisateur) gr�ce � la classe qx::service::QxClientAsync. +

    + La classe qx::service::QxClientAsync utilise le moteur + d'introspection de la biblioth�que QxOrm ainsi que le m�canisme SIGNAL-SLOT de + Qt. + Elle prend en param�tre : +
      +
    • une instance de service ;
    • +
    • les param�tres d'entr�e/sortie du service ;
    • +
    • le nom de la routine serveur � ex�cuter (sous forme de chaine de caract�res) ;
    • +
    • une fonction � appeler une fois que la transaction est termin�e (connexion � l'�v�nement + signal finished()). +
    • +
    +
    + Voici l'exemple issu du tutoriel qxClientServer qui + ex�cute une routine serveur de mani�re asynchrone : +

    + + + + + + + +
    +
    void main_dlg::onClickBtnDateTimeAsync()
    +{
    +   if (m_pDateTimeAsync) { qDebug("[QxOrm] '%s' transaction is already running", "server_infos::get_current_date_time"); return; }
    +
    +   // Cr�ation d'une instance de service et appel � la m�thode pour recevoir la date-heure courante du serveur (mode asynchrone)
    +   server_infos_ptr service = server_infos_ptr(new server_infos());
    +   m_pDateTimeAsync.reset(new qx::service::QxClientAsync());
    +   QObject::connect(m_pDateTimeAsync.get(), SIGNAL(finished()), this, SLOT(onDateTimeAsyncFinished()));
    +   m_pDateTimeAsync->setService(service, "get_current_date_time");
    +   m_pDateTimeAsync->start();
    +}
    +
    +void main_dlg::onDateTimeAsyncFinished()
    +{
    +   if (! m_pDateTimeAsync || ! m_pDateTimeAsync->getService()) { return; }
    +   updateLastTransactionLog(m_pDateTimeAsync->getService()->getTransaction());
    +   m_pDateTimeAsync.reset();
    +}
    +
    +

    + Remarque : l'exemple ci-dessus montre comment effectuer une requ�te asynchrone avec les + actions suivantes : +
      +
    • cr�ation d'une instance d'un service (de type server_infos_ptr pour cet exemple) ; +
    • +
    • cr�ation d'une instance de type qx::service::QxClientAsync ;
    • +
    • connexion � l'�v�nement finished (pour indiquer qu'une r�ponse du serveur vient + d'arriver) ;
    • +
    • passage de l'instance du service et de la m�thode � appeler (sous forme de chaine de + caract�res) � l'objet qx::service::QxClientAsync ;
    • +
    • d�marrage de la transaction avec l'appel de la m�thode start().
    • +
    +
    +
    +
    + +

    Moteur mod�le/vue (module + QxModelView)

    +
    + Le module QxModelView permet d'utiliser le moteur model/view de Qt avec + toutes les classes enregistr�es dans le contexte QxOrm : +
      +
    • QML : toute propri�t� enregistr�e dans le contexte QxOrm est + accessible en QML : le module QxModelView permet ainsi de faciliter l'int�raction entre QML + et les bases de donn�es ;
    • +
    • Qt widgets : utilisation de QTableView ou QListView + par exemple pour afficher/modifier le contenu d'une table de la base de donn�es.
    • +
    + L'interface qx::IxModel + propose une base commune pour tous les mod�les li�s aux classes persistantes d�clar�es dans le + contexte QxOrm. + Les m�thodes de cette classe pr�fix�es par 'qx' appellent les fonctions du namespace qx::dao et + communiquent donc directement avec la base de donn�es. + L'interface qx::IxModel + fournit �galement des m�thodes d�finies Q_INVOKABLE et sont donc accessibles directement en + QML : +
      +
    • qxCount_() : compte le nombre d'�l�ments dans la table de la base de donn�es associ�e + au mod�le (avec possibilit� d'indiquer un filtre SQL) ;
    • +
    • qxFetchById_() : alimente le mod�le en fonction de l'identifiant pass� en param�tre ; +
    • +
    • qxFetchAll_() : alimente le mod�le avec tous les �l�ments contenus dans la table de + la base de donn�es associ�e au mod�le ;
    • +
    • qxFetchByQuery_() : alimente le mod�le avec les �l�ments de la table de la base de + donn�es associ�e au mod�le en fonction d'une requ�te SQL ;
    • +
    • qxFetchRow_() : alimente (met � jour) une ligne du mod�le (chaque ligne du mod�le + dispose de son propre identifiant de base de donn�es) ;
    • +
    • qxInsert_() : ins�re l'int�gralit� du mod�le en base de donn�es ;
    • +
    • qxInsertRow_() : ins�re une ligne du mod�le en base de donn�es ;
    • +
    • qxUpdate_() : met � jour l'int�gralit� du mod�le en base de donn�es ;
    • +
    • qxUpdateRow_() : met � jour une ligne du mod�le en base de donn�es ;
    • +
    • qxSave_() : sauvegarde l'int�gralit� du mod�le en base de donn�es (insertion ou mise + � jour) ;
    • +
    • qxSaveRow_() : sauvegarde une ligne du mod�le en base de donn�es (insertion ou mise � + jour) ;
    • +
    • qxDeleteById_() : supprime un �l�ment de la base de donn�es en fonction de + l'identifiant pass� en param�tre ;
    • +
    • qxDeleteAll_() : supprime tous les �l�ments de la table de la base de donn�es + associ�e au mod�le ;
    • +
    • qxDeleteByQuery_() : supprime les �l�ments de la table de la base de donn�es associ�e + au mod�le en fonction d'une requ�te SQL ;
    • +
    • qxDeleteRow_() : supprime une ligne du mod�le de la base de donn�es (chaque ligne du + mod�le dispose de son propre identifiant de base de donn�es) ;
    • +
    • qxDestroyById_() : supprime un �l�ment de la base de donn�es en fonction de + l'identifiant pass� en param�tre (avec prise en compte de la + suppression logique) ;
    • +
    • qxDestroyAll_() : supprime tous les �l�ments de la table de la base de donn�es + associ�e au mod�le (avec prise en compte de la suppression + logique) ;
    • +
    • qxDestroyByQuery_() : supprime les �l�ments de la table de la base de donn�es + associ�e au mod�le en fonction d'une requ�te SQL (avec prise en compte de la suppression logique) ;
    • +
    • qxDestroyRow_() : supprime une ligne du mod�le de la base de donn�es (chaque ligne du + mod�le dispose de son propre identifiant de base de donn�es), avec prise en compte de la suppression logique ;
    • +
    • qxExecuteQuery_() : alimente le mod�le en fonction d'une requ�te SQL personnalis�e ou proc�dure stock�e ;
    • +
    • qxExist_() : teste l'existence d'un �l�ment en fonction de l'identifiant pass� en + param�tre ;
    • +
    • qxValidate_() : teste la validit� de l'int�gralit� du mod�le (module QxValidator) ;
    • +
    • qxValidateRow_() : teste la validit� d'une ligne du mod�le (module QxValidator).
    • +
    +
    + Remarque : le projet de test qxBlogModelView pr�sent dans le dossier ./test/ + du package QxOrm montre comment cr�er rapidement un mod�le et l'associer au moteur + model/view de Qt (d'abord dans un widget Qt, puis dans une vue QML). +

    +

    D�finir un mod�le + "simple" (sans relation)

    +
    + Toute classe enregistr�e dans le contexte QxOrm peut �tre utilis�e en tant que mod�le afin + d'alimenter des vues. + La classe de base qx::IxModel des mod�les QxOrm h�rite de la classe Qt QAbstractItemModel : + les mod�les QxOrm sont donc enti�rement compatibles avec le moteur model/view de Qt. +

    + Une seule ligne de code est suffisante pour instancier un mod�le QxOrm : +

    + + + + + + + +
    +
       qx::IxModel * pModel = new qx::QxModel<MyClass>();   
    +
    +
    + Remarque : le mod�le cr�� avec cette ligne de code expose automatiquement toutes les + propri�t�s enregistr�es dans le contexte QxOrm au moteur model/view. +

    +
    +

    Mod�les avec relations + (notion de mod�les imbriqu�s)

    +
    + Adapter les relations entre classe (1-n, n-1 ou n-n) au moteur model/view + de Qt est complexe : la solution propos�e par la biblioth�que QxOrm est l'utilisation de mod�les imbriqu�s. + Pour plus de d�tails sur la notion de mod�les imbriqu�s, un tutoriel est disponible sur le site developpez.com. +

    + Pour utiliser les relations (1-n, n-1 ou n-n) avec le module QxModelView, il + est important de comprendre qu'il peut y avoir une hi�rarchie entre mod�les (un mod�le + parent peut avoir plusieurs mod�les enfants associ�s, c'est la notion de mod�les imbriqu�s). +

    + Afin de pouvoir travailler avec des relations (mod�les imbriqu�s), il est n�cessaire de cr�er + des classes mod�les qui h�ritent de : qx::QxModel<T>. + Ainsi, toutes les propri�t�s simples (non relation) sont automatiquement expos�es aux vues + (gr�ce � la classe de base), il reste � �crire uniquement les accesseurs pour acc�der aux + relations. + L'application QxEntityEditor est livr�e avec le plugin QxEECppModelViewExport : + ce plugin g�n�re automatiquement le code source pour pouvoir travailler avec des mod�les + imbriqu�s. +

    + Voici un exemple de code g�n�r� par l'application QxEntityEditor afin de cr�er un mod�le + bas� sur la classe blog (voir le tutoriel + qxBlog pour plus de d�tails). + La classe blog dispose de 3 relations : author (n-1), list_of_comment (1-n) + et list_of_category (n-n) : +

    + * Fichier blog.model_view.gen.h :
    + + + + + + + +
    +
    namespace model_view {
    +
    +typedef qx::QxModel<blog> blog_model_base_class;
    +
    +class QXBLOG_MODEL_VIEW_EXPORT blog_model : public blog_model_base_class
    +{
    +
    +   Q_OBJECT
    +
    +public:
    +
    +   blog_model(QObject * parent = 0);
    +   blog_model(qx::IxModel * other, QObject * parent);
    +   virtual ~blog_model();
    +
    +   Q_INVOKABLE QObject * author(int row, bool bLoadFromDatabase = false, const QString & sAppendRelations = QString());
    +   Q_INVOKABLE QObject * list_of_comment(int row, bool bLoadFromDatabase = false, const QString & sAppendRelations = QString());
    +   Q_INVOKABLE QObject * list_of_category(int row, bool bLoadFromDatabase = false, const QString & sAppendRelations = QString());
    +
    +   /* List of properties exposed by the model (3) :
    +      - blog_id
    +      - title
    +      - text
    +   */
    +
    +protected:
    +
    +   virtual void syncNestedModel(int row, const QStringList & relation);
    +   virtual void syncAllNestedModel(const QStringList & relation);
    +
    +};
    +
    +} // namespace model_view
    +
    +
    + * Fichier blog.model_view.gen.cpp :
    +
    +
    namespace model_view {
    +
    +blog_model::blog_model(QObject * parent /* = 0 */) : blog_model_base_class(parent) { ; }
    +
    +blog_model::blog_model(qx::IxModel * other, QObject * parent) : blog_model_base_class(other, parent) { ; }
    +
    +blog_model::~blog_model() { ; }
    +
    +QObject * blog_model::author(int row, bool bLoadFromDatabase /* = false */, const QString & sAppendRelations /* = QString() */)
    +{
    +   QString sRelation = "author";
    +   qx::IxModel * pChild = (bLoadFromDatabase ? NULL : this->getChild(row, sRelation));
    +   if (pChild) { return static_cast<QObject *>(pChild); }
    +
    +   if ((row < 0) || (row >= this->m_model.count())) { qAssert(false); return NULL; }
    +   blog_model_base_class::type_ptr ptr = this->m_model.getByIndex(row);
    +   if (! ptr) { qAssert(false); return NULL; }
    +   long id = ptr->getblog_id();
    +   blog::type_author value = ptr->getauthor();
    +
    +   if (bLoadFromDatabase)
    +   {
    +      if (! sAppendRelations.isEmpty() && ! sAppendRelations.startsWith("->") && ! sAppendRelations.startsWith(">>")) { sRelation += "->" + sAppendRelations; }
    +      else if (! sAppendRelations.isEmpty()) { sRelation += sAppendRelations; }
    +      blog tmp;
    +      tmp.setblog_id(id);
    +      this->m_lastError = qx::dao::fetch_by_id_with_relation(sRelation, tmp);
    +      if (this->m_lastError.isValid()) { return NULL; }
    +      value = tmp.getauthor();
    +      ptr->setauthor(value);
    +   }
    +
    +   model_view::author_model * pNewChild = NULL;
    +   pChild = qx::model_view::create_nested_model_with_type(this, QModelIndex(), value, pNewChild);
    +   if (pChild) { this->insertChild(row, "author", pChild); }
    +   return static_cast<QObject *>(pChild);
    +}
    +
    +QObject * blog_model::list_of_comment(int row, bool bLoadFromDatabase /* = false */, const QString & sAppendRelations /* = QString() */)
    +{
    +   QString sRelation = "list_of_comment";
    +   qx::IxModel * pChild = (bLoadFromDatabase ? NULL : this->getChild(row, sRelation));
    +   if (pChild) { return static_cast<QObject *>(pChild); }
    +
    +   if ((row < 0) || (row >= this->m_model.count())) { qAssert(false); return NULL; }
    +   blog_model_base_class::type_ptr ptr = this->m_model.getByIndex(row);
    +   if (! ptr) { qAssert(false); return NULL; }
    +   long id = ptr->getblog_id();
    +   blog::type_list_of_comment value = ptr->getlist_of_comment();
    +
    +   if (bLoadFromDatabase)
    +   {
    +      if (! sAppendRelations.isEmpty() && ! sAppendRelations.startsWith("->") && ! sAppendRelations.startsWith(">>")) { sRelation += "->" + sAppendRelations; }
    +      else if (! sAppendRelations.isEmpty()) { sRelation += sAppendRelations; }
    +      blog tmp;
    +      tmp.setblog_id(id);
    +      this->m_lastError = qx::dao::fetch_by_id_with_relation(sRelation, tmp);
    +      if (this->m_lastError.isValid()) { return NULL; }
    +      value = tmp.getlist_of_comment();
    +      ptr->setlist_of_comment(value);
    +   }
    +
    +   model_view::comment_model * pNewChild = NULL;
    +   pChild = qx::model_view::create_nested_model_with_type(this, QModelIndex(), value, pNewChild);
    +   if (pChild) { this->insertChild(row, "list_of_comment", pChild); }
    +   return static_cast<QObject *>(pChild);
    +}
    +
    +QObject * blog_model::list_of_category(int row, bool bLoadFromDatabase /* = false */, const QString & sAppendRelations /* = QString() */)
    +{
    +   QString sRelation = "list_of_category";
    +   qx::IxModel * pChild = (bLoadFromDatabase ? NULL : this->getChild(row, sRelation));
    +   if (pChild) { return static_cast<QObject *>(pChild); }
    +
    +   if ((row < 0) || (row >= this->m_model.count())) { qAssert(false); return NULL; }
    +   blog_model_base_class::type_ptr ptr = this->m_model.getByIndex(row);
    +   if (! ptr) { qAssert(false); return NULL; }
    +   long id = ptr->getblog_id();
    +   blog::type_list_of_category value = ptr->getlist_of_category();
    +
    +   if (bLoadFromDatabase)
    +   {
    +      if (! sAppendRelations.isEmpty() && ! sAppendRelations.startsWith("->") && ! sAppendRelations.startsWith(">>")) { sRelation += "->" + sAppendRelations; }
    +      else if (! sAppendRelations.isEmpty()) { sRelation += sAppendRelations; }
    +      blog tmp;
    +      tmp.setblog_id(id);
    +      this->m_lastError = qx::dao::fetch_by_id_with_relation(sRelation, tmp);
    +      if (this->m_lastError.isValid()) { return NULL; }
    +      value = tmp.getlist_of_category();
    +      ptr->setlist_of_category(value);
    +   }
    +
    +   model_view::category_model * pNewChild = NULL;
    +   pChild = qx::model_view::create_nested_model_with_type(this, QModelIndex(), value, pNewChild);
    +   if (pChild) { this->insertChild(row, "list_of_category", pChild); }
    +   return static_cast<QObject *>(pChild);
    +}
    +
    +void blog_model::syncNestedModel(int row, const QStringList & relation)
    +{
    +   Q_UNUSED(relation);
    +   qx::IxModel * pNestedModel = NULL;
    +   if ((row < 0) || (row >= this->m_model.count())) { return; }
    +   blog_model_base_class::type_ptr ptr = this->m_model.getByIndex(row);
    +   if (! ptr) { return; }
    +
    +   pNestedModel = this->getChild(row, "author");
    +   if (pNestedModel)
    +   {
    +      this->syncNestedModelRecursive(pNestedModel, relation);
    +      blog::type_author value;
    +      qx::model_view::sync_nested_model(pNestedModel, value);
    +      ptr->setauthor(value);
    +   }
    +
    +   pNestedModel = this->getChild(row, "list_of_comment");
    +   if (pNestedModel)
    +   {
    +      this->syncNestedModelRecursive(pNestedModel, relation);
    +      blog::type_list_of_comment value;
    +      qx::model_view::sync_nested_model(pNestedModel, value);
    +      ptr->setlist_of_comment(value);
    +   }
    +
    +   pNestedModel = this->getChild(row, "list_of_category");
    +   if (pNestedModel)
    +   {
    +      this->syncNestedModelRecursive(pNestedModel, relation);
    +      blog::type_list_of_category value;
    +      qx::model_view::sync_nested_model(pNestedModel, value);
    +      ptr->setlist_of_category(value);
    +   }
    +}
    +
    +void blog_model::syncAllNestedModel(const QStringList & relation)
    +{
    +   if (this->m_lstChild.count() <= 0) { return; }
    +   for (long l = 0; l < this->m_model.count(); l++)
    +   { this->syncNestedModel(static_cast<int>(l), relation); }
    +}
    +
    +} // namespace model_view
    +
    +

    + Remarque : comme on peut le constater sur l'exemple ci-dessus, le code source � �crire + pour travailler avec des mod�les imbriqu�s est verbeux. + Afin de travailler avec les relations, il est donc fortement recommand� d'utiliser + l'application QxEntityEditor afin de g�n�rer le code source automatiquement. +

    +
    +

    Int�raction avec les vues + QML

    +
    + Voici un exemple en QML (en Qt5, le module QxModelView + �tant �galement compatible avec Qt4) qui utilise la table 'author' d�finie dans le tutoriel qxBlog (le code source de cet + exemple QML est disponible dans le projet de test qxBlogModelView pr�sent dans le package + QxOrm) :
    +
    + + + + + + + +
    +
    // Create a model and fetch all data from database
    +qx::IxModel * pModel = new qx::QxModel<author>();
    +pModel->qxFetchAll();
    +
    +// Associate the model to a QML view and display it
    +QQuickView qmlView;
    +qmlView.rootContext()->setContextProperty("myModel", pModel);
    +qmlView.setSource(QUrl("qrc:/documents/main.qml"));
    +qmlView.show();
    +
    +
    + Et voici le contenu du fichier 'main.qml' :
    +
    + + + + + + + +
    +
    import QtQuick 2.1
    +import QtQuick.Controls 1.0
    +
    +Item {
    +   width: 400
    +   height: 300
    +   Row {
    +      height: 20
    +      spacing: 20
    +      Button {
    +         text: "Clear"
    +         onClicked: myModel.clear()
    +      }
    +      Button {
    +         text: "Fetch All"
    +         onClicked: myModel.qxFetchAll_()
    +      }
    +      Button {
    +         text: "Save"
    +         onClicked: myModel.qxSave_()
    +      }
    +   }
    +   ListView {
    +      y: 30
    +      height: 270
    +      model: myModel
    +      delegate: Row {
    +         height: 20
    +         spacing: 10
    +         Text { text: "id: " + author_id }
    +         TextField {
    +            text: name
    +            onTextChanged: name = text
    +         }
    +      }
    +   }
    +}
    +
    +
    + Ce qui donne le r�sultat suivant � l'ex�cution :
    +
    + qx_model_view_02
    +
    + Remarque : comme on peut le constater dans le fichier 'main.qml', les propri�t�s + 'author_id' et 'name' du mod�le 'author' (variable myModel) sont + accessibles automatiquement en lecture/�criture (car elles ont �t� enregistr�es dans le contexte + QxOrm).
    + De plus, l'interface qx::IxModel propose une liste de m�thodes accessibles en QML (utilisation + de Q_INVOKABLE) pour communiquer directement avec la base de donn�es : ainsi, le bouton + 'Save' de l'�cran ci-dessus enregistre le mod�le en base de donn�es depuis QML. +

    + Autre remarque : un plugin de l'application QxEntityEditor + permet de g�n�rer automatiquement le code des mod�les pour la gestion des relations. Il + est ainsi possible de travailler avec des mod�les imbriqu�s (pour plus de d�tails sur la notion de mod�les + imbriqu�s, rendez-vous sur ce tutoriel du site developpez.com). +

    +
    +

    Int�raction avec les vues + QtWidget

    +
    + Voici un exemple de cr�ation d'un mod�le pour afficher/modifier les donn�es de la table + 'author' (voir le tutoriel qxBlog + pour la d�finition de la classe 'author') dans un QTableView (le code source + de cet exemple est disponible dans le projet de test qxBlogModelView pr�sent dans le + package QxOrm) :
    +
    + + + + + + + +
    +
       // Create a model and fetch all data from database
    +   qx::IxModel * pModel = new qx::QxModel<author>();
    +   pModel->qxFetchAll();
    +
    +   // Associate the model to a QTableView and display it   
    +   QTableView tableView;
    +   tableView.setModel(pModel);
    +   tableView.show();
    +
    +
    + Ce qui donne le r�sultat suivant � l'ex�cution :
    +
    + qx_model_view_01
    +
    + Remarque : Qt propose par d�faut plusieurs vues QtWidget qui peuvent �tre mapp�es + sur un mod�le, par exemple : QListView, QTableView, QTreeView. + Il est �galement possible d'utiliser la classe QDataWidgetMapper + pour cr�er ses propres formulaires bas�s sur des mod�les (un tutoriel est disponible sur le site developpez.com). +

    +
    +

    Connexion d'un mod�le au + module QxService

    +
    + Le module QxModelView + fournit la classe template : qx::QxModelService<T, S> (qui h�rite de : qx::QxModel<T> + >> qx::IxModel + >> QAbstractItemModel). + Cette classe dispose de 2 param�tres template : +
      +
    • T : classe enregistr�e dans le contexte QxOrm dont toutes les propri�t�s sont + expos�es au moteur + model/view de Qt ;
    • +
    • S : classe de services du module QxService pour + acc�der/enregistrer les donn�es du mod�le (requ�tes client/serveur).
    • +
    + Les donn�es propos�es par le mod�le sont ainsi issues de requ�tes client/serveur gr�ce au module QxService (elles ne proviennent pas directement de la base de + donn�es). + La classe de services S doit proposer les m�thodes suivantes : +
      +
    • count() : requ�te client/serveur pour compter le nombre d'�l�ments (avec + possibilit� d'utiliser un filtre SQL) ;
    • +
    • fetchById() : requ�te client/serveur pour alimenter les propri�t�s du mod�le en + fonction de son identifiant ;
    • +
    • fetchAll() : requ�te client/serveur pour alimenter les propri�t�s du mod�le qui + contiendra tous les �l�ments d'une table ;
    • +
    • fetchByQuery() : requ�te client/serveur pour alimenter les propri�t�s du mod�le + qui contiendra les �l�ments filtr�s par une requ�te SQL ;
    • +
    • insert() : requ�te client/serveur pour ins�rer les donn�es du mod�le ;
    • +
    • update() : requ�te client/serveur pour mettre � jour les donn�es du mod�le ;
    • +
    • save() : requ�te client/serveur pour sauvegarder les donn�es du mod�le (insertion + ou mise � jour) ;
    • +
    • deleteById() : requ�te client/serveur pour supprimer un mod�le en fonction de son + identifiant ;
    • +
    • deleteAll() : requ�te client/serveur pour supprimer tous les �l�ments de la table + mapp�e au mod�le ;
    • +
    • deleteByQuery() : requ�te client/serveur pour supprimer tous les �l�ments en + fonction d'une requ�te SQL ;
    • +
    • destroyById() : requ�te client/serveur pour supprimer un mod�le en fonction de son + identifiant (avec prise en compte de la suppression logique) ; +
    • +
    • destroyAll() : requ�te client/serveur pour supprimer tous les �l�ments de la table + mapp�e au mod�le (avec prise en compte de la suppression + logique) ;
    • +
    • destroyByQuery() : requ�te client/serveur pour supprimer tous les �l�ments en + fonction d'une requ�te SQL (avec prise en compte de la suppression + logique) ;
    • +
    • executeQuery() : requ�te client/serveur pour ex�cuter une + requ�te SQL personnalis�e ou proc�dure stock�e ;
    • +
    • exist() : requ�te client/serveur pour tester l'existence du mod�le en fonction de + son identifiant ;
    • +
    • isValid() : requ�te client/serveur pour tester la validit� du mod�le (module QxValidator).
    • +
    +
    + Remarque : l'application QxEntityEditor est livr�e avec les plugins + QxEECppServicesExport et QxEECppModelViewExport : ces plugins g�n�rent + automatiquement tout le code source n�cessaire pour travailler avec des mod�les qui utilisent le + module QxService. + Afin de travailler avec la classe qx::QxModelService<T, S>, il est donc fortement recommand� + d'utiliser l'application QxEntityEditor afin de g�n�rer le code source automatiquement. +

    +
    +
    + +

    QxOrm et MongoDB (C++ ODM + Object Document Mapper)

    +
    + En plus des bases de donn�es relationnelles classiques (MySQL, PostgreSQL, SQLite, Oracle, + Microsoft SQLServer, MariaDB, etc...), la biblioth�que QxOrm supporte �galement la base de donn�es MongoDB. +

    + D�finition du site Wikipedia : + MongoDB est un syst�me de gestion de base de donn�es orient�e documents, r�partissable sur un + nombre quelconque d'ordinateurs et ne n�cessitant pas de sch�ma pr�d�fini des donn�es. + Il fait partie de la mouvance NoSQL. +

    + La base de donn�es MongoDB pr�sente de nombreux avantages par rapport � une base de donn�es + relationnelle classique (liste non exhaustive) : +
      +
    • Aucun sch�ma � d�finir : il est inutile d'avoir � maintenir des tables et colonnes + (donc fini les scripts complexes pour migrer la base de donn�es d'une version � une autre). + Les Collections MongoDB peuvent contenir des Documents avec diff�rents champs, + diff�rentes tailles, etc... Concernant QxOrm, �a signifie que vous pouvez faire �voluer vos + classes C++ sans vous soucier d'un sch�ma DDL � maintenir d'une version � une autre (convient + parfaitement dans un environnement de d�veloppement AGILE par exemple) ;
    • +
    • Les donn�es sont stock�es dans un format BSON (correspond � du JSON) : facile � lire + m�me avec des structures de donn�es complexes ;
    • +
    • Moteur de requ�tes (JSON) tr�s puissant avec possibilit� de positionner des index sur + n'importe qu'elle propri�t� d'un Document ;
    • +
    • La base de donn�es est gratuite, et propose un support pour les professionnels + ;
    • +
    • Depuis la version 3.6 de MongoDB : le moteur de requ�tes permet de cr�er des + jointures (entre Documents) de mani�re �quivalente � une base de donn�es + relationnelle classique. +
    • +
    +
    + L'utilisation de la biblioth�que QxOrm avec MongoDB est tr�s proche des bases de donn�es + relationnelles classiques. + Toutes les fonctionnalit�s de la biblioth�que QxOrm sont support�es avec MongoDB : donc tout ce qui + se trouve dans ce manuel utilisateur est disponible avec une base de donn�es MongoDB. + Les principales diff�rences � prendre en compte sont : + +
    + Remarque : le package QxOrm fournit un projet de test nomm� qxBlogMongoDB (dans le + dossier ./test/). + Ce projet montre comment se connecter � une base de donn�es MongoDB avec la biblioth�que QxOrm. +

    +

    Pr�-requis : driver + libmongoc et libbson

    +
    + Le module QtSql fourni par + le framework Qt sur lequel est bas� la biblioth�que QxOrm ne propose pas de connecteur � une + base de donn�es MongoDB. + La biblioth�que QxOrm a donc besoin de 2 d�pendances suppl�mentaires pour se connecter � une + base MongoDB : + +
    + Un guide + d'installation est disponible pour installer ces 2 biblioth�ques sur votre environnement + de d�veloppement. +

    +
    +

    Param�trage du fichier + QxOrm.pri (ou QxOrm.cmake)

    +
    + Une fois que les biblioth�ques libmongoc et libbson sont correctement install�es + sur votre environnement de d�veloppement, il est n�cessaire de param�trer le fichier de + configuration QxOrm.pri (ou QxOrm.cmake) en activant l'option de compilation + _QX_ENABLE_MONGODB. +

    + + + + + + + +
    +
    #######################################
    +# MongoDB Driver Library Dependencies #
    +#######################################
    +
    +# If you enable _QX_ENABLE_MONGODB option, then QxOrm library will be able to use mongoc driver to store all QxOrm registered classes in a MongoDB database
    +# When _QX_ENABLE_MONGODB compilation option is defined, you must provide following paths to manage mongoc library dependencies :
    +#  - a BSON_INCLUDE environment variable to define where bson library source code is located (or a QX_BSON_INCLUDE_PATH qmake variable)
    +#  - a MONGOC_INCLUDE environment variable to define where mongoc library source code is located (or a QX_MONGOC_INCLUDE_PATH qmake variable)
    +#  - a BSON_LIB environment variable to define where bson library is located (or a QX_BSON_LIB_PATH qmake variable)
    +#  - a MONGOC_LIB environment variable to define where mongoc library is located (or a QX_MONGOC_LIB_PATH qmake variable)
    +
    +

    + Remarque : une fois l'option de compilation _QX_ENABLE_MONGODB activ�e, il est + possible de compiler et ex�cuter le projet de test qxBlogMongoDB du dossier + ./test/ afin de valider l'environnement de d�veloppement. +

    +
    +

    Connexion � la base de + donn�es MongoDB

    +
    + Voici un exemple de param�trage pour se connecter � une base de donn�es MongoDB : +

    + + + + + + + +
    +
    // Parameters to connect to MongoDB database
    +qx::QxSqlDatabase * pDatabase = qx::QxSqlDatabase::getSingleton();
    +pDatabase->setDriverName("QXMONGODB");
    +pDatabase->setDatabaseName("qxBlog");
    +pDatabase->setHostName("localhost");
    +pDatabase->setPort(27017);
    +pDatabase->setUserName("");
    +pDatabase->setPassword("");
    +
    +

    +
    +

    D�finition d'une classe + persistante MongoDB (Collection) dans le contexte QxOrm (mapping)

    +
    + D�clarer une classe persistante MongoDB dans le contexte QxOrm est �quivalent � d�clarer une classe persistante pour une base de donn�es relationnelle + classique. + Voici un exemple du projet de test qxBlogMongoDB : +

    + Fichier blog.h : + + + + + + + +
    +
    #ifndef _QX_BLOG_BLOG_H_
    +#define _QX_BLOG_BLOG_H_
    +
    +#include "author.h"
    +#include "comment.h"
    +#include "category.h"
    +
    +class QX_BLOG_DLL_EXPORT blog
    +{
    +public:
    +// -- properties
    +   QString        m_id;
    +   QString        m_text;
    +   QDateTime      m_dt_creation;
    +   author_ptr     m_author;
    +   list_comment   m_commentX;
    +   list_category  m_categoryX;
    +// -- contructor, virtual destructor
    +   blog() { ; }
    +   virtual ~blog() { ; }
    +};
    +
    +QX_REGISTER_PRIMARY_KEY(blog, QString)
    +QX_REGISTER_HPP_QX_BLOG(blog, qx::trait::no_base_class_defined, 0)
    +
    +typedef std::shared_ptr<blog> blog_ptr;
    +typedef std::vector<blog_ptr> list_blog;
    +
    +#endif // _QX_BLOG_BLOG_H_
    +
    +
    + Fichier blog.cpp : + + + + + + + +
    +
    #include "../include/precompiled.h"
    +#include "../include/blog.h"
    +#include <QxOrm_Impl.h>
    +
    +QX_REGISTER_CPP_QX_BLOG(blog)
    +
    +namespace qx {
    +template <> void register_class(QxClass<blog> & t)
    +{
    +   t.id(& blog::m_id, "blog_id");
    +
    +   t.data(& blog::m_text, "blog_text");
    +   t.data(& blog::m_dt_creation, "date_creation");
    +   t.data(& blog::m_categoryX, "list_category"); // Embedded relationship
    +
    +   t.relationManyToOne(& blog::m_author, "author_id"); // Referenced relationship
    +   t.relationOneToMany(& blog::m_commentX, "list_comment", "blog_id"); // Referenced relationship
    +}}
    +
    +
    + Remarque : l'exemple ci-dessus montre comment d�finir : + +
    +

    Gestion des cl�s + primaires ObjectId

    +
    + Comme indiqu� en jaune dans l'exemple pr�c�dent, il est conseill� de d�finir en C++ une cl� + primaire de type QString. + Il n'y a pas de notion de cl� num�rique auto-incr�ment�e : MongoDB + utilise un type ObjectId qui peut �tre mapp� en C++ avec QString et g�n�r� + automatiquement (il est �galement possible de d�finir son propre type C++ pour mapper un + ObjectId MongoDB). +

    +
    +
    +

    Ins�rer une instance C++ + (Document) dans la base de donn�es MongoDB (INSERT)

    +
    + Voici un exemple d'insertion de document avec g�n�ration automatique de la cl� primaire (de type + MongoDB + ObjectId) : +

    + + + + + + + +
    +
    // Insert one author without id
    +author_ptr author_1 = std::make_shared<author>();
    +author_1->m_name = "author_1";
    +author_1->m_sex = author::male;
    +author_1->m_birthdate = QDate(1998, 07, 12);
    +daoError = qx::dao::insert(author_1);
    +
    +

    + Voici un exemple d'insertion de document avec une cl� primaire personnalis�e : +

    + + + + + + + +
    +
    // Insert one author with a custom id
    +author_ptr author_2 = std::make_shared<author>();
    +author_2->m_id = "my_custom_id_author_2";
    +author_2->m_name = "author_2";
    +author_2->m_sex = author::female;
    +author_2->m_birthdate = QDate(2003, 02, 28);
    +daoError = qx::dao::insert(author_2);
    +
    +

    +

    Ins�rer une liste + d'instances C++ (plusieurs Documents) dans la base de donn�es MongoDB (INSERT)

    +
    + Voici un exemple d'insertion de plusieurs documents dans la base de donn�es MongoDB : +

    + + + + + + + +
    +
    // Insert many authors with/without ids
    +QList<author> authorX;
    +author author_3; author_3.m_name = "author_3"; author_3.m_sex = author::female; author_3.m_birthdate = QDate(1968, 05, 01);
    +author author_4; author_4.m_id = "my_custom_id_author_4"; author_4.m_name = "author_4"; author_4.m_sex = author::male;
    +author author_5; author_5.m_name = "author_5"; author_5.m_sex = author::female; author_5.m_birthdate = QDate(1978, 03, 03);
    +authorX.append(author_3); authorX.append(author_4); authorX.append(author_5);
    +daoError = qx::dao::insert(authorX);
    +
    +

    + Remarque : QxOrm supporte plusieurs types C++ de listes / + collections. +

    +
    +
    +

    Mettre � jour une + instance C++ (Document) dans la base de donn�es MongoDB (UPDATE)

    +
    + Voici un exemple de mise � jour d'un document dans la base MongoDB : +

    + + + + + + + +
    +
    // Update one author
    +author author_4;
    +author_4.m_id = "my_custom_id_author_4";
    +author_4.m_name = "author_4_modified";
    +daoError = qx::dao::update(author_4);
    +
    +

    +

    Mettre � jour une + liste d'instances C++ (plusieurs Documents) dans la base de donn�es MongoDB (UPDATE) +

    +
    + Voici un exemple de mise � jour de plusieurs documents dans la base MongoDB : +

    + + + + + + + +
    +
    // Update many authors
    +QList<author> authorX;
    +author_3.m_name = "author_3_modified_twice"; authorX.append(author_3);
    +author_2->m_name = "author_2_modified"; authorX.append(* author_2);
    +author_1->m_name = "author_1_modified"; authorX.append(* author_1);
    +daoError = qx::dao::update(authorX);
    +
    +

    + Remarque : QxOrm supporte plusieurs types C++ de listes / + collections. +

    +
    +
    +

    Supprimer une instance + C++ (Document) de la base de donn�es MongoDB (DELETE)

    +
    + Voici un exemple de suppression d'un document de la base MongoDB : +

    + + + + + + + +
    +
    // Delete one author by id
    +author_ptr pAuthor = std::make_shared<author>();
    +pAuthor->m_id = "my_custom_id_author_4";
    +daoError = qx::dao::delete_by_id(pAuthor);
    +
    +

    +

    Supprimer une liste + d'instances C++ (plusieurs Documents) de la base de donn�es MongoDB (DELETE)

    +
    + Voici un exemple de suppression de plusieurs documents de la base MongoDB par identifiant + (cl� primaire) : +

    + + + + + + + +
    +
    // Delete many authors by id
    +QList<author> authorX;
    +author_3.m_id = "id_author_3"; authorX.append(author_3);
    +author_2->m_id = "id_author_2"; authorX.append(* author_2);
    +author_1->m_id = "id_author_1"; authorX.append(* author_1);
    +daoError = qx::dao::delete_by_id(authorX);
    +
    +

    + Voici un exemple de suppression de plusieurs documents de la base MongoDB par requ�te JSON : +

    + + + + + + + +
    +
    // Delete authors by query (all male)
    +qx_query query{ { "sex", author::male } };
    +daoError = qx::dao::delete_by_query<author>(query);
    +
    +

    + Pour supprimer tous les documents de la Collection author : +

    + + + + + + + +
    +
    // Delete all authors
    +daoError = qx::dao::delete_all<author>();
    +
    +

    + Remarque : QxOrm supporte plusieurs types C++ de listes / + collections. +

    +
    +
    +

    R�cup�rer une instance + C++ (Document) de la base de donn�es MongoDB (FETCH)

    +
    + Voici un exemple pour r�cup�rer (FETCH) un document de la base MongoDB par identifiant (cl� + primaire) : +

    + + + + + + + +
    +
    // Fetch one author by id
    +author_ptr pAuthor = std::make_shared<author>();
    +pAuthor->m_id = "my_custom_id_author_2";
    +daoError = qx::dao::fetch_by_id(pAuthor);
    +
    +

    +

    R�cup�rer une liste + d'instances C++ (plusieurs Documents) de la base de donn�es MongoDB (FETCH)

    +
    + Voici un exemple pour r�cup�rer (FETCH) plusieurs documents de la base MongoDB par + identifiant (cl� primaire) : +

    + + + + + + + +
    +
    // Fetch many authors by id
    +QList<author> authorX;
    +author_3.m_id = "id_author_3"; authorX.append(author_3);
    +author_2->m_id = "id_author_2"; authorX.append(* author_2);
    +author_1->m_id = "id_author_1"; authorX.append(* author_1);
    +daoError = qx::dao::fetch_by_id(authorX);
    +
    +

    + Voici un exemple pour r�cup�rer (FETCH) plusieurs documents de la base MongoDB par requ�te JSON : +

    + + + + + + + +
    +
    // Fetch many authors by query (only female)
    +list_author list_of_female_author;
    +qx_query query{ { "sex", author::female } };
    +daoError = qx::dao::fetch_by_query(query, list_of_female_author);
    +
    +

    + Voici un exemple pour r�cup�rer (FETCH) tous les documents de la Collection author de + la base MongoDB : +

    + + + + + + + +
    +
    // Fetch all authors
    +list_author allAuthors;
    +daoError = qx::dao::fetch_all(allAuthors);
    +
    +

    + Voici un exemple pour r�cup�rer (FETCH) tous les documents de la Collection author de + la base MongoDB (en s�lectionnant les propri�t�s/colonnes � r�cup�rer) : +

    + + + + + + + +
    +
    // Fetch all authors (with only 'date_creation' and 'name' properties)
    +list_author allAuthors;
    +QStringList columns = QStringList() << "date_creation" << "name";
    +daoError = qx::dao::fetch_all(allAuthors, NULL, columns);
    +
    +

    + Remarque : QxOrm supporte plusieurs types C++ de listes / + collections. +

    +
    +
    +

    Requ�tes JSON

    +
    + La principale diff�rence entre une base de donn�es relationnelle classique et MongoDB est la + fa�on de requ�ter les donn�es : � la place du SQL, MongoDB propose un moteur de + requ�te JSON. +

    +

    Utilisation de la + classe qx::QxSqlQuery (ou son alias qx_query)

    +
    + La classe qx::QxSqlQuery (ou son alias qx_query) utilis�e pour + construire du SQL classique est �galement compatible pour construire des requ�tes JSON + MongoDB. + Cette classe utilise la fonctionnalit� C++11 std::initializer_list afin d'�crire les + requ�tes en C++ avec une syntaxe proche du JSON (il est �galement possible d'�crire la + requ�te sous forme de cha�ne de caract�res). + Par exemple : +

    + + + + + + + +
    +
    // Fetch many authors by query (only female)
    +list_author list_of_female_author;
    +qx_query query { { "sex", author::female } };
    +daoError = qx::dao::fetch_by_query(query, list_of_female_author);
    +
    +

    +
    +

    Utiliser le moteur + d'aggregation MongoDB

    +
    + La base de donn�es MongoDB fournit �galement un puissant moteur + d'aggregation qui �tend encore plus les possibilit�s pour requ�ter les donn�es. + Voici comment utiliser ce moteur d'aggregation avec la classe qx::QxSqlQuery (ou son alias qx_query), le 1er param�tre du + constructeur doit �tre �gal � aggregate : +

    + + + + + + + +
    +
    // Fetch by query using MongoDB aggregation framework (only female)
    +list_author list_of_female_author;
    +qx_query queryAggregate("aggregate",
    +               "[ { \"$match\" : { \"sex\" : " + QString::number(static_cast<int>(author::female)) + " } } ]");
    +daoError = qx::dao::fetch_by_query(queryAggregate, list_of_female_author);
    +
    +

    +
    +

    Ajouter des + propri�t�s � la requ�te de type : 'sort', 'limit', 'skip', etc...

    +
    + Il est souvent n�cessaire de limiter les donn�es, ou bien de les trier par exemple. + Pour effectuer ces op�rations, la base de donn�es MongoDB utilise la notion de projection. + Voici un exemple d'utilisation avec la classe qx::QxSqlQuery (ou son + alias qx_query), avec une QStringList en constructeur (ou bien 2�me param�tre std::initializer_list) : +

    + + + + + + + +
    +
    // Fetch by query (only female) adding 'sort', 'limit', 'skip', etc... commands (see second query QStringList parameter)
    +list_of_female_author.clear();
    +qx_query queryOpts(QStringList() << "{ \"sex\" : " + QString::number(static_cast(author::female)) + " }"
    +                              << "{ \"sort\" : { \"sex\" : -1 }, \"limit\" : 2 }");
    +daoError = qx::dao::fetch_by_query(queryOpts, list_of_female_author);
    +
    +

    +
    +

    Ex�cuter une requ�te + personnalis�e

    +
    + Il est possible d'ex�cuter une requ�te personnalis�e avec la fonction + qx::dao::call_query(). + Le r�sultat de la requ�te peut �tre facilement convertie en QVariantMap ou bien + QList<QVariantMap> (si la requ�te retourne un curseur) afin de faciliter la + lecture des r�sultats de la requ�te personnalis�e. + Voici quelques exemples d'appels de requ�tes personnalis�es : +

    + + + + + + + +
    +
    // Drop database
    +qx_query dropDB("{ \"dropDatabase\" : 1 }");
    +QSqlError daoError = qx::dao::call_query(dropDB);
    +
    +

    + + + + + + + +
    +
    // Call a custom query and get JSON response as QVariantMap
    +qx_query customQuery("{ \"find\": \"author\", \"filter\": { } }");
    +daoError = qx::dao::call_query(customQuery); qAssert(! daoError.isValid());
    +QString responseCustomQuery = customQuery.response().toString();
    +QVariantMap responseCustomQueryAsJson;
    +qx::serialization::json::from_string(responseCustomQueryAsJson, responseCustomQuery);
    +
    +

    + + + + + + + +
    +
    // Call a custom query with cursor and get JSON response as QList<QVariantMap>
    +qx_query customQueryCursor("cursor", "{ \"find\": \"author\", \"filter\": { } }");
    +daoError = qx::dao::call_query(customQueryCursor); qAssert(! daoError.isValid());
    +QString responseCustomQueryCursor = customQueryCursor.response().toString();
    +QList<QVariantMap> responseCustomQueryCursorAsJson;
    +qx::serialization::json::from_string(responseCustomQueryCursorAsJson, responseCustomQueryCursor);
    +
    +

    +
    +
    +

    Moteur de relations + (n�cessite une version MongoDB 3.6 ou +)

    +
    + Le moteur de relations de la biblioth�que QxOrm est compatible avec la + base de donn�es MongoDB (version 3.6 minimale). + QxOrm est donc capable de r�cup�rer les donn�es d'un Document sur plusieurs Collections en une + seule requ�te. +

    + Voici un exemple pour r�cup�rer un Document et toutes ses relations sur 1 niveau de profondeur + (parent > enfants) : +

    + + + + + + + +
    +
    // Fetch blog with all relations : 'author', 'comment' and 'category' (MongoDB version 3.6+ is required for relationships)
    +blog_ptr blog = std::make_shared<blog>();
    +blog->m_id = "id_blog_1";
    +daoError = qx::dao::fetch_by_id_with_all_relation(blog);
    +
    +

    + Voici un exemple pour r�cup�rer un Document et toutes ses relations sur 4 niveaux de profondeur + (utilisation de la syntaxe *->*->*->*) : +

    + + + + + + + +
    +
    // Fetch blog with many relations using "*->*->*->*" (4 levels of relationships)
    +blog_ptr blog = std::make_shared<blog>();
    +blog->m_id = "id_blog_1";
    +daoError = qx::dao::fetch_by_id_with_relation("*->*->*->*", blog);
    +
    +

    + Voici un exemple pour r�cup�rer un Document en s�lectionnant les relations et propri�t�s � + alimenter (utilisation de la syntaxe { <col_1>, <col_2>, etc... }) : +

    + + + + + + + +
    +
    // Fetch relations defining fields to fetch with syntax { col_1, col_2, etc... }
    +list_blog lstBlogComplexRelation;
    +QStringList relations = QStringList() << "{ blog_text }" << "author_id { name, birthdate }" << "list_comment { comment_text } -> blog_id -> *";
    +daoError = qx::dao::fetch_all_with_relation(relations, lstBlogComplexRelation);
    +
    +

    + Voici un exemple pour r�cup�rer un Document en s�lectionnant les relations et les propri�t�s � + ne pas alimenter (utilisation de la syntaxe -{ <col_1>, <col_2>, etc... }) : +

    + + + + + + + +
    +
    // Fetch relations defining columns to remove before fetching with syntax -{ col_1, col_2, etc... }
    +list_blog lstBlogComplexRelation2;
    +QStringList relations = QStringList() << "-{ blog_text }" << "author_id -{ name, birthdate }" << "list_comment -{ comment_text } -> blog_id -> *";
    +daoError = qx::dao::fetch_all_with_relation(relations, lstBlogComplexRelation2);
    +
    +

    +

    Relations : Embedded + vs Referenced

    +
    + Un des points forts de la base de donn�es MongoDB est de pouvoir stocker des structures + complexes de donn�es (on n'est pas limit� � une structure en tableau table/colonne des bases + de donn�es relationnelles classiques). + Un Document MongoDB peut donc contenir un objet et plusieurs sous-objets (notion de + hi�rarchie dans la structure du Document). + Inclure un sous-objet dans un m�me Document pr�sente des avantages (aucune jointure par + exemple, donc plus rapide � r�cup�rer) et inconv�nients (un m�me objet peut �tre dupliqu� + plusieurs fois dans la base). + Il est donc important de r�fl�chir sur la strat�gie � adopter pour stocker les donn�es. +

    + La biblioth�que QxOrm supporte les 2 fa�ons de proc�der : +
      +
    • Embedded relation : le sous-objet est inclu dans le Document ;
    • +
    • Referenced relation : cr�� une jointure comme dans une base de donn�es + relationnelle classique.
    • +
    +
    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<blog> & t)
    +{
    +   t.id(& blog::m_id, "blog_id");
    +
    +   t.data(& blog::m_text, "blog_text");
    +   t.data(& blog::m_dt_creation, "date_creation");
    +   t.data(& blog::m_categoryX, "list_category"); // Embedded relationship
    +
    +   t.relationManyToOne(& blog::m_author, "author_id"); // Referenced relationship
    +   t.relationOneToMany(& blog::m_commentX, "list_comment", "blog_id"); // Referenced relationship
    +}}
    +
    +

    +
    +
    +

    Cr�ation automatique des + index

    +
    + La biblioth�que QxOrm fournit une m�thode pour g�n�rer automatiquement les index (cette fonction + peut �tre appel�e en d�but de programme, par exemple dans le main) : +
      +
    • les index li�s aux relations entre les Collections (pour optimiser les jointures) ;
    • +
    • les index d�finis par la m�thode qx::IxDataMember::setIndex() (dans la fonction + qx::register_class()). +
    • +
    +
    + + + + + + + +
    +
    // To optimize queries : create automatically indexes based on relationships and properties marked as 'index'
    +daoError = qx::dao::mongodb::QxMongoDB_Helper::autoCreateIndexes(true);
    +
    +

    +
    +
    + +

    Serveur web HTTP/HTTPS + (module QxHttpServer)

    +
    + La biblioth�que QxOrm fournit un serveur web compatible HTTP 1.1 autonome (aucune n�cessit� + d'installer une application tierce comme Apache ou Nginx), + performant (multi-thread) et simple d'utilisation : il s'agit du module QxHttpServer (bas� + sur le module QxService). +

    + Le module QxHttpServer supporte de nombreuses fonctionnalit�s : + + Combin� avec le module QxRestApi (qui propose une API JSON pour requ�ter + les donn�es persistantes), le module QxHttpServer est particuli�rement adapt� pour + d�velopper des applications web modernes. + Par exemple, des applications web de type SPA (Single-Page + Applications) avec les c�l�bres frameworks Javascript comme AngularJS, React, Meteor.js, + etc... +

    + Remarque : pour activer le module QxHttpServer, il faut d�finir l'option de + compilation _QX_ENABLE_QT_NETWORK dans le fichier de configuration QxOrm.pri (ou QxOrm.cmake). + Cette option de compilation ajoute une d�pendance au binaire QtNetwork fourni avec la + biblioth�que Qt. +

    + Autre remarque : le package QxOrm est livr� avec le projet de test + qxBlogRestApi. + Ce projet de test est une application web avec de nombreux exemples pour requ�ter les donn�es + persistantes depuis une page web (HTML et Javascript). +

    +

    Hello World !

    +
    + Voici le code source d'une application web bas�e sur le module QxHttpServer (cette + application renvoie Hello World ! au navigateur web client) : +

    + + + + + + + +
    +
    #include <QtCore/qcoreapplication.h>
    +#include <QxOrm.h>
    +
    +int main(int argc, char * argv[])
    +{
    +   QCoreApplication app(argc, argv);
    +
    +   // HTTP server settings
    +   qx::service::QxConnect * serverSettings = qx::service::QxConnect::getSingleton();
    +   serverSettings->setPort(9642); // HTTP server listening port
    +   serverSettings->setKeepAlive(5000); // Keep-alive connection with client during 5s, then socket is disconnected and thread becomes available for other clients
    +   serverSettings->setThreadCount(50); // Number of threads waiting for client's requests,
    +                                                           // which means also how many requests can be handled simultaneously (in parallel) by HTTP server
    +
    +   // Create a QxOrm HTTP server instance
    +   qx::QxHttpServer httpServer;
    +
    +   // Define all HTTP server routes (dispatcher) to handle requests
    +   // Each callback is executed in a dedicated thread, so QxOrm HTTP server can handle several requests in parallel
    +   httpServer.dispatch("GET", "/", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
    +      response.data() = "Hello World !";
    +   });
    +
    +   // Start HTTP server
    +   httpServer.startServer();
    +
    +   // Start event loop
    +   return app.exec();
    +}
    +
    +

    + R�sultat : ouvrir un navigateur web (Chrome, Firefox, Safari, Internet Explorer, Opera, + etc...) et aller � l'adresse http://localhost:9642/, l'�cran suivant doit + apparaitre : +

    + QxHttpServer Hello World ! +

    +
    +

    Param�trage du serveur + web HTTP

    +
    + Les param�tres du serveur web HTTP sont accessibles avec la classe singleton qx::service::QxConnect : +
      +
    • setPort() : port d'�coute du serveur web (un serveur web classique �coute sur le + port 80 mais vous pouvez d�finir une autre valeur) ;
    • +
    • setThreadCount() : nombre de threads utilis�s par le serveur web pour traiter les + requ�tes HTTP (nombre de connexions clientes simultan�es) ;
    • +
    • setMaxWait() : temps d'attente maximum en milli-secondes (par exemple lecture ou + �criture sur la socket), la valeur -1 signifie attendre ind�finiment ;
    • +
    • setCompressData() : si le client HTTP supporte la compression GZIP, alors les + r�ponses de type texte (fichier HTML / Javascript / CSS, flux JSON, etc...) seront + compress�s au format GZIP ;
    • +
    • setKeepAlive() : la socket avec le client reste connect�e pendant le laps de temps + d�fini par cette fonction (en milli-secondes), la valeur -1 signifie ne jamais se + d�connecter ;
    • +
    • setSessionTimeOut() : indique le temps d'attente (en milli-secondes) avant de + d�truire une session (stockage par client c�t� serveur) non + utilis�e.
    • +
    +
    +

    Connexions s�curis�es + SSL/TLS

    +
    + La classe singleton qx::service::QxConnect fournit �galement des param�tres pour g�rer les + connexions s�curis�es HTTPS (SSL et/ou TLS).
    + Voici un exemple de param�trage de connexions s�curis�es avec certificat serveur et autorit� + de certification (voir le projet de test + qxBlogRestApi pour tester ce code) : +

    + + + + + + + +
    +
    // Certificates created with this tutorial : https://deliciousbrains.com/ssl-certificate-authority-for-local-https-development/
    +QFile::copy(":/documents/cert_qxorm_ca.pem", appPath.filePath("files/cert_qxorm_ca.pem"));
    +QFile::copy(":/documents/cert_qxorm_server.crt", appPath.filePath("files/cert_qxorm_server.crt"));
    +QFile::copy(":/documents/cert_qxorm_server.key", appPath.filePath("files/cert_qxorm_server.key"));
    +
    +QFile fileCertCA(appPath.filePath("files/cert_qxorm_ca.pem"));
    +fileCertCA.open(QIODevice::ReadOnly);
    +QList<QSslCertificate> certCA; certCA << QSslCertificate(fileCertCA.readAll());
    +
    +QFile fileCertServerPublic(appPath.filePath("files/cert_qxorm_server.crt"));
    +fileCertServerPublic.open(QIODevice::ReadOnly);
    +QSslCertificate certServerPublic(fileCertServerPublic.readAll());
    +
    +QFile fileCertServerPrivate(appPath.filePath("files/cert_qxorm_server.key"));
    +fileCertServerPrivate.open(QIODevice::ReadOnly);
    +QSslKey certServerPrivate(fileCertServerPrivate.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, "qxorm");
    +
    +qx::service::QxConnect * serverSettings = qx::service::QxConnect::getSingleton();
    +serverSettings->setSSLEnabled(true);
    +serverSettings->setSSLCACertificates(certCA);
    +serverSettings->setSSLLocalCertificate(certServerPublic);
    +serverSettings->setSSLPrivateKey(certServerPrivate);
    +
    +
    +

    + Remarque : par d�faut, toutes les erreurs SSL sont ignor�es (erreurs souvent li�es � + des probl�mes de certificats). + Pour adapter votre serveur web � vos normes de s�curit�, vous pouvez utiliser les fonctions + suivantes (classe singleton qx::service::QxConnect) : + +
    +
    +
    +

    Routage des URL (d�finir + les endpoints / dispatcher)

    +
    + Le module QxHttpServer fournit un moteur de routage des URL (dispatcher) pour + d�finir les fonctions (ou lambda) � ex�cuter en fonction des param�tres de la requ�te HTTP + (m�thode HTTP GET, POST, DELETE, etc... + URL demand�e).
    + Les fonctions (ou lambda) doivent avoir cette signature : + void myRequestHandler(qx::QxHttpRequest & request, qx::QxHttpResponse & response); + +

    + La classe qx::QxHttpServer (ou son alias qx_http_server) dispose des m�thodes suivantes : +
      +
    • setCustomRequestHandler() : d�fini une fonction (ou lambda) ex�cut�e si aucune + autre fonction n'a �t� trouv�e par le dispatcher ;
    • +
    • dispatch() : le 1er param�tre correspond � la m�thode HTTP (GET, POST, DELETE, + etc...), le 2�me param�tre correspond � l'URL demand�e (ou son pattern), le 3�me + param�tre correpond � la fonction (ou lambda) � ex�cuter ;
    • +
    • beforeDispatching() : fonction (ou lambda) ex�cut�e avant de traiter la requ�te + HTTP (peut �tre utile par exemple pour tracer des logs, ou bien mettre en place un syst�me + d'authentification) ;
    • +
    • afterDispatching() : fonction (ou lambda) ex�cut�e apr�s le traitement de la + requ�te HTTP (peut �tre utile par exemple pour tracer des logs) ;
    • +
    • clearDispatcher() : n�ttoie toutes les r�gles de routage (seule la fonction ou + lambda d�finie par setCustomRequestHandler() sera ex�cut�e).
    • +
    + Remarque : le dispatcher est thread-safe, vous pouvez donc red�finir les r�gles de + routage des URL m�me si le serveur web est en cours d'ex�cution. +

    + Autre remarque : chaque fonction (ou lambda) est + ex�cut�e dans son propre thread + . + Le serveur web HTTP fourni par la biblioth�que QxOrm peut donc g�rer plusieurs requ�tes HTTP + simultan�ment. +

    + Exemple n�1 : cette r�gle de routage intercepte toutes les requ�tes de type GET + dont l'URL commence par /files/, et renvoie comme r�ponse le contenu d'un fichier + statique stock� sur le serveur (QDir::currentPath() indique le r�pertoire parent du + stockage des fichiers statiques, et 5000 correspond � la taille pour l'envoi des fichiers + par bloc / chunked response, ce dernier param�tre �tant optionnel) : +

    + + + + + + + +
    +
    httpServer.dispatch("GET", "/files/*", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
    +   qx::QxHttpServer::buildResponseStaticFile(request, response, QDir::currentPath(), 5000);
    +});
    +
    +

    + Exemple n�2 : cette r�gle de routage intercepte toutes les requ�tes de type POST + dont l'URL est /qx, et utilise le module QxRestApi (qui propose + une API JSON pour requ�ter les donn�es persistantes). + Ces 2 exemples (avec l'exemple n�1 qui renvoie des fichiers statiques) sont suffisants pour + d�marrer une application web de type SPA (Single-Page Applications) avec les c�l�bres + frameworks Javascript comme AngularJS, React, Meteor.js, etc... +

    + + + + + + + +
    +
    httpServer.dispatch("POST", "/qx", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
    +   qx::QxHttpServer::buildResponseQxRestApi(request, response);
    +});
    +
    +

    + Exemple n�3 : cette r�gle de routage intercepte toutes les requ�tes de type GET + dont l'URL est /test_big_json, et construit une r�ponse de type JSON contenant un tableau + de 10000 �l�ments : +

    + + + + + + + +
    +
    httpServer.dispatch("GET", "/test_big_json", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
    +   // To compare with this benchmark : https://blog.binaryspaceship.com/2017/cpp-rest-api-frameworks-benchmark/
    +   // This is more a JSON benchmark than HTTP server benchmark (RapidJSON is faster than Qt QJson engine)
    +   QJsonArray arr; Q_UNUSED(request);
    +   for (int i = 0; i < 10000; ++i)
    +   {
    +      QJsonObject item;
    +      item.insert("id", QString::number(i));
    +      item.insert("name", QString("Hello World"));
    +      item.insert("type", QString("application"));
    +      arr.append(item);
    +   }
    +   QJsonDocument doc(arr);
    +   response.headers().insert("Content-Type", "application/json; charset=utf-8");
    +   response.data() = doc.toJson(QJsonDocument::Compact);
    +});
    +
    +

    + Remarque : l'ordre dans lequel est appel� les diff�rentes fonctions dispatch() est + important. + Le 1er �l�ment trouv� par le dispatcher correspondant � l'URL demand�e est ex�cut� (et ignore + les �l�ments suivants). + Il est donc indispensable de d�finir en 1er les URL les plus sp�cifiques, jusqu'aux URL les plus + g�n�riques (par exemple, le pattern correspondant � toutes les URL est /*). +

    +

    Routage dynamique des + URL

    +
    + Le dispatcher du module QxHttpServer supporte �galement la notion de routage dynamique + des URL.
    + Il est possible de d�finir des variables au niveau de l'URL � router avec la syntaxe : <var_name:var_type> (var_type + �tant optionnel, et peut prendre comme valeur : int, long, float, double, string). +

    + Le routage dynamique est particuli�rement utile pour mettre en place une API REST.
    + Par exemple, le pattern /blog/<blog_id:int> associ� � la m�thode HTTP GET + peut �tre utilis� pour r�cup�rer les donn�es d'un blog en fonction de son identifiant de type + num�rique (fetch_by_id). +

    + Il faut voir l'URL comme une liste de segments dont le s�parateur est le caract�re + /.
    + Le dispatcher v�rifie que chaque segment de l'URL demand�e correspond au pattern utilis� afin + de valider une fonction (ou lambda) � ex�cuter.
    + Pour r�cup�rer les valeurs des param�tres de l'URL, il faut utiliser : request.dispatchParams().value("var_name") + (retourne un QVariant). +

    + Exemple : la r�gle de routage suivante intercepte toutes les requ�tes de type + GET dont l'URL commence /params/, suivi par un segment qui sera associ� � la + variable var1, suivi par un segment de type num�rique associ� � la variable + var2. + Elle construit une r�ponse qui renvoie la valeur des 2 param�tres var1 et var2. + Si le navigateur web appelle l'URL /params/abc/123/ alors la fonction (ou lambda) sera + ex�cut�e, par contre si le navigateur web appelle l'URL /params/abc/def/ alors la + fonction (ou lambda) ne sera pas ex�cut�e (car def n'est pas num�rique) et cherchera + un autre �l�ment du dispatcher : +

    + + + + + + + +
    +
    httpServer.dispatch("GET", "/params/<var1>/<var2:int>", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
    +   response.data() = "Test URL dispatch parameters :\r\n";
    +   response.data() += " - var1 = " + request.dispatchParams().value("var1").toByteArray() + "\r\n";
    +   response.data() += " - var2 = " + request.dispatchParams().value("var2").toByteArray() + "\r\n";
    +});
    +
    +

    + Remarque : il est �galement possible de d�finir une expression r�guli�re pour router + les URL avec la syntaxe : + <var_name:{my_reg_exp}> + . +

    +
    +
    +

    R�cup�rer les param�tres + de la requ�te HTTP

    +
    + La classe qx::QxHttpRequest (ou son alias qx_http_request) contient tous les param�tres d'appel de la + requ�te HTTP : +
      +
    • QUrl & url() : URL demand�e par le navigateur web client ;
    • +
    • QString & command() : m�thode HTTP utilis�e par le navigateur web client (GET, + POST, PUT, DELETE, etc...) ;
    • +
    • QString & version() : version HTTP fournie par le navigateur web client (en + g�n�ral HTTP/1.1) ;
    • +
    • QByteArray & data() : contenu de la requ�te HTTP ;
    • +
    • QByteArray header(const QByteArray & key) : permet de r�cup�rer la valeur d'un + en-t�te HTTP fourni par le navigateur web client (par exemple : + request.header("Accept-Encoding")) ; +
    • +
    • QxHttpCookie cookie(const QByteArray & name) : permet de r�cup�rer la valeur d'un + cookie HTTP fourni par le navigateur web client ; +
    • +
    • QString param(const QString & key) : permet de r�cup�rer la valeur d'un param�tre + de la requ�te HTTP (fourni soit dans l'URL, soit dans le contenu si l'en-t�te HTTP + 'content-type' est 'application/x-www-form-urlencoded') ; +
    • +
    • QHash<QString, QVariant> & dispatchParams() : liste des param�tres + dynamiques de l'URL calcul�s par le routage/dispatcher ;
    • +
    • QString & sourceAddress() : adresse IP du navigateur web client ;
    • +
    • long & sourcePort() : port utilis� par le navigateur web client ;
    • +
    • QString guid() : identifiant unique de la requ�te HTTP � usage interne uniquement + (peut �tre utilis� pour tracer des logs par exemple).
    • +
    +
    +
    +

    G�n�rer la r�ponse + HTTP

    +
    + La classe qx::QxHttpResponse (ou son alias qx_http_response) permet de g�n�rer la r�ponse HTTP avec les + m�thodes suivantes : +
      +
    • int & status() : code retour de la r�ponse HTTP (par d�faut 200) ;
    • +
    • QByteArray & data() : contenu de la r�ponse HTTP ;
    • +
    • QByteArray header(const QByteArray & key) : renseigne un en-t�te HTTP � envoyer au + navigateur web client (par d�faut, les en-t�tes suivants sont cr��s : Server, + Date, Content-Type et Connection) ; +
    • +
    • QxHttpCookie cookie(const QByteArray & name) : renseigne un cookie HTTP � envoyer au navigateur web client ;
    • +
    • qx_bool writeChunked(const QByteArray & data) : permet d'envoyer le contenu de la r�ponse par bloc (chunked responses).
    • +
    +
    +
    +

    Sessions (stockage par + client c�t� serveur)

    +
    + Les sessions HTTP sont un m�canisme c�t� serveur web permettant de stocker des donn�es + sp�cifiques � un client. + Ces donn�es sont accessibles pendant un laps de temps pour toutes les requ�tes envoy�es par le + client. + Lors du 1er acc�s � une session pour un client, un cookie HTTP + contenant un identifiant unique est g�n�r� et associ� automatiquement � la r�ponse HTTP. + Par la suite, toutes les requ�tes HTTP envoy�es par le client contiendront automatiquement un cookie HTTP avec l'identifiant unique calcul� pr�c�demment. + Lorsqu'une session n'est plus utilis�e pendant un certain laps de temps, alors elle est d�truite + automatiquement. +

    + La classe qx::QxHttpSession (ou son alias qx_http_session) repr�sente une session HTTP c�t� serveur.
    + Une session est accessible avec le singleton qx::QxHttpSessionManager : +

    + + + + + + + +
    +
    httpServer.dispatch("GET", "/", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
    +   // If this is the first time to access to session, then a cookie is created automatically and attached to the response
    +   // Then each request sent by web browser will contain a cookie with the session id
    +   // The session expires on server side after qx::service::QxConnect::setSessionTimeOut() milliseconds
    +   qx::QxHttpSession_ptr session = qx::QxHttpSessionManager::getSession(request, response);
    +   if (session) { session->set("last_request_per_user", QDateTime::currentDateTime()); }
    +});
    +
    +

    + Remarque : la classe qx::QxHttpSession contient une hash-map (QHash<QByteArray, + QVariant>) pouvant stocker n'importe quelle valeur. +

    + Autre remarque : la dur�e (en milli-secondes) pour supprimer une session non utilis�e est + param�tr�e par la m�thode : qx::service::QxConnect::setSessionTimeOut(). +

    +
    +

    Cookies

    +
    + Les cookies HTTP sont un + m�canisme d'�change de donn�es entre client HTTP et serveur HTTP.
    + Les cookies sont utilis�s par exemple pour : +
      +
    • g�rer les sessions HTTP ;
    • +
    • m�moriser l'information sur l'utilisateur d'un site, dans le but de lui montrer un + contenu appropri� dans le futur. Par exemple, un serveur web peut envoyer un cookie + contenant le dernier nom d'utilisateur utilis� pour se connecter � ce site web, afin que + ce nom d'utilisateur puisse �tre pr�-rempli lors des prochaines visites ;
    • +
    • voir Wikipedia pour + d'autres cas d'utilisation.
    • +
    +
    + Les classes qx::QxHttpRequest et qx::QxHttpResponse disposent des m�thodes n�cessaires pour lire les + cookies envoy�s par le navigateur web client ou bien g�n�rer un cookie dans la r�ponse HTTP. + Par exemple : +

    + + + + + + + +
    +
    qx::QxHttpCookie cookie;
    +cookie.name = "my_http_cookie";
    +cookie.value = "my_value";
    +response.cookies().insert(cookie.name, cookie);
    +
    +

    +
    +

    Gestion des fichiers + statiques

    +
    + La classe qx::QxHttpServer (ou son alias qx_http_server) dispose d'une m�thode statique permettant + d'envoyer au navigateur web client des fichiers stock�s sur le serveur (par exemple : fichiers + HTML, Javascript, CSS, images PNG, JPEG, vid�os, etc...). +

    + + + + + + + +
    +
    httpServer.dispatch("GET", "/files/*", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
    +   qx::QxHttpServer::buildResponseStaticFile(request, response, QDir::currentPath(), 5000);
    +});
    +
    +
    +
      +
    • Le 3�me param�tre (dans l'exemple QDir::currentPath()) repr�sente le r�pertoire + parent o� sont stock�s les fichiers statiques sur le serveur web ;
    • +
    • Le 4�me param�tre (dans l'exemple 5000) est optionnel et correspond � la taille + pour l'envoi des fichiers par bloc (chunked response). Ce + param�tre peut �tre utile pour envoyer des fichiers volumineux (streaming).
    • +
    +
    +
    +

    Encodage de transfert en + bloc (chunked responses)

    +
    + D�finition du + site Wikipedia : Chunked transfer encoding (ou Encodage de transfert en bloc) est un + m�canisme de transfert de donn�es de la version 1.1 du protocole Hypertext Transfer Protocol + (HTTP), qui permet � un serveur ou � un client de commencer � transmettre des donn�es par blocs + sans avoir � conna�tre � l'avance la taille totale des donn�es qui seront transmises. + Dans le protocole HTTP, l'en-t�te "Content-Length" peut remplacer la directive "Chunked transfer + encoding" d�crite ici. + La taille en octets de chaque bloc est envoy�e, sous forme de texte en hexadecimal, juste avant + le bloc lui-m�me afin que le serveur puisse dire au client quand il a fini de recevoir les + donn�es de ce bloc. + Le transfert total d'un fichier encod� par blocs se termine par un bloc final au contenu nul. +

    + L'introduction de l'encodage de transfert en bloc du protocole HTTP 1.1 a fourni un certain + nombre d'avantages : +
      +
    • Permettre � un serveur de maintenir une connexion HTTP persistante pour un contenu g�n�r� + dynamiquement.
    • +
    • Permettre � l'exp�diteur d'envoyer des en-t�tes suppl�mentaires apr�s le corps du + message. Sans l'encodage de transfert en bloc, l'exp�diteur devrait tamponner le contenu + jusqu'� ce qu'il soit compl�t� afin de calculer une valeur et l'envoyer avant le contenu. +
    • +
    +
    + La classe qx::QxHttpResponse dispose de la m�thode qx_bool + writeChunked(const QByteArray & data). + Cette m�thode permet d'envoyer la r�ponse par bloc. + Elle est utilis�e par exemple pour envoyer des fichiers statiques + volumineux : +

    + + + + + + + +
    +
    while (! file.atEnd())
    +{
    +   if (! response.writeChunked(file.read(chunkedSize))) { return; }
    +}
    +
    +

    + Remarque : le 1er appel de la m�thode response.writeChunked() d�clenche + automatiquement l'envoi de tous les en-t�tes HTTP de la r�ponse. + Il faut donc d�finir tous les en-t�tes de la r�ponse HTTP avant d'appeler + response.writeChunked() pour la 1�re fois. +

    +
    +

    Requ�tes par les API JSON + (module QxRestApi)

    +
    + Le module QxRestApi propose une API JSON g�n�rique pour requ�ter les + donn�es persistantes (op�rations CRUD, requ�tes complexes avec plusieurs niveaux de relations, + possibilit� de d�finir un format de sortie JSON, appels dynamiques � des fonctions natives C++, + validation d'instances, requ�tes personnalis�es � la base de donn�es). +

    + Ce manuel utilisateur dispose d'un chapitre entier d�di� au module + QxRestApi : il contient notamment de nombreux exemples d'utilisation. + En combinant le module QxRestApi et le module QxHttpServer : + vous avez tous les outils n�cessaires pour d�velopper des applications web modernes. + Par exemple, des applications web de type SPA + (Single-Page Applications) avec les c�l�bres frameworks Javascript comme AngularJS, React, Meteor.js, + etc... +

    + Remarque : le package QxOrm contient un projet de test + qxBlogRestApi. + Ce projet pr�sente la cr�ation d'un serveur web HTTP avec QxOrm, et �galement l'�criture de la + partie cliente en HTML + Javascript (avec utilisation de jQuery). +

    + Par exemple, voici la fonction Javascript utilis�e pour envoyer les requ�tes JSON (m�thode POST) + depuis le client (navigateur web) vers le serveur web HTTP QxOrm (toutes les requ�tes sont + envoy�es � la m�me adresse /qx) : +

    + + + + + + + +
    +
    function sendRequest(request) {
    +   $.post("/qx", request, function(data, status, xhr) {
    +      $("#txtResponse").val(JSON.stringify(data, null, 3));
    +   }, "json").fail(function(error) {
    +      alert("An error occurred sending request to QxOrm HTTP server : " + error);
    +   });
    +}
    +
    +

    + C�t� serveur, la r�ception et le traitement de ces requ�tes est tr�s simple : la classe qx::QxHttpServer (ou son alias qx_http_server) dispose de la m�thode statique + qx::QxHttpServer::buildResponseQxRestApi() : +

    + + + + + + + +
    +
    httpServer.dispatch("POST", "/qx", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) {
    +   qx::QxHttpServer::buildResponseQxRestApi(request, response);
    +});
    +
    +

    + Voici un exemple de requ�te JSON envoy�e par le navigateur web, elle r�cup�re la liste des tous + les blogs de la base de donn�es (fetch_all) : +

    +
    +
    {
    +   "request_id": "2b393e4c-a00c-45dc-a279-e9d76f1c55cf",
    +   "action": "fetch_all",
    +   "entity": "blog"
    +}
    +
    +

    + Voici la r�ponse JSON renvoy�e par le serveur web HTTP contenant la liste de blogs : +

    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 1,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-03-27T20:51:23.107",
    +         "list_category": [],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 2,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-03-27T20:51:23.107",
    +         "list_category": [],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 3,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-03-27T20:51:23.107",
    +         "list_category": [],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 4,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-03-27T20:51:23.107",
    +         "list_category": [],
    +         "list_comment": []
    +      }
    +   ],
    +   "request_id": "2b393e4c-a00c-45dc-a279-e9d76f1c55cf"
    +}
    +
    +

    +
    +

    WebSocket

    +
    + D�finition du site Wikipedia + : le protocole WebSocket vise � d�velopper un canal de communication full-duplex sur un + socket TCP pour les navigateurs et les serveurs web et permet : +
      +
    • la notification au client d'un changement d'�tat du serveur ;
    • +
    • l'envoi de donn�es en mode � pousser � (m�thode Push) du serveur vers le client (sans que + ce dernier ait � effectuer une requ�te).
    • +
    +
    + La biblioth�que QxOrm est bas�e sur le framework Qt qui dispose d�j� d'une impl�mentation + WebSocket.
    + La mise en place d'un serveur web avec les WebSockets Qt est tr�s simple : il y a plusieurs exemples dans + la documentation Qt. +

    + Il est donc tout � fait possible d'impl�menter un serveur web avec : +
      +
    • un port d'�coute d�di� � toutes les connexions HTTP (utilisant le module + QxHttpServer) ; +
    • +
    • un autre port d'�coute d�di� � toutes les connexions WebSockets (utilisant le module QtWebSockets + fourni par Qt).
    • +
    +
    + Remarque : une connexion WebSocket �tant g�n�ralement cr��e par le code Javascript du + navigateur web client, le fait d'avoir 2 ports ouverts sur le serveur web n'est pas un probl�me. +

    +
    +

    Performance (test� avec + Apache Benchmark)

    +
    + Voici les r�sultats d'un test de performance r�alis� avec les param�tres suivants : +
      +
    • Syst�me d'exploitation : Windows 2010 64bits ;
    • +
    • Processeur : Intel Core i7-6820HQ @ 2.70GHz (laptop) ;
    • +
    • Version Qt : 5.1.1 (en mode release) ;
    • +
    • Version QxOrm : 1.4.6 (compil� en mode release avec Visual Studio 2012, avec les + param�tres par d�faut, aucune optimisation particuli�re) ;
    • +
    • Serveur web HTTP : projet de test qxBlogRestApi ;
    • +
    • Outil de test : Apache Benchmark ;
    • +
    • Simulation de 20000 requ�tes avec 50 clients connect�s simultan�ment : ab -n 20000 + -c 50 -k http://localhost:9642/params/abc/123
    • +
    +
    + Le r�sultat indique que le serveur web HTTP QxOrm peut g�rer plus de 12000 requ�tes par seconde : +

    + QxHttpServer performance +

    +

    Am�liorer les + performances avec epoll dispatcher sous Linux

    +
    + Sous Linux, il est possible d'ameliorer significativement les performances du serveur web + HTTP en utilisant le m�canisme + epoll pour g�rer les socket. + Par d�faut, le framework Qt utilise un autre m�canisme (select) plus lent, mais donne + la possibilit� de d�finir une autre gestion d'�v�nements. + Plusieurs biblioth�ques existent, par exemple : + +
    + La classe qx::QxHttpServer (ou son alias qx_http_server) dispose de la m�thode suivante pour d�finir des + �v�nements bas�s sur epoll (� appeler avant le d�marrage du serveur web) : +

    + + + + + + + +
    +
       httpServer.setEventDispatcher(new QEventDispatcherEpoll());   
    +
    +

    +
    +
    +
    + +

    API REST JSON (module + QxRestApi)

    +
    + Le module QxRestApi est une API JSON pour g�rer (de fa�on g�n�rique) la couche de donn�es + persistantes (base de donn�es) ou appeler des fonctions natives C++ + (enregistr�es dans le contexte QxOrm). + Le module QxRestApi est bas� sur un m�canisme requ�te/r�ponse : envoi d'une requ�te au + format JSON et r�ception d'une r�ponse au format JSON. + Le module QxRestApi est particuli�rement adapt� pour d�velopper des services + REST. +

    + Le module QxRestApi supporte les fonctionnalit�s suivantes : +
      +
    • op�rations CRUD ;
    • +
    • requ�tes complexes avec plusieurs niveaux de relations ;
    • +
    • possibilit� de d�finir un format de sortie JSON ;
    • +
    • appels dynamiques � des fonctions natives C++ ;
    • +
    • validation d'instances ;
    • +
    • requ�tes personnalis�es � la base de donn�es ou proc�dures stock�es.
    • +
    +
    +

    Principe de + fonctionnement

    +
    + Le module QxRestApi est tr�s simple d'utilisation : la classe qx::QxRestApi + permet d'utiliser les API JSON avec une seule m�thode : processRequest().
    + Pr�requis : pour pouvoir utiliser le module QxRestApi, les classes enregistr�es + dans le contexte QxOrm doivent impl�menter l'interface qx::IxPersistable. +

    + La structure d'une requ�te JSON est g�n�rique et contient les �l�ments suivants : +

    +
    +
    {
    +   "request_id" : // [optional] unique identifier generated by client to associate response to request (if provided by caller, then the response will contain the same unique identifier)
    +   "action" : // [required] what is the action to execute on the server
    +   "entity" : // [optional or required depending on action] C++ class registered in QxOrm context
    +   "data" : // [optional or required depending on action] data in JSON format needed to execute action
    +   "columns" : // [optional] list of columns to fetch or update (if empty, means all columns)
    +   "relations" : // [optional] list of relationships to fetch or save
    +   "query" : // [optional or required depending on action] query to execute on database
    +   "output_format" : // [optional] output fields for the response (filter), if empty then response will contain all fields
    +   "fct" : // [required only with action 'call_entity_function'] used to call C++ native functions
    +   "save_mode" : // [optional] used only with action 'save' to define insert or update or check both insert/update
    +}
    +
    +

    + La r�ponse JSON contient les �l�ments suivants : +

    +
    +
    {
    +   "request_id" : // unique identifier generated by client's request (if any)
    +   "data" : // contain the response data
    +   "error" : // if an error occured, then contain a code and description of the error
    +}
    +
    +

    +

    Cas d'utilisation +

    +
    + Plusieurs langages de programmation supporte le JSON nativement (Javascript, PHP, Python, + etc...). + Le module QxRestApi permet ainsi une interop�rabilit� entre la biblioth�que QxOrm et + d'autres applications utilisant d'autres technologies (autre que C++/Qt par exemple). +

    + Le module QxRestApi peut �tre utilis� : +
      +
    • D�veloppement d'un serveur web HTTP (avec le module + QxHttpServer par exemple) ;
    • +
    • Publication de web-services REST ;
    • +
    • Acc�s aux donn�es persistantes depuis une application QML (voir le projet de test qxBlogRestApi) ;
    • +
    • Possibilit� de scripter l'application C++ (avec un langage comme Python par exemple). +
    • +
    +
    +
    +
    +

    Projet de test + qxBlogRestApi (QML et serveur web HTTP)

    +
    + Le package QxOrm est livr� avec un projet de test nomm� qxBlogRestApi (dans le dossier + ./test/qxBlogRestApi/).
    + Ce projet de test montre 2 cas d'utilisation du module QxRestApi : +
      +
    • La 1�re fen�tre correspond � une application QML + qui utilise le moteur JS int�gr� � QML pour requ�ter les donn�es persistantes ou + appeler des fonctions natives C++ : +

      QxHttpServer performance


      +
    • +
    • La 2�me fen�tre d�marre un serveur web HTTP + bas� sur le module QxHttpServer, puis ouvre le navigateur web par + d�faut � l'adresse correspondante (HTML + Javascript avec jQuery) : +

      QxHttpServer performance
      +
    • +
    +
    + Ces 2 fen�tres sont d�velopp�es avec 2 technologies diff�rentes (QML versus HTML + Javascript), + mais proposent exactement les m�mes fonctionnalit�s : +
      +
    • En haut � gauche de l'�cran : zone permettant d'�crire la requ�te JSON � envoyer au + module QxRestApi ; +
    • +
    • Juste en dessous de la requ�te JSON : un bouton permettant d'envoyer la requ�te JSON au + module QxRestApi ; +
    • +
    • En bas � gauche de l'�cran : une liste d'exemples de requ�tes JSON pr�tes � �tre + ex�cut�es (un click dans cette liste alimente automatiquement la requ�te JSON � envoyer au + module QxRestApi) ; +
    • +
    • A droite de l'�cran : la r�ponse JSON fournie par le module QxRestApi apr�s + traitement de la requ�te.
    • +
    +
    +
    +

    R�cup�ration de donn�es + (fetch/count/exist)

    +
    + Ce chapitre d�taille les diff�rentes m�thodes pour r�cup�rer les donn�es issues de la base de + donn�es : +
      +
    • R�cup�rer tous les �l�ments d'une table et �ventuellement les + relations associ�es (fetch_all) ;
    • +
    • R�cup�rer les donn�es d'un �l�ment d'une table en fonction de son identifiant unique (fetch_by_id) ;
    • +
    • R�cup�rer les �l�ments d'une table filtr�s par une requ�te (fetch_by_query) ;
    • +
    • Compter les �l�ments d'une table avec ou sans requ�te (count) + ;
    • +
    • Tester l'existence d'un ou plusieurs �l�ments d'une table en fonction de l'identifiant + (exist).
    • +
    +
    +

    fetch_all

    +
    + L'action fetch_all permet de r�cup�rer tous les �l�ments d'une table de la base + de donn�es (et �ventuellement les relations associ�es sur plusieurs + niveaux). +

    + -- Exemple n�1 -- r�cup�rer tous les + blogs (sous forme de liste) : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "5e988bac-c812-4cb1-b0d8-6a2c9dc4478b",
    +   "action": "fetch_all",
    +   "entity": "blog"
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 1,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 2,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 3,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 4,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [],
    +         "list_comment": []
    +      }
    +   ],
    +   "request_id": "5e988bac-c812-4cb1-b0d8-6a2c9dc4478b"
    +}
    +
    +

    + -- Exemple n�2 -- r�cup�rer tous les + blogs (sous forme de collection type hash-map avec cl�/valeur) : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "ad400135-19fd-40e0-8034-201be6a2ff7a",
    +   "action": "fetch_all",
    +   "entity": "blog",
    +   "data": [
    +      {
    +         "key": "",
    +         "value": ""
    +      }
    +   ]
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": [
    +      {
    +         "key": 1,
    +         "value": {
    +            "author_id": {
    +               "author_id": "author_id_2",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 1,
    +            "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +            "date_creation": "2019-04-01T16:18:54",
    +            "list_category": [],
    +            "list_comment": []
    +         }
    +      },
    +      {
    +         "key": 2,
    +         "value": {
    +            "author_id": {
    +               "author_id": "author_id_2",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 2,
    +            "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +            "date_creation": "2019-04-01T16:18:54",
    +            "list_category": [],
    +            "list_comment": []
    +         }
    +      },
    +      {
    +         "key": 3,
    +         "value": {
    +            "author_id": {
    +               "author_id": "author_id_2",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 3,
    +            "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +            "date_creation": "2019-04-01T16:18:54",
    +            "list_category": [],
    +            "list_comment": []
    +         }
    +      },
    +      {
    +         "key": 4,
    +         "value": {
    +            "author_id": {
    +               "author_id": "author_id_2",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 4,
    +            "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +            "date_creation": "2019-04-01T16:18:54",
    +            "list_category": [],
    +            "list_comment": []
    +         }
    +      }
    +   ],
    +   "request_id": "ad400135-19fd-40e0-8034-201be6a2ff7a"
    +}
    +
    +

    + -- Exemple n�3 -- r�cup�rer tous les + blogs et toutes les relations associ�es sur 2 niveaux : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "cf9ea2a8-3e41-438f-9a48-bbc8593d2b99",
    +   "action": "fetch_all",
    +   "entity": "blog",
    +   "relations": [
    +      "*->*"
    +   ]
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "list_blog": [
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 2,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 3,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 4,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               }
    +            ],
    +            "name": "author name modified at index 1 => container is dirty !!!",
    +            "sex": 1
    +         },
    +         "blog_id": 1,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [
    +            {
    +               "key": 1,
    +               "value": {
    +                  "category_id": 1,
    +                  "description": "desc_1",
    +                  "list_blog": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "author_id": {
    +                              "author_id": "author_id_2",
    +                              "birthdate": null,
    +                              "list_blog": [],
    +                              "name": "",
    +                              "sex": 2
    +                           },
    +                           "blog_id": 1,
    +                           "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                           "date_creation": "2019-04-01T16:18:54",
    +                           "list_category": [],
    +                           "list_comment": []
    +                        }
    +                     }
    +                  ],
    +                  "name": "category_1"
    +               }
    +            },
    +            {
    +               "key": 3,
    +               "value": {
    +                  "category_id": 3,
    +                  "description": "desc_3",
    +                  "list_blog": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "author_id": {
    +                              "author_id": "author_id_2",
    +                              "birthdate": null,
    +                              "list_blog": [],
    +                              "name": "",
    +                              "sex": 2
    +                           },
    +                           "blog_id": 1,
    +                           "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                           "date_creation": "2019-04-01T16:18:54",
    +                           "list_category": [],
    +                           "list_comment": []
    +                        }
    +                     }
    +                  ],
    +                  "name": "category_3"
    +               }
    +            }
    +         ],
    +         "list_comment": [
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               "comment_id": 1,
    +               "comment_text": "comment_1 text",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               "comment_id": 3,
    +               "comment_text": "comment_1 text",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               "comment_id": 5,
    +               "comment_text": "comment_1 text",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               "comment_id": 7,
    +               "comment_text": "comment_1 text",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               "comment_id": 2,
    +               "comment_text": "comment_2 text",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               "comment_id": 4,
    +               "comment_text": "comment_2 text",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               "comment_id": 6,
    +               "comment_text": "comment_2 text",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               "comment_id": 8,
    +               "comment_text": "comment_2 text",
    +               "date_creation": "2019-04-01T16:18:54"
    +            }
    +         ]
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "list_blog": [
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 2,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 3,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 4,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               }
    +            ],
    +            "name": "author name modified at index 1 => container is dirty !!!",
    +            "sex": 1
    +         },
    +         "blog_id": 2,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [
    +            {
    +               "key": 4,
    +               "value": {
    +                  "category_id": 4,
    +                  "description": "desc_1",
    +                  "list_blog": [
    +                     {
    +                        "key": 2,
    +                        "value": {
    +                           "author_id": {
    +                              "author_id": "author_id_2",
    +                              "birthdate": null,
    +                              "list_blog": [],
    +                              "name": "",
    +                              "sex": 2
    +                           },
    +                           "blog_id": 2,
    +                           "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                           "date_creation": "2019-04-01T16:18:54",
    +                           "list_category": [],
    +                           "list_comment": []
    +                        }
    +                     }
    +                  ],
    +                  "name": "category_1"
    +               }
    +            },
    +            {
    +               "key": 5,
    +               "value": {
    +                  "category_id": 5,
    +                  "description": "desc_3",
    +                  "list_blog": [
    +                     {
    +                        "key": 2,
    +                        "value": {
    +                           "author_id": {
    +                              "author_id": "author_id_2",
    +                              "birthdate": null,
    +                              "list_blog": [],
    +                              "name": "",
    +                              "sex": 2
    +                           },
    +                           "blog_id": 2,
    +                           "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                           "date_creation": "2019-04-01T16:18:54",
    +                           "list_category": [],
    +                           "list_comment": []
    +                        }
    +                     }
    +                  ],
    +                  "name": "category_3"
    +               }
    +            }
    +         ],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "list_blog": [
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 2,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 3,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 4,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               }
    +            ],
    +            "name": "author name modified at index 1 => container is dirty !!!",
    +            "sex": 1
    +         },
    +         "blog_id": 3,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [
    +            {
    +               "key": 6,
    +               "value": {
    +                  "category_id": 6,
    +                  "description": "desc_1",
    +                  "list_blog": [
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "author_id": {
    +                              "author_id": "author_id_2",
    +                              "birthdate": null,
    +                              "list_blog": [],
    +                              "name": "",
    +                              "sex": 2
    +                           },
    +                           "blog_id": 3,
    +                           "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                           "date_creation": "2019-04-01T16:18:54",
    +                           "list_category": [],
    +                           "list_comment": []
    +                        }
    +                     }
    +                  ],
    +                  "name": "category_1"
    +               }
    +            },
    +            {
    +               "key": 7,
    +               "value": {
    +                  "category_id": 7,
    +                  "description": "desc_3",
    +                  "list_blog": [
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "author_id": {
    +                              "author_id": "author_id_2",
    +                              "birthdate": null,
    +                              "list_blog": [],
    +                              "name": "",
    +                              "sex": 2
    +                           },
    +                           "blog_id": 3,
    +                           "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                           "date_creation": "2019-04-01T16:18:54",
    +                           "list_category": [],
    +                           "list_comment": []
    +                        }
    +                     }
    +                  ],
    +                  "name": "category_3"
    +               }
    +            }
    +         ],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "list_blog": [
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 2,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 3,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               },
    +               {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": null,
    +                     "list_blog": [],
    +                     "name": "",
    +                     "sex": 2
    +                  },
    +                  "blog_id": 4,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [],
    +                  "list_comment": []
    +               }
    +            ],
    +            "name": "author name modified at index 1 => container is dirty !!!",
    +            "sex": 1
    +         },
    +         "blog_id": 4,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [
    +            {
    +               "key": 8,
    +               "value": {
    +                  "category_id": 8,
    +                  "description": "desc_1",
    +                  "list_blog": [
    +                     {
    +                        "key": 4,
    +                        "value": {
    +                           "author_id": {
    +                              "author_id": "author_id_2",
    +                              "birthdate": null,
    +                              "list_blog": [],
    +                              "name": "",
    +                              "sex": 2
    +                           },
    +                           "blog_id": 4,
    +                           "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                           "date_creation": "2019-04-01T16:18:54",
    +                           "list_category": [],
    +                           "list_comment": []
    +                        }
    +                     }
    +                  ],
    +                  "name": "category_1"
    +               }
    +            },
    +            {
    +               "key": 9,
    +               "value": {
    +                  "category_id": 9,
    +                  "description": "desc_3",
    +                  "list_blog": [
    +                     {
    +                        "key": 4,
    +                        "value": {
    +                           "author_id": {
    +                              "author_id": "author_id_2",
    +                              "birthdate": null,
    +                              "list_blog": [],
    +                              "name": "",
    +                              "sex": 2
    +                           },
    +                           "blog_id": 4,
    +                           "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                           "date_creation": "2019-04-01T16:18:54",
    +                           "list_category": [],
    +                           "list_comment": []
    +                        }
    +                     }
    +                  ],
    +                  "name": "category_3"
    +               }
    +            }
    +         ],
    +         "list_comment": []
    +      }
    +   ],
    +   "request_id": "cf9ea2a8-3e41-438f-9a48-bbc8593d2b99"
    +}
    +
    +

    + -- Exemple n�4 -- r�cup�rer tous les + blogs et plusieurs relations associ�es en d�finissant un format de + sortie (toutes les propri�t�s ne feront pas partie de la r�ponse JSON) : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "4c45fdf9-8001-4509-bb4b-ce27a4a8708a",
    +   "action": "fetch_all",
    +   "entity": "blog",
    +   "relations": [
    +      "<blog_alias> { blog_text }",
    +      "author_id <author_alias> { name, birthdate }",
    +      "list_comment <list_comment_alias> { comment_text } -> blog_id <blog_alias_2> -> * <..._my_alias_suffix>"
    +   ],
    +   "output_format": [
    +      "{ blog_text }",
    +      "author_id { name, birthdate }",
    +      "list_comment { comment_text } -> blog_id -> *"
    +   ]
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "name": "author name modified at index 1 => container is dirty !!!"
    +         },
    +         "blog_id": 1,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "list_comment": [
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": "2019-04-01",
    +                     "name": "author name modified at index 1 => container is dirty !!!",
    +                     "sex": 1
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "category_id": 1,
    +                           "description": "desc_1",
    +                           "name": "category_1"
    +                        }
    +                     },
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "category_id": 3,
    +                           "description": "desc_3",
    +                           "name": "category_3"
    +                        }
    +                     }
    +                  ],
    +                  "list_comment": [
    +                     {
    +                        "comment_id": 1,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 3,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 5,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 7,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 2,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 4,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 6,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 8,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     }
    +                  ]
    +               },
    +               "comment_id": 1,
    +               "comment_text": "comment_1 text"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": "2019-04-01",
    +                     "name": "author name modified at index 1 => container is dirty !!!",
    +                     "sex": 1
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "category_id": 1,
    +                           "description": "desc_1",
    +                           "name": "category_1"
    +                        }
    +                     },
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "category_id": 3,
    +                           "description": "desc_3",
    +                           "name": "category_3"
    +                        }
    +                     }
    +                  ],
    +                  "list_comment": [
    +                     {
    +                        "comment_id": 1,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 3,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 5,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 7,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 2,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 4,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 6,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 8,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     }
    +                  ]
    +               },
    +               "comment_id": 3,
    +               "comment_text": "comment_1 text"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": "2019-04-01",
    +                     "name": "author name modified at index 1 => container is dirty !!!",
    +                     "sex": 1
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "category_id": 1,
    +                           "description": "desc_1",
    +                           "name": "category_1"
    +                        }
    +                     },
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "category_id": 3,
    +                           "description": "desc_3",
    +                           "name": "category_3"
    +                        }
    +                     }
    +                  ],
    +                  "list_comment": [
    +                     {
    +                        "comment_id": 1,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 3,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 5,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 7,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 2,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 4,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 6,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 8,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     }
    +                  ]
    +               },
    +               "comment_id": 5,
    +               "comment_text": "comment_1 text"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": "2019-04-01",
    +                     "name": "author name modified at index 1 => container is dirty !!!",
    +                     "sex": 1
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "category_id": 1,
    +                           "description": "desc_1",
    +                           "name": "category_1"
    +                        }
    +                     },
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "category_id": 3,
    +                           "description": "desc_3",
    +                           "name": "category_3"
    +                        }
    +                     }
    +                  ],
    +                  "list_comment": [
    +                     {
    +                        "comment_id": 1,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 3,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 5,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 7,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 2,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 4,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 6,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 8,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     }
    +                  ]
    +               },
    +               "comment_id": 7,
    +               "comment_text": "comment_1 text"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": "2019-04-01",
    +                     "name": "author name modified at index 1 => container is dirty !!!",
    +                     "sex": 1
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "category_id": 1,
    +                           "description": "desc_1",
    +                           "name": "category_1"
    +                        }
    +                     },
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "category_id": 3,
    +                           "description": "desc_3",
    +                           "name": "category_3"
    +                        }
    +                     }
    +                  ],
    +                  "list_comment": [
    +                     {
    +                        "comment_id": 1,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 3,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 5,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 7,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 2,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 4,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 6,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 8,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     }
    +                  ]
    +               },
    +               "comment_id": 2,
    +               "comment_text": "comment_2 text"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": "2019-04-01",
    +                     "name": "author name modified at index 1 => container is dirty !!!",
    +                     "sex": 1
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "category_id": 1,
    +                           "description": "desc_1",
    +                           "name": "category_1"
    +                        }
    +                     },
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "category_id": 3,
    +                           "description": "desc_3",
    +                           "name": "category_3"
    +                        }
    +                     }
    +                  ],
    +                  "list_comment": [
    +                     {
    +                        "comment_id": 1,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 3,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 5,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 7,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 2,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 4,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 6,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 8,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     }
    +                  ]
    +               },
    +               "comment_id": 4,
    +               "comment_text": "comment_2 text"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": "2019-04-01",
    +                     "name": "author name modified at index 1 => container is dirty !!!",
    +                     "sex": 1
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "category_id": 1,
    +                           "description": "desc_1",
    +                           "name": "category_1"
    +                        }
    +                     },
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "category_id": 3,
    +                           "description": "desc_3",
    +                           "name": "category_3"
    +                        }
    +                     }
    +                  ],
    +                  "list_comment": [
    +                     {
    +                        "comment_id": 1,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 3,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 5,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 7,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 2,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 4,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 6,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 8,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     }
    +                  ]
    +               },
    +               "comment_id": 6,
    +               "comment_text": "comment_2 text"
    +            },
    +            {
    +               "blog_id": {
    +                  "author_id": {
    +                     "author_id": "author_id_2",
    +                     "birthdate": "2019-04-01",
    +                     "name": "author name modified at index 1 => container is dirty !!!",
    +                     "sex": 1
    +                  },
    +                  "blog_id": 1,
    +                  "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +                  "date_creation": "2019-04-01T16:18:54",
    +                  "list_category": [
    +                     {
    +                        "key": 1,
    +                        "value": {
    +                           "category_id": 1,
    +                           "description": "desc_1",
    +                           "name": "category_1"
    +                        }
    +                     },
    +                     {
    +                        "key": 3,
    +                        "value": {
    +                           "category_id": 3,
    +                           "description": "desc_3",
    +                           "name": "category_3"
    +                        }
    +                     }
    +                  ],
    +                  "list_comment": [
    +                     {
    +                        "comment_id": 1,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 3,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 5,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 7,
    +                        "comment_text": "comment_1 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 2,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 4,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 6,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     },
    +                     {
    +                        "comment_id": 8,
    +                        "comment_text": "comment_2 text",
    +                        "date_creation": "2019-04-01T16:18:54"
    +                     }
    +                  ]
    +               },
    +               "comment_id": 8,
    +               "comment_text": "comment_2 text"
    +            }
    +         ]
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "name": "author name modified at index 1 => container is dirty !!!"
    +         },
    +         "blog_id": 2,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "name": "author name modified at index 1 => container is dirty !!!"
    +         },
    +         "blog_id": 3,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "name": "author name modified at index 1 => container is dirty !!!"
    +         },
    +         "blog_id": 4,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "list_comment": []
    +      }
    +   ],
    +   "request_id": "4c45fdf9-8001-4509-bb4b-ce27a4a8708a"
    +}
    +
    +

    +
    +

    fetch_by_id

    +
    + L'action fetch_by_id permet de r�cup�rer les donn�es d'un �l�ment d'une table + en fonction de son identifiant unique. +

    + -- Exemple n�1 -- r�cup�rer les donn�es + du blog qui a pour identifiant unique 1 : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "4d6fbb9e-e088-482a-abfa-4e7ddee80569",
    +   "action": "fetch_by_id",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 1
    +   }
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "author_id": {
    +         "author_id": "author_id_2",
    +         "birthdate": null,
    +         "list_blog": [],
    +         "name": "",
    +         "sex": 2
    +      },
    +      "blog_id": 1,
    +      "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +      "date_creation": "2019-04-01T16:18:54",
    +      "list_category": [],
    +      "list_comment": []
    +   },
    +   "request_id": "4d6fbb9e-e088-482a-abfa-4e7ddee80569"
    +}
    +
    +

    + -- Exemple n�2 -- r�cup�re uniquement + quelques donn�es du blog qui a pour identifiant unique 1 (les autres donn�es font partie du + JSON mais avec une valeur vide ou null) : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "72c9b362-d194-410e-98ed-23797a34318e",
    +   "action": "fetch_by_id",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 1
    +   },
    +   "columns": [
    +      "blog_text",
    +      "date_creation"
    +   ]
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "author_id": null,
    +      "blog_id": 1,
    +      "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +      "date_creation": "2019-04-01T16:18:54",
    +      "list_category": [],
    +      "list_comment": []
    +   },
    +   "request_id": "72c9b362-d194-410e-98ed-23797a34318e"
    +}
    +
    +

    + -- Exemple n�3 -- r�cup�re une liste de + blogs en fonction de leur identifiant : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "59c37f70-26ee-42e5-9177-b32c331adce1",
    +   "action": "fetch_by_id",
    +   "entity": "blog",
    +   "data": [
    +      {
    +         "blog_id": 1
    +      },
    +      {
    +         "blog_id": 2
    +      },
    +      {
    +         "blog_id": 3
    +      }
    +   ]
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 1,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 2,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [],
    +         "list_comment": []
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": null,
    +            "list_blog": [],
    +            "name": "",
    +            "sex": 2
    +         },
    +         "blog_id": 3,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "date_creation": "2019-04-01T16:18:54",
    +         "list_category": [],
    +         "list_comment": []
    +      }
    +   ],
    +   "request_id": "59c37f70-26ee-42e5-9177-b32c331adce1"
    +}
    +
    +

    + -- Exemple n�4 -- r�cup�re une liste de + blogs (avec quelques relations associ�es) en fonction de leur identifiant, et d�fini un + format de sortie (toutes les propri�t�s ne feront pas partie de la r�ponse JSON) : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "325d64f4-29ac-47ab-9846-d6a71a9e9d73",
    +   "action": "fetch_by_id",
    +   "entity": "blog",
    +   "data": [
    +      {
    +         "blog_id": 1
    +      },
    +      {
    +         "blog_id": 2
    +      }
    +   ],
    +   "relations": [
    +      "{ blog_text }",
    +      "author_id <author_alias> { name, birthdate }",
    +      "list_comment <list_comment_alias> { comment_text }"
    +   ],
    +   "output_format": [
    +      "{ blog_text }",
    +      "author_id { name, birthdate }",
    +      "list_comment { comment_text }"
    +   ]
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "name": "author name modified at index 1 => container is dirty !!!"
    +         },
    +         "blog_id": 1,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "list_comment": [
    +            {
    +               "comment_id": 1,
    +               "comment_text": "comment_1 text"
    +            },
    +            {
    +               "comment_id": 2,
    +               "comment_text": "comment_2 text"
    +            },
    +            {
    +               "comment_id": 3,
    +               "comment_text": "comment_1 text"
    +            },
    +            {
    +               "comment_id": 4,
    +               "comment_text": "comment_2 text"
    +            },
    +            {
    +               "comment_id": 5,
    +               "comment_text": "comment_1 text"
    +            },
    +            {
    +               "comment_id": 6,
    +               "comment_text": "comment_2 text"
    +            },
    +            {
    +               "comment_id": 7,
    +               "comment_text": "comment_1 text"
    +            },
    +            {
    +               "comment_id": 8,
    +               "comment_text": "comment_2 text"
    +            }
    +         ]
    +      },
    +      {
    +         "author_id": {
    +            "author_id": "author_id_2",
    +            "birthdate": "2019-04-01",
    +            "name": "author name modified at index 1 => container is dirty !!!"
    +         },
    +         "blog_id": 2,
    +         "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +         "list_comment": []
    +      }
    +   ],
    +   "request_id": "325d64f4-29ac-47ab-9846-d6a71a9e9d73"
    +}
    +
    +

    +
    +

    fetch_by_query +

    +
    + L'action fetch_by_query permet de r�cup�rer les �l�ments d'une table filtr�s par une requ�te. +

    + -- Exemple n�1 -- r�cup�re uniquement les + �l�ments de la table author dont le sexe est de type female (female == + enum dont la valeur est 1) : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "c178194c-a76f-4a77-af12-2b97fc7078e4",
    +   "action": "fetch_by_query",
    +   "entity": "author",
    +   "query": {
    +      "sql": "WHERE author.sex = :sex",
    +      "params": [
    +         {
    +            "key": ":sex",
    +            "value": 1
    +         }
    +      ]
    +   }
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": "author_id_2",
    +         "birthdate": "2019-04-01",
    +         "list_blog": [],
    +         "name": "author name modified at index 1 => container is dirty !!!",
    +         "sex": 1
    +      },
    +      {
    +         "author_id": "author_id_3",
    +         "birthdate": "1998-03-06",
    +         "list_blog": [],
    +         "name": "author_3",
    +         "sex": 1
    +      }
    +   ],
    +   "request_id": "c178194c-a76f-4a77-af12-2b97fc7078e4"
    +}
    +
    +

    + -- Exemple n�2 -- r�cup�re uniquement les + �l�ments de la table author (et toutes ses relations + associ�es) dont le sexe est de type female : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "84e2e13a-0bf9-4d78-b655-970568a97e4c",
    +   "action": "fetch_by_query",
    +   "entity": "author",
    +   "query": {
    +      "sql": "WHERE author.sex = :sex",
    +      "params": [
    +         {
    +            "key": ":sex",
    +            "value": 1,
    +            "type": "in"
    +         }
    +      ]
    +   },
    +   "relations": [
    +      "*"
    +   ]
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": "author_id_2",
    +         "birthdate": "2019-04-01",
    +         "list_blog": [
    +            {
    +               "author_id": {
    +                  "author_id": "author_id_2",
    +                  "birthdate": null,
    +                  "list_blog": [],
    +                  "name": "",
    +                  "sex": 2
    +               },
    +               "blog_id": 1,
    +               "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +               "date_creation": "2019-04-01T16:18:54",
    +               "list_category": [],
    +               "list_comment": []
    +            },
    +            {
    +               "author_id": {
    +                  "author_id": "author_id_2",
    +                  "birthdate": null,
    +                  "list_blog": [],
    +                  "name": "",
    +                  "sex": 2
    +               },
    +               "blog_id": 2,
    +               "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +               "date_creation": "2019-04-01T16:18:54",
    +               "list_category": [],
    +               "list_comment": []
    +            },
    +            {
    +               "author_id": {
    +                  "author_id": "author_id_2",
    +                  "birthdate": null,
    +                  "list_blog": [],
    +                  "name": "",
    +                  "sex": 2
    +               },
    +               "blog_id": 3,
    +               "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +               "date_creation": "2019-04-01T16:18:54",
    +               "list_category": [],
    +               "list_comment": []
    +            },
    +            {
    +               "author_id": {
    +                  "author_id": "author_id_2",
    +                  "birthdate": null,
    +                  "list_blog": [],
    +                  "name": "",
    +                  "sex": 2
    +               },
    +               "blog_id": 4,
    +               "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +               "date_creation": "2019-04-01T16:18:54",
    +               "list_category": [],
    +               "list_comment": []
    +            }
    +         ],
    +         "name": "author name modified at index 1 => container is dirty !!!",
    +         "sex": 1
    +      },
    +      {
    +         "author_id": "author_id_3",
    +         "birthdate": "1998-03-06",
    +         "list_blog": [],
    +         "name": "author_3",
    +         "sex": 1
    +      }
    +   ],
    +   "request_id": "84e2e13a-0bf9-4d78-b655-970568a97e4c"
    +}
    +
    +

    + -- Exemple n�3 -- r�cup�re uniquement les + �l�ments de la table author (et toutes ses relations + associ�es) dont le sexe est de type female, et d�fini un format de sortie + (toutes les propri�t�s ne feront pas partie de la r�ponse JSON) : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "c18b59e7-54f9-4a4f-843d-f0797f4fb676",
    +   "action": "fetch_by_query",
    +   "entity": "author",
    +   "query": {
    +      "sql": "WHERE author.sex = :sex",
    +      "params": [
    +         {
    +            "key": ":sex",
    +            "value": 1,
    +            "type": "in"
    +         }
    +      ]
    +   },
    +   "relations": [
    +      "*"
    +   ],
    +   "output_format": [
    +      "{ birthdate, name }",
    +      "list_blog { blog_text, date_creation }"
    +   ]
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": "author_id_2",
    +         "birthdate": "2019-04-01",
    +         "list_blog": [
    +            {
    +               "blog_id": 1,
    +               "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": 2,
    +               "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": 3,
    +               "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +               "date_creation": "2019-04-01T16:18:54"
    +            },
    +            {
    +               "blog_id": 4,
    +               "blog_text": "blog property 'text' modified => blog is dirty !!!",
    +               "date_creation": "2019-04-01T16:18:54"
    +            }
    +         ],
    +         "name": "author name modified at index 1 => container is dirty !!!"
    +      },
    +      {
    +         "author_id": "author_id_3",
    +         "birthdate": "1998-03-06",
    +         "list_blog": [],
    +         "name": "author_3"
    +      }
    +   ],
    +   "request_id": "c18b59e7-54f9-4a4f-843d-f0797f4fb676"
    +}
    +
    +

    +
    +

    count

    +
    + L'action count permet de compter les �l�ments d'une table avec ou sans requ�te + (et avec ou sans relation). +

    + -- Exemple n�1 -- compter le nombre de + blogs dans la base de donn�es : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "1ef62fd7-d847-4d67-9fd0-0207af463aa4",
    +   "action": "count",
    +   "entity": "blog"
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "count": 4
    +   },
    +   "request_id": "1ef62fd7-d847-4d67-9fd0-0207af463aa4"
    +}
    +
    +

    + -- Exemple n�2 -- compter tous les + author dont le sexe est de type female : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "a80646d1-5a42-46fb-9306-3b91c7f594c8",
    +   "action": "count",
    +   "entity": "author",
    +   "query": {
    +      "sql": "WHERE author.sex = :sex",
    +      "params": [
    +         {
    +            "key": ":sex",
    +            "value": 1
    +         }
    +      ]
    +   }
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "count": 2
    +   },
    +   "request_id": "a80646d1-5a42-46fb-9306-3b91c7f594c8"
    +}
    +
    +

    + -- Exemple n�3 -- compter tous les blogs + dont l'author est de type female : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "6ef252f7-385c-465e-8304-b9afa9fea490",
    +   "action": "count",
    +   "entity": "blog",
    +   "query": {
    +      "sql": "WHERE author_alias.sex = :sex",
    +      "params": [
    +         {
    +            "key": ":sex",
    +            "value": 1
    +         }
    +      ]
    +   },
    +   "relations": [
    +      "author_id <author_alias> { sex }"
    +   ]
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "count": 4
    +   },
    +   "request_id": "6ef252f7-385c-465e-8304-b9afa9fea490"
    +}
    +
    +

    +
    +

    exist

    +
    + L'action exist permet de tester l'existence d'un ou plusieurs �l�ments d'une + table en fonction de l'identifiant. +

    + -- Exemple n�1 -- tester l'existence d'un + blog dont l'identifiant unique a pour valeur 1 : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "e8db33db-b249-4349-93fe-ad12e208520e",
    +   "action": "exist",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 1
    +   }
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "exist": true
    +   },
    +   "request_id": "e8db33db-b249-4349-93fe-ad12e208520e"
    +}
    +
    +

    + -- Exemple n�2 -- tester l'existence de + plusieurs blogs : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "f2d6ca3f-36de-4920-8f4c-c04842603467",
    +   "action": "exist",
    +   "entity": "blog",
    +   "data": [
    +      {
    +         "blog_id": 1
    +      },
    +      {
    +         "blog_id": 999
    +      },
    +      {
    +         "blog_id": 3
    +      }
    +   ]
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "exist": false
    +   },
    +   "request_id": "f2d6ca3f-36de-4920-8f4c-c04842603467"
    +}
    +
    +

    + -- Exemple n�3 -- tester l'existence d'un + author : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "2c7df172-8010-4816-b8e1-3edbb0b0b90e",
    +   "action": "exist",
    +   "entity": "author",
    +   "data": {
    +      "author_id": "author_id_2"
    +   }
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "exist": true
    +   },
    +   "request_id": "2c7df172-8010-4816-b8e1-3edbb0b0b90e"
    +}
    +
    +

    +
    +
    +

    Ajout de donn�es + (insert)

    +
    + L'action insert permet d'ins�rer un ou plusieurs �l�ments dans la base de donn�es. + Les identifiants uniques g�n�r�s par la base de donn�es (par exemple identifiant + auto-incr�ment�) sont fournis dans la r�ponse JSON. +

    + -- Exemple n�1 -- ins�rer un blog dans la + base de donn�es : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "573e4940-607a-4037-8a09-11ec52deb21c",
    +   "action": "insert",
    +   "entity": "blog",
    +   "data": {
    +      "blog_text": "this is a new blog from QxOrm REST API !",
    +      "date_creation": "2018-01-30T12:42:01",
    +      "author_id": "author_id_2"
    +   }
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "blog_id": 5
    +   },
    +   "request_id": "573e4940-607a-4037-8a09-11ec52deb21c"
    +}
    +
    +

    + -- Exemple n�2 -- ins�rer une liste de blogs + dans la base de donn�es : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "6ade2d01-086c-45d6-971b-b65e8836475f",
    +   "action": "insert",
    +   "entity": "blog",
    +   "data": [
    +      {
    +         "blog_text": "new blog from QxOrm REST API !",
    +         "date_creation": "2018-01-30T12:42:01",
    +         "author_id": "author_id_2"
    +      },
    +      {
    +         "blog_text": "another blog from QxOrm REST API !",
    +         "date_creation": "2016-06-12T08:33:12",
    +         "author_id": "author_id_1"
    +      }
    +   ]
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": [
    +      {
    +         "blog_id": 6
    +      },
    +      {
    +         "blog_id": 7
    +      }
    +   ],
    +   "request_id": "6ade2d01-086c-45d6-971b-b65e8836475f"
    +}
    +
    +

    + -- Exemple n�3 -- ins�rer un author + dans la base de donn�es : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "0cffa916-99f4-4395-bccd-02918a4b3c57",
    +   "action": "insert",
    +   "entity": "author",
    +   "data": {
    +      "author_id": "author_id_from_rest_api",
    +      "birthdate": "1978-05-11",
    +      "name": "new author created by QxOrm REST API",
    +      "sex": 1
    +   }
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "author_id": "author_id_from_rest_api"
    +   },
    +   "request_id": "0cffa916-99f4-4395-bccd-02918a4b3c57"
    +}
    +
    +
    + Remarque : l'identifiant unique de la table author doit �tre fourni par l'appelant + (non auto-incr�ment�). + Si on rejoue une 2�me fois la m�me requ�te, on obtient l'erreur suivante : +
    +
    +
    {
    +   "error": {
    +      "code": 19,
    +      "desc": "Unable to fetch row\ncolumn author_id is not unique"
    +   },
    +   "request_id": "0cffa916-99f4-4395-bccd-02918a4b3c57"
    +}
    +
    +

    +
    +

    Mise � jour de donn�es + (update)

    +
    + L'action update permet une mise � jour de un ou plusieurs �l�ments dans la base de + donn�es. +

    + -- Exemple n�1 -- mise � jour du blog avec + pour identifiant unique la valeur 1 : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "4fa24a7f-a3d8-4bbf-85c1-c86df83dec0b",
    +   "action": "update",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 1,
    +      "blog_text": "modify blog from QxOrm REST API",
    +      "date_creation": "2013-11-25T09:56:33",
    +      "author_id": "author_id_1"
    +   }
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "blog_id": 1
    +   },
    +   "request_id": "4fa24a7f-a3d8-4bbf-85c1-c86df83dec0b"
    +}
    +
    +

    + -- Exemple n�2 -- mise � jour uniquement de + certaines colonnes d'un blog : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "d0704db1-5c3a-48ad-b27e-14aa54ac0efb",
    +   "action": "update",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 2,
    +      "blog_text": "modify blog from QxOrm REST API",
    +      "date_creation": "2013-11-25T09:56:33"
    +   },
    +   "columns": [
    +      "blog_text",
    +      "date_creation"
    +   ]
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "blog_id": 2
    +   },
    +   "request_id": "d0704db1-5c3a-48ad-b27e-14aa54ac0efb"
    +}
    +
    +

    + -- Exemple n�3 -- mise � jour de plusieurs + author : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "26ec3a7b-cf2d-47f7-bab7-db303f15ee51",
    +   "action": "update",
    +   "entity": "author",
    +   "data": [
    +      {
    +         "author_id": "author_id_from_rest_api",
    +         "birthdate": "1992-11-03",
    +         "name": "modify author from QxOrm REST API",
    +         "sex": 0
    +      },
    +      {
    +         "author_id": "author_id_1",
    +         "birthdate": "1978-12-25",
    +         "name": "modify another author from QxOrm REST API",
    +         "sex": 2
    +      }
    +   ]
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": [
    +      {
    +         "author_id": "author_id_from_rest_api"
    +      },
    +      {
    +         "author_id": "author_id_1"
    +      }
    +   ],
    +   "request_id": "26ec3a7b-cf2d-47f7-bab7-db303f15ee51"
    +}
    +
    +

    +
    +

    Sauvegarde de donn�es + (save)

    +
    + L'action save permet d'ins�rer ou mettre � jour (insert ou update) + un ou plusieurs �l�ments dans la base de donn�es. + En cas d'insertion, les identifiants uniques g�n�r�s par la base de donn�es (par exemple + identifiant auto-incr�ment�) sont fournis dans la r�ponse JSON. +

    + La requ�te JSON dispose d'un param�tre optionnel nomm� save_mode qui peut prendre les + valeurs suivantes : +
      +
    • check_insert_or_update : sauvegarde l'instance et les relations de fa�on r�cursive + (sur plusieurs niveaux) en v�rifiant pour chaque relation s'il faut faire un insert + ou update (m�thode pouvant �tre lente si beaucoup de relations � traiter) ;
    • +
    • insert_only : ins�re de fa�on r�cursive (sur plusieurs niveaux) l'instance et + toutes les relations associ�es ;
    • +
    • update_only : met � jour de fa�on r�cursive (sur plusieurs niveaux) l'instance et + toutes les relations associ�es.
    • +
    +
    + -- Exemple n�1 -- sauvegarde (ins�re ou met + � jour suivant l'identifant unique) un blog dans la base de donn�es : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "ec3c71eb-5014-4b36-85a0-aeb7ae48a5e9",
    +   "action": "save",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 1,
    +      "blog_text": "modify blog from QxOrm REST API",
    +      "date_creation": "2013-11-25T09:56:33",
    +      "author_id": "author_id_1"
    +   }
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "blog_id": 1
    +   },
    +   "request_id": "ec3c71eb-5014-4b36-85a0-aeb7ae48a5e9"
    +}
    +
    +

    + -- Exemple n�2 -- sauvegarde (ins�re ou met + � jour suivant l'identifant unique) une liste de blogs dans la base de donn�es : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "dc7c804e-f95a-4a9b-a4e3-547adcacf090",
    +   "action": "save",
    +   "entity": "blog",
    +   "data": [
    +      {
    +         "blog_id": 1,
    +         "blog_text": "save blog from QxOrm REST API !",
    +         "date_creation": "2018-01-30T12:42:01",
    +         "author_id": "author_id_2"
    +      },
    +      {
    +         "blog_text": "save another blog from QxOrm REST API !",
    +         "date_creation": "2016-06-12T08:33:12",
    +         "author_id": "author_id_1"
    +      }
    +   ]
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": [
    +      {
    +         "blog_id": 1
    +      },
    +      {
    +         "blog_id": 5
    +      }
    +   ],
    +   "request_id": "dc7c804e-f95a-4a9b-a4e3-547adcacf090"
    +}
    +
    +

    + -- Exemple n�3 -- sauvegarde (ins�re ou met + � jour suivant l'identifant unique) un blog et toutes ses relations sur plusieurs niveaux (de + fa�on r�cursive) : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "5b78e468-2fa3-4aeb-82ce-4d85408f5fa7",
    +   "action": "save",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 1,
    +      "blog_text": "save recursive blog from QxOrm REST API",
    +      "date_creation": "2013-11-25T09:56:33",
    +      "author_id": {
    +         "author_id": "author_id_1",
    +         "birthdate": "1965-07-21",
    +         "name": "save recursive author from QxOrm REST API",
    +         "sex": 0
    +      }
    +   },
    +   "save_mode": "check_insert_or_update"
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "blog_id": 1
    +   },
    +   "request_id": "5b78e468-2fa3-4aeb-82ce-4d85408f5fa7"
    +}
    +
    +

    + -- Exemple n�4 -- ins�re (save_mode = + insert_only) un blog et toutes ses relations sur plusieurs niveaux (de fa�on r�cursive) : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "ef147c62-74e0-4be2-a294-ffeb020d5304",
    +   "action": "save",
    +   "entity": "blog",
    +   "data": {
    +      "blog_text": "save recursive - new blog from QxOrm REST API",
    +      "date_creation": "2013-11-25T09:56:33",
    +      "author_id": {
    +         "author_id": "author_id_save_recursive",
    +         "birthdate": "1965-07-21",
    +         "name": "save recursive (insert only) author from QxOrm REST API",
    +         "sex": 0
    +      }
    +   },
    +   "save_mode": "insert_only"
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "blog_id": 7
    +   },
    +   "request_id": "ef147c62-74e0-4be2-a294-ffeb020d5304"
    +}
    +
    +

    +
    +

    Suppression de donn�es + (delete)

    +
    + Ce chapitre d�taille les diff�rentes m�thodes pour supprimer des �l�ments de la base de donn�es + : + +
    + Remarque : la diff�rence entre delete et destroy est li�e � la suppression logique (soft delete) d'un �l�ment. +

    +

    delete_all / + destroy_all

    +
    + Les actions delete_all et destroy_all permettent de supprimer + tous les �l�ments d'une table. + La diff�rence entre delete et destroy est li�e � la suppression logique (soft delete) d'un �l�ment. +

    + -- Exemple n�1 -- supprime tous les + �l�ments de la table comment : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "7b06b5c0-409f-4e0d-bfc4-acafbfe7e796",
    +   "action": "delete_all",
    +   "entity": "comment"
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "deleted": true
    +   },
    +   "request_id": "7b06b5c0-409f-4e0d-bfc4-acafbfe7e796"
    +}
    +
    +

    +
    +

    delete_by_query / + destroy_by_query

    +
    + Les actions delete_by_query et destroy_by_query permettent de + supprimer les �l�ments d'une table en fonction d'une requ�te. + La diff�rence entre delete et destroy est li�e � la suppression logique (soft delete) d'un �l�ment. +

    + -- Exemple n�1 -- supprime les �l�ments + de la table author qui ont un sexe de type female (female = enum de + valeur 1) : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "169ff0be-6e49-457b-a99c-22bd7141dc02",
    +   "action": "delete_by_query",
    +   "entity": "author",
    +   "query": {
    +      "sql": "WHERE author.sex = :sex",
    +      "params": [
    +         {
    +            "key": ":sex",
    +            "value": 1
    +         }
    +      ]
    +   }
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "deleted": true
    +   },
    +   "request_id": "169ff0be-6e49-457b-a99c-22bd7141dc02"
    +}
    +
    +

    +
    +

    delete_by_id / + destroy_by_id

    +
    + Les actions delete_by_id et destroy_by_id permettent de supprimer + les �l�ments d'une table en fonction de leur identifiant unique. + La diff�rence entre delete et destroy est li�e � la suppression logique (soft delete) d'un �l�ment. +

    + -- Exemple n�1 -- supprime de la base de + donn�es le blog qui a pour identifiant unique la valeur 4 : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "80bff383-8ebd-4bde-bb42-37b6f67bc39f",
    +   "action": "delete_by_id",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 4
    +   }
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "blog_id": 4
    +   },
    +   "request_id": "80bff383-8ebd-4bde-bb42-37b6f67bc39f"
    +}
    +
    +

    + -- Exemple n�2 -- supprime de la base de + donn�es les blogs qui ont pour identifiant unique les valeurs 2 et 3 : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "38020cb7-d725-4c0e-80a0-63db7569155e",
    +   "action": "delete_by_id",
    +   "entity": "blog",
    +   "data": [
    +      {
    +         "blog_id": 3
    +      },
    +      {
    +         "blog_id": 2
    +      }
    +   ]
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": [
    +      {
    +         "blog_id": 3
    +      },
    +      {
    +         "blog_id": 2
    +      }
    +   ],
    +   "request_id": "38020cb7-d725-4c0e-80a0-63db7569155e"
    +}
    +
    +

    +
    +
    +

    Validation de donn�es + (validate)

    +
    + L'action validate permet de valider les propri�t�s d'une instance (sans d�clencher + d'action sur la base de donn�es). + L'action validate appelle le module QxValidator de la + biblioth�que QxOrm. +

    + -- Exemple n�1 -- un blog doit + contenir du texte (propri�t� blog_text) pour pouvoir �tre sauvegard� dans la base de + donn�es. + La requ�te JSON suivante permet d'indiquer que l'instance est non valide avec un message + explicite : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "92043c2b-4ba8-4583-8fad-c828251734ba",
    +   "action": "validate",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 9999,
    +      "blog_text": ""
    +   }
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "invalid_values": [
    +         "blog",
    +         [
    +            {
    +               "message": "'blog_text' property cannot be empty",
    +               "path": "blog"
    +            }
    +         ]
    +      ]
    +   },
    +   "request_id": "92043c2b-4ba8-4583-8fad-c828251734ba"
    +}
    +
    +

    + -- Exemple n�2 -- en ajoutant une valeur � + la propri�t� blog_text, alors le blog devient valide (la r�ponse JSON dispose d'un + champ invalid_values qui vaut null) : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "92043c2b-4ba8-4583-8fad-c828251734ba",
    +   "action": "validate",
    +   "entity": "blog",
    +   "data": {
    +      "blog_id": 9999,
    +      "blog_text": "my blog text !!!"
    +   }
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "invalid_values": null
    +   },
    +   "request_id": "92043c2b-4ba8-4583-8fad-c828251734ba"
    +}
    +
    +

    +
    +

    Appel RAW SQL ou + proc�dure stock�e

    +
    + L'action call_custom_query permet d'appeler une requ�te SQL personnalis�e ou une + proc�dure stock�e. +

    + -- Exemple n�1 -- ins�re dans la base de + donn�es un nouveau author avec une requ�te SQL personnalis�e : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "ff2a2256-041d-4c5f-bd86-3745ce46ead8",
    +   "action": "call_custom_query",
    +   "query": {
    +      "sql": "INSERT INTO author (author_id, name, birthdate, sex) VALUES (:author_id, :name, :birthdate, :sex)",
    +      "params": [
    +         {
    +            "key": ":author_id",
    +            "value": "author_id_custom_query"
    +         },
    +         {
    +            "key": ":name",
    +            "value": "new author inserted by custom query"
    +         },
    +         {
    +            "key": ":birthdate",
    +            "value": "20190215"
    +         },
    +         {
    +            "key": ":sex",
    +            "value": 2
    +         }
    +      ]
    +   }
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "query_output": {
    +         "distinct": false,
    +         "list_values": {
    +            ":author_id": [
    +               "author_id_custom_query",
    +               1
    +            ],
    +            ":birthdate": [
    +               "20190215",
    +               1
    +            ],
    +            ":name": [
    +               "new author inserted by custom query",
    +               1
    +            ],
    +            ":sex": [
    +               2,
    +               1
    +            ]
    +         },
    +         "parenthesis_count": 0,
    +         "query": [
    +            "INSERT INTO author (author_id, name, birthdate, sex) VALUES (:author_id, :name, :birthdate, :sex)"
    +         ],
    +         "response": "",
    +         "result_position_by_key": {},
    +         "result_values": [],
    +         "sql_element_index": 0,
    +         "sql_element_list": [],
    +         "sql_element_temp_type": 0,
    +         "type": ""
    +      }
    +   },
    +   "request_id": "ff2a2256-041d-4c5f-bd86-3745ce46ead8"
    +}
    +
    +

    +
    +

    Appel fonctions natives + C++

    +
    + L'action call_entity_function permet d'appeler des fonctions natives C++ + enregistr�es dans le contexte QxOrm.
    + Pr�requis : la fonction native C++ doit �tre une fonction static avec pour + signature : + static QJsonValue myNativeCppFct(const QJsonValue & + request); + +

    + Voici un exemple d'enregistrement de fonction native C++ pouvant �tre appel�e par les API JSON + de la biblioth�que QxOrm : +

    + + + + + + + +
    +
    namespace qx {
    +template <> void register_class(QxClass<blog> & t)
    +{
    +   // Register 'helloWorld()' static function in QxOrm context (can be called by QxRestApi JSON API module)
    +   t.fctStatic_1<QJsonValue, const QJsonValue & >(& blog::helloWorld, "helloWorld");
    +}}
    +
    +// 'helloWorld()' static function implementation
    +QJsonValue blog::helloWorld(const QJsonValue & request)
    +{
    +   QJsonObject response;
    +   response.insert("request", request);
    +   response.insert("response", QString("Hello World !"));
    +   return response;
    +}
    +
    +

    + Voici comment appeler cette fonction helloWorld avec les API JSON en utilisant l'action + call_entity_function : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "ab1ba7d3-9f98-4b18-a310-a9c34498d043",
    +   "action": "call_entity_function",
    +   "entity": "blog",
    +   "fct": "helloWorld",
    +   "data": {
    +      "param1": "test",
    +      "param2": "static fct call"
    +   }
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "request": {
    +         "param1": "test",
    +         "param2": "static fct call"
    +      },
    +      "response": "Hello World !"
    +   },
    +   "request_id": "ab1ba7d3-9f98-4b18-a310-a9c34498d043"
    +}
    +
    +

    +
    +

    Meta-data (structure des + classes C++ enregistr�es dans le contexte QxOrm)

    +
    + L'action get_meta_data permet de r�cup�rer les m�ta-donn�es d'une ou de toutes les + entit�s enregistr�es dans le contexte QxOrm (structure des classes avec liste des propri�t�s et + relations). +

    + -- Exemple n�1 -- r�cup�re toutes les + m�ta-donn�es du projet d'exemple qxBlogRestApi : +

    + Requ�te JSON :
    +
    +
    {
    +   "request_id": "842ed7b5-9b94-455f-86dc-32992866b3d5",
    +   "action": "get_meta_data",
    +   "entity": "*"
    +}
    +
    + R�ponse JSON :
    +
    +
    {
    +   "data": {
    +      "entities": [
    +         {
    +            "base_entity": "",
    +            "description": "",
    +            "entity_id": {
    +               "description": "",
    +               "key": "author_id",
    +               "type": "QString"
    +            },
    +            "key": "author",
    +            "name": "author",
    +            "properties": [
    +               {
    +                  "description": "",
    +                  "key": "name",
    +                  "type": "QString"
    +               },
    +               {
    +                  "description": "",
    +                  "key": "birthdate",
    +                  "type": "QDate"
    +               },
    +               {
    +                  "description": "",
    +                  "key": "sex",
    +                  "type": "enum author::enum_sex *"
    +               }
    +            ],
    +            "relations": [
    +               {
    +                  "description": "",
    +                  "key": "list_blog",
    +                  "target": "blog",
    +                  "type": "std::vector<std::shared_ptr<blog>>",
    +                  "type_relation": "relation one-to-many"
    +               }
    +            ],
    +            "version": 0
    +         },
    +         {
    +            "base_entity": "",
    +            "description": "",
    +            "entity_id": {
    +               "description": "",
    +               "key": "blog_id",
    +               "type": "long"
    +            },
    +            "key": "blog",
    +            "name": "blog",
    +            "properties": [
    +               {
    +                  "description": "",
    +                  "key": "blog_text",
    +                  "type": "QString"
    +               },
    +               {
    +                  "description": "",
    +                  "key": "date_creation",
    +                  "type": "QDateTime"
    +               }
    +            ],
    +            "relations": [
    +               {
    +                  "description": "",
    +                  "key": "author_id",
    +                  "target": "author",
    +                  "type": "std::shared_ptr<author>",
    +                  "type_relation": "relation many-to-one"
    +               },
    +               {
    +                  "description": "",
    +                  "key": "list_comment",
    +                  "target": "comment",
    +                  "type": "QList<std::shared_ptr<comment>>",
    +                  "type_relation": "relation one-to-many"
    +               },
    +               {
    +                  "description": "",
    +                  "key": "list_category",
    +                  "target": "category",
    +                  "type": "qx::QxCollection<long, QSharedPointer<category>>",
    +                  "type_relation": "relation many-to-many"
    +               }
    +            ],
    +            "version": 0
    +         },
    +         {
    +            "base_entity": "",
    +            "description": "",
    +            "entity_id": {
    +               "description": "",
    +               "key": "comment_id",
    +               "type": "long"
    +            },
    +            "key": "comment",
    +            "name": "comment",
    +            "properties": [
    +               {
    +                  "description": "",
    +                  "key": "comment_text",
    +                  "type": "QString"
    +               },
    +               {
    +                  "description": "",
    +                  "key": "date_creation",
    +                  "type": "QDateTime"
    +               }
    +            ],
    +            "relations": [
    +               {
    +                  "description": "",
    +                  "key": "blog_id",
    +                  "target": "blog",
    +                  "type": "std::shared_ptr<blog>",
    +                  "type_relation": "relation many-to-one"
    +               }
    +            ],
    +            "version": 0
    +         },
    +         {
    +            "base_entity": "",
    +            "description": "",
    +            "entity_id": {
    +               "description": "",
    +               "key": "category_id",
    +               "type": "long"
    +            },
    +            "key": "category",
    +            "name": "category",
    +            "properties": [
    +               {
    +                  "description": "",
    +                  "key": "name",
    +                  "type": "QString"
    +               },
    +               {
    +                  "description": "",
    +                  "key": "description",
    +                  "type": "QString"
    +               }
    +            ],
    +            "relations": [
    +               {
    +                  "description": "",
    +                  "key": "list_blog",
    +                  "target": "blog",
    +                  "type": "qx::QxCollection<long, std::shared_ptr<blog>>",
    +                  "type_relation": "relation many-to-many"
    +               }
    +            ],
    +            "version": 0
    +         }
    +      ]
    +   },
    +   "request_id": "842ed7b5-9b94-455f-86dc-32992866b3d5"
    +}
    +
    +

    +
    +

    Envoyer une liste de + requ�tes JSON

    +
    + Afin de limiter le nombre de transactions entre le client et le serveur, il est possible + d'envoyer une liste de requ�tes JSON au module QxRestApi. + Chaque requ�te JSON de la liste peut disposer de son propre identifiant request_id (afin + d'associer une r�ponse JSON � la requ�te correspondante). + Lorsqu'une liste de requ�tes JSON est envoy�e au module QxRestApi, alors une transaction (commit/rollback) est automatiquement cr��e (ainsi en + cas d'erreur, tous les traitements sur la base de donn�es sont annul�s). +

    + -- Exemple n�1 -- envoi 4 requ�tes JSON au + module QxRestApi (1 requ�te pour r�cup�rer les m�ta-donn�es du + projet + 3 requ�tes fetch_all avec diff�rent niveau de + r�cup�ration des relations) : +

    + Requ�te JSON :
    +
    +
    [
    +   {
    +      "request_id": "53c96a23-2566-4b3d-ae6c-bff634600e79",
    +      "action": "get_meta_data",
    +      "entity": "*"
    +   },
    +   {
    +      "request_id": "56e3ca99-5c12-4aca-aa6c-7d0e43c1e636",
    +      "action": "fetch_all",
    +      "entity": "blog"
    +   },
    +   {
    +      "request_id": "692968e4-8885-41ad-b918-6ce2791b3bb8",
    +      "action": "fetch_all",
    +      "entity": "blog",
    +      "data": [
    +         {
    +            "key": "",
    +            "value": ""
    +         }
    +      ]
    +   },
    +   {
    +      "request_id": "4ffe38a6-d642-44b0-8be1-198e84256321",
    +      "action": "fetch_all",
    +      "entity": "blog",
    +      "relations": [
    +         "*->*"
    +      ]
    +   }
    +]
    +
    + R�ponse JSON :
    +
    +
    [
    +   {
    +      "data": {
    +         "entities": [
    +            {
    +               "base_entity": "",
    +               "description": "",
    +               "entity_id": {
    +                  "description": "",
    +                  "key": "author_id",
    +                  "type": "QString"
    +               },
    +               "key": "author",
    +               "name": "author",
    +               "properties": [
    +                  {
    +                     "description": "",
    +                     "key": "name",
    +                     "type": "QString"
    +                  },
    +                  {
    +                     "description": "",
    +                     "key": "birthdate",
    +                     "type": "QDate"
    +                  },
    +                  {
    +                     "description": "",
    +                     "key": "sex",
    +                     "type": "enum author::enum_sex *"
    +                  }
    +               ],
    +               "relations": [
    +                  {
    +                     "description": "",
    +                     "key": "list_blog",
    +                     "target": "blog",
    +                     "type": "std::vector<std::shared_ptr<blog>>",
    +                     "type_relation": "relation one-to-many"
    +                  }
    +               ],
    +               "version": 0
    +            },
    +            {
    +               "base_entity": "",
    +               "description": "",
    +               "entity_id": {
    +                  "description": "",
    +                  "key": "blog_id",
    +                  "type": "long"
    +               },
    +               "key": "blog",
    +               "name": "blog",
    +               "properties": [
    +                  {
    +                     "description": "",
    +                     "key": "blog_text",
    +                     "type": "QString"
    +                  },
    +                  {
    +                     "description": "",
    +                     "key": "date_creation",
    +                     "type": "QDateTime"
    +                  }
    +               ],
    +               "relations": [
    +                  {
    +                     "description": "",
    +                     "key": "author_id",
    +                     "target": "author",
    +                     "type": "std::shared_ptr<author>",
    +                     "type_relation": "relation many-to-one"
    +                  },
    +                  {
    +                     "description": "",
    +                     "key": "list_comment",
    +                     "target": "comment",
    +                     "type": "QList<std::shared_ptr<comment>>",
    +                     "type_relation": "relation one-to-many"
    +                  },
    +                  {
    +                     "description": "",
    +                     "key": "list_category",
    +                     "target": "category",
    +                     "type": "qx::QxCollection<long, QSharedPointer<category>>",
    +                     "type_relation": "relation many-to-many"
    +                  }
    +               ],
    +               "version": 0
    +            },
    +            {
    +               "base_entity": "",
    +               "description": "",
    +               "entity_id": {
    +                  "description": "",
    +                  "key": "comment_id",
    +                  "type": "long"
    +               },
    +               "key": "comment",
    +               "name": "comment",
    +               "properties": [
    +                  {
    +                     "description": "",
    +                     "key": "comment_text",
    +                     "type": "QString"
    +                  },
    +                  {
    +                     "description": "",
    +                     "key": "date_creation",
    +                     "type": "QDateTime"
    +                  }
    +               ],
    +               "relations": [
    +                  {
    +                     "description": "",
    +                     "key": "blog_id",
    +                     "target": "blog",
    +                     "type": "std::shared_ptr<blog>",
    +                     "type_relation": "relation many-to-one"
    +                  }
    +               ],
    +               "version": 0
    +            },
    +            {
    +               "base_entity": "",
    +               "description": "",
    +               "entity_id": {
    +                  "description": "",
    +                  "key": "category_id",
    +                  "type": "long"
    +               },
    +               "key": "category",
    +               "name": "category",
    +               "properties": [
    +                  {
    +                     "description": "",
    +                     "key": "name",
    +                     "type": "QString"
    +                  },
    +                  {
    +                     "description": "",
    +                     "key": "description",
    +                     "type": "QString"
    +                  }
    +               ],
    +               "relations": [
    +                  {
    +                     "description": "",
    +                     "key": "list_blog",
    +                     "target": "blog",
    +                     "type": "qx::QxCollection<long, std::shared_ptr<blog>>",
    +                     "type_relation": "relation many-to-many"
    +                  }
    +               ],
    +               "version": 0
    +            }
    +         ]
    +      },
    +      "request_id": "53c96a23-2566-4b3d-ae6c-bff634600e79"
    +   },
    +   {
    +      "data": [
    +         {
    +            "author_id": {
    +               "author_id": "author_id_1",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 1,
    +            "blog_text": "save recursive blog from QxOrm REST API",
    +            "date_creation": "2013-11-25T09:56:33",
    +            "list_category": [],
    +            "list_comment": []
    +         },
    +         {
    +            "author_id": {
    +               "author_id": "author_id_1",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 5,
    +            "blog_text": "save another blog from QxOrm REST API !",
    +            "date_creation": "2016-06-12T08:33:12",
    +            "list_category": [],
    +            "list_comment": []
    +         },
    +         {
    +            "author_id": {
    +               "author_id": "author_id_save_recursive",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 6,
    +            "blog_text": "save recursive - new blog from QxOrm REST API",
    +            "date_creation": "2013-11-25T09:56:33",
    +            "list_category": [],
    +            "list_comment": []
    +         },
    +         {
    +            "author_id": {
    +               "author_id": "author_id_save_recursive",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 7,
    +            "blog_text": "save recursive - new blog from QxOrm REST API",
    +            "date_creation": "2013-11-25T09:56:33",
    +            "list_category": [],
    +            "list_comment": []
    +         }
    +      ],
    +      "request_id": "56e3ca99-5c12-4aca-aa6c-7d0e43c1e636"
    +   },
    +   {
    +      "data": [
    +         {
    +            "key": 1,
    +            "value": {
    +               "author_id": {
    +                  "author_id": "author_id_1",
    +                  "birthdate": null,
    +                  "list_blog": [],
    +                  "name": "",
    +                  "sex": 2
    +               },
    +               "blog_id": 1,
    +               "blog_text": "save recursive blog from QxOrm REST API",
    +               "date_creation": "2013-11-25T09:56:33",
    +               "list_category": [],
    +               "list_comment": []
    +            }
    +         },
    +         {
    +            "key": 5,
    +            "value": {
    +               "author_id": {
    +                  "author_id": "author_id_1",
    +                  "birthdate": null,
    +                  "list_blog": [],
    +                  "name": "",
    +                  "sex": 2
    +               },
    +               "blog_id": 5,
    +               "blog_text": "save another blog from QxOrm REST API !",
    +               "date_creation": "2016-06-12T08:33:12",
    +               "list_category": [],
    +               "list_comment": []
    +            }
    +         },
    +         {
    +            "key": 6,
    +            "value": {
    +               "author_id": {
    +                  "author_id": "author_id_save_recursive",
    +                  "birthdate": null,
    +                  "list_blog": [],
    +                  "name": "",
    +                  "sex": 2
    +               },
    +               "blog_id": 6,
    +               "blog_text": "save recursive - new blog from QxOrm REST API",
    +               "date_creation": "2013-11-25T09:56:33",
    +               "list_category": [],
    +               "list_comment": []
    +            }
    +         },
    +         {
    +            "key": 7,
    +            "value": {
    +               "author_id": {
    +                  "author_id": "author_id_save_recursive",
    +                  "birthdate": null,
    +                  "list_blog": [],
    +                  "name": "",
    +                  "sex": 2
    +               },
    +               "blog_id": 7,
    +               "blog_text": "save recursive - new blog from QxOrm REST API",
    +               "date_creation": "2013-11-25T09:56:33",
    +               "list_category": [],
    +               "list_comment": []
    +            }
    +         }
    +      ],
    +      "request_id": "692968e4-8885-41ad-b918-6ce2791b3bb8"
    +   },
    +   {
    +      "data": [
    +         {
    +            "author_id": {
    +               "author_id": "author_id_1",
    +               "birthdate": "2019-04-02",
    +               "list_blog": [
    +                  {
    +                     "author_id": {
    +                        "author_id": "author_id_1",
    +                        "birthdate": null,
    +                        "list_blog": [],
    +                        "name": "",
    +                        "sex": 2
    +                     },
    +                     "blog_id": 5,
    +                     "blog_text": "save another blog from QxOrm REST API !",
    +                     "date_creation": "2016-06-12T08:33:12",
    +                     "list_category": [],
    +                     "list_comment": []
    +                  },
    +                  {
    +                     "author_id": {
    +                        "author_id": "author_id_1",
    +                        "birthdate": null,
    +                        "list_blog": [],
    +                        "name": "",
    +                        "sex": 2
    +                     },
    +                     "blog_id": 1,
    +                     "blog_text": "save recursive blog from QxOrm REST API",
    +                     "date_creation": "2013-11-25T09:56:33",
    +                     "list_category": [],
    +                     "list_comment": []
    +                  }
    +               ],
    +               "name": "author_1",
    +               "sex": 0
    +            },
    +            "blog_id": 1,
    +            "blog_text": "save recursive blog from QxOrm REST API",
    +            "date_creation": "2013-11-25T09:56:33",
    +            "list_category": [],
    +            "list_comment": []
    +         },
    +         {
    +            "author_id": {
    +               "author_id": "author_id_1",
    +               "birthdate": "2019-04-02",
    +               "list_blog": [
    +                  {
    +                     "author_id": {
    +                        "author_id": "author_id_1",
    +                        "birthdate": null,
    +                        "list_blog": [],
    +                        "name": "",
    +                        "sex": 2
    +                     },
    +                     "blog_id": 5,
    +                     "blog_text": "save another blog from QxOrm REST API !",
    +                     "date_creation": "2016-06-12T08:33:12",
    +                     "list_category": [],
    +                     "list_comment": []
    +                  },
    +                  {
    +                     "author_id": {
    +                        "author_id": "author_id_1",
    +                        "birthdate": null,
    +                        "list_blog": [],
    +                        "name": "",
    +                        "sex": 2
    +                     },
    +                     "blog_id": 1,
    +                     "blog_text": "save recursive blog from QxOrm REST API",
    +                     "date_creation": "2013-11-25T09:56:33",
    +                     "list_category": [],
    +                     "list_comment": []
    +                  }
    +               ],
    +               "name": "author_1",
    +               "sex": 0
    +            },
    +            "blog_id": 5,
    +            "blog_text": "save another blog from QxOrm REST API !",
    +            "date_creation": "2016-06-12T08:33:12",
    +            "list_category": [],
    +            "list_comment": []
    +         },
    +         {
    +            "author_id": {
    +               "author_id": "author_id_save_recursive",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 6,
    +            "blog_text": "save recursive - new blog from QxOrm REST API",
    +            "date_creation": "2013-11-25T09:56:33",
    +            "list_category": [],
    +            "list_comment": []
    +         },
    +         {
    +            "author_id": {
    +               "author_id": "author_id_save_recursive",
    +               "birthdate": null,
    +               "list_blog": [],
    +               "name": "",
    +               "sex": 2
    +            },
    +            "blog_id": 7,
    +            "blog_text": "save recursive - new blog from QxOrm REST API",
    +            "date_creation": "2013-11-25T09:56:33",
    +            "list_category": [],
    +            "list_comment": []
    +         }
    +      ],
    +      "request_id": "4ffe38a6-d642-44b0-8be1-198e84256321"
    +   }
    +]
    +
    +

    +
    +
    + +
    +
    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/doc/qxorm_fr/manual_qxee.html b/doc/qxorm_fr/manual_qxee.html new file mode 100644 index 0000000..c8a0b16 --- /dev/null +++ b/doc/qxorm_fr/manual_qxee.html @@ -0,0 +1,5824 @@ + + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic + editor (data model designer and source code generator) + + + + + + + + + + + + + + + + + + + + + +
    QxOrm + + + Windows + Linux + Macintosh + C++
    +
    + + + + + + + + + + + + + + + + + + +
    AccueilT�l�chargementExemple rapide + Tutoriel (4) + + + + + + +
    + +
    +
    + Manuel (2) + + + + + + +
    + +
    +
    ForumNos clients
    +
    + + + + + + + + + + + + + + + + + +
    + QxOrm >> Manuel d'utilisation de l'application + QxEntityEditor + + + + + + + + + + + + + + +
    + Version courante :  + + QxOrm 1.5.0 - documentation en ligne de la biblioth�que QxOrm - GitHub +
    + + + QxEntityEditor 1.2.8 +
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    + + + + + + + + + + +
    + S�lection du manuel : + + Manuel QxOrm + Manuel QxEntityEditor +
    +
    +
    + + + + + + + + + +
    +

    Manuel d'utilisation de l'application QxEntityEditor - Table des + mati�res

    +
    +
      +
    1. + Introduction +
        +
      1. + QxEntityEditor : �diteur graphique pour la biblioth�que + QxOrm +
      2. +
      3. + T�l�chargement et installation +
      4. +
      5. + Vid�os de pr�sentation de l'application + QxEntityEditor +
      6. +
      +
    2. +
    3. + Pr�sentation g�n�rale +
        +
      1. + Vues de l'�cran principal de QxEntityEditor +
          +
        1. + Zoomer/d�zoomer le diagramme d'entit�s +
        2. +
        3. + Affichage en mode "splitter" +
        4. +
        +
      2. +
      3. + Param�trage de l'application QxEntityEditor +
      4. +
      5. + Enregistrer un code licence +
      6. +
      7. + Cr�er/ouvrir un projet *.qxee +
          +
        1. + Architecture d'un fichier projet *.qxee (base + de donn�es SQLite) +
        2. +
        +
      8. +
      9. + Param�trage d'un projet *.qxee +
      10. +
      11. + Gestion des entit�s +
          +
        1. + Cr�er une entit� +
        2. +
        3. + Modifier une entit� +
        4. +
        5. + Supprimer une entit� +
        6. +
        7. + Cloner une entit� +
        8. +
        9. + Param�tres d'une entit� +
        10. +
        11. + Couleur associ�e � une entit� +
        12. +
        +
      12. +
      13. + Gestion des propri�t�s associ�es � une entit� +
          +
        1. + Ajouter/modifier/supprimer une + propri�t� +
        2. +
        3. + Param�trage d�taill� d'une propri�t� +
        4. +
        +
      14. +
      15. + Gestion des relations entre entit�s +
          +
        1. + Ajouter/modifier/supprimer une + relation +
        2. +
        3. + Param�trage d�taill� d'une relation +
        4. +
        +
      16. +
      17. + Gestion des �num�rations +
          +
        1. + Ajouter/modifier/supprimer/cloner une + �num�ration +
        2. +
        3. + Param�tres d'une �num�ration +
        4. +
        5. + Couleur associ�e � une �num�ration +
        6. +
        +
      18. +
      19. + Gestion des espaces de nom (namespace) +
          +
        1. + Renommer un espace de nom +
        2. +
        3. + Gestion des couleurs d'un espace + de nom +
        4. +
        +
      20. +
      21. + Gestion des notes (ou post-it) +
          +
        1. + Ajouter/modifier/supprimer/cloner une + note +
        2. +
        3. + Couleur associ�e � une note +
        4. +
        +
      22. +
      23. + Organisation automatique du diagramme + d'entit�s +
      24. +
      25. + Historique d'un projet *.qxee +
      26. +
      27. + Aper�u du code C++ d'une entit�/�num�ration +
      28. +
      29. + Convention de nommage (snake_case, camelCase) +
      30. +
      31. + Liste des plugins disponibles +
      32. +
      +
    4. +
    5. + Plugins d'import +
        +
      1. + Importer un projet � partir d'un + r�pertoire associ� � un gestionnaire de code source (Git, Perforce, CVS, + etc.) +
      2. +
      3. + Importer un projet � partir d'un fichier texte au + format JSON +
      4. +
      5. + Importer � partir d'une base de donn�es MySQL ou + MariaDB +
      6. +
      7. + Importer � partir d'une base de donn�es + PostgreSQL +
      8. +
      9. + Importer � partir d'une base de donn�es + SQLite +
      10. +
      11. + Importer � partir d'une base de donn�es en + utilisant un driver ODBC (Oracle, MS SQL Server, etc.) +
      12. +
      +
    6. +
    7. + Plugins d'export +
        +
      1. + Exporter en projet C++ +
          +
        1. + Param�tres de l'export C++ +
        2. +
        3. + Pr�sentation du projet C++ + g�n�r� +
        4. +
        5. + Compilation du projet C++ + g�n�r� (par qmake ou cmake) +
        6. +
        7. + Exemple d'utilisation +
        8. +
        +
      2. +
      3. + Exporter en projet C++ de type + model/view +
          +
        1. + Param�tres de + l'export +
        2. +
        3. + Pr�sentation du + projet g�n�r� +
        4. +
        +
      4. +
      5. + Exporter en projet C++ de type + services +
          +
        1. + Param�tres de + l'export +
        2. +
        3. + Pr�sentation du projet + g�n�r� +
        4. +
        5. + Serveur + d'applications g�n�rique +
        6. +
        +
      6. +
      7. + Exporter le sch�ma de base de donn�es SQL + DDL +
      8. +
      9. + Imprimer le diagramme d'entit�s +
      10. +
      11. + Exporter le projet dans un + r�pertoire associ� � un gestionnaire de code source (Git, Perforce, CVS, + etc.) +
      12. +
      13. + Exporter le projet sous format XML ou JSON +
      14. +
      +
    8. +
    9. + Moteur Javascript pour personnaliser les exports +
        +
      1. + Architecture et fonctionnement du moteur de + personnalisation Javascript +
          +
        1. + Liste des param�tres d'appel du moteur + Javascript +
        2. +
        +
      2. +
      3. + Fonctions disponibles par Javascript +
          +
        1. + Obtenir les informations associ�es + � une entit� +
        2. +
        3. + Parcourir la liste des + propri�t�s d'une entit� +
        4. +
        5. + Obtenir les informations + associ�es � une propri�t� +
        6. +
        7. + Obtenir les informations associ�es � + une �num�ration +
        8. +
        9. + Acc�s aux m�ta-donn�es d'une + entit�/propri�t�/�num�ration +
        10. +
        11. + Acc�der aux variables + d'environnement +
        12. +
        13. + Gestion des fichiers : lecture et + �criture +
        14. +
        15. + Obtenir la liste des toutes les + entit�s/�num�rations d'un projet +
        16. +
        17. + R�cup�rer le param�trage de + l'application (niveau global, projet et plugin) +
        18. +
        +
      4. +
      5. + Ajout d'une action (placeholder) + personnalis�e dans le template d'export C++ +
      6. +
      7. + Exemple de script : ajout automatique + de la d�finition Q_PROPERTY sur les propri�t�s C++ g�n�r�es +
      8. +
      9. + Activation du d�bogueur Javascript int�gr� +
      10. +
      +
    10. +
    11. + Ex�cution de scripts personnalis�s avant/apr�s ex�cution + d'un plugin +
    12. +
    13. + Ex�cuter QxEntityEditor en ligne de commande +
    14. +
    +
    qt_ambassador
    + + QxOrm library has been accepted into the Qt + Ambassador Program + +
    +
    +
    +
    + +

    Introduction

    +
    + L'objectif de ce manuel utilisateur est de pr�senter de mani�re structur�e l'ensemble des + fonctionnalit�s propos�es par l'application QxEntityEditor (�diteur graphique de la + biblioth�que QxOrm). + Ce manuel est destin� aux d�veloppeurs et architectes logiciel qui souhaitent g�rer une couche de + donn�es persistante en C++/Qt. + Des comp�tences techniques en C++ et base de donn�es sont requises pour la bonne compr�hension de + ce document. +

    + Remarque : un manuel utilisateur d�di� � la biblioth�que + QxOrm est �galement disponible. +

    +

    QxEntityEditor : �diteur + graphique pour la biblioth�que QxOrm

    +
    + QxEntityEditor est un �diteur graphique pour la biblioth�que QxOrm : + QxEntityEditor permet de g�rer graphiquement le mod�le d'entit�s.
    + QxEntityEditor est multi-plateforme (disponible pour Windows, Linux et Mac OS X) et + g�n�re du code natif pour tous les environnements : bureau (Windows, Linux, Mac OS X), embarqu� + et mobile (Android, iOS, Windows Phone, Raspberry Pi, etc.).
    +
    + QxEntityEditor est bas� sur un syst�me de plugins et propose diverses fonctionnalit�s + pour importer/exporter le mod�le de donn�es : + + + + + + + + + + + + + + + + + +
    QxEntityEditor on Windows + QxEntityEditor on Linux + QxEntityEditor on Mac OS X +
    QxEntityEditor on WindowsQxEntityEditor on LinuxQxEntityEditor on Mac OS X
    +

    + QxEntityEditor est d�velopp� XDL Teamarty, Ing�nieur en d�veloppement logiciel depuis + 2003.
    +

    +
    +

    T�l�chargement + et installation

    +
    + L'application QxEntityEditor peut �tre t�l�charg�e sur la page de + t�l�chargement du site QxOrm. + Plusieurs versions sont disponibles : +
      +
    • Windows (de Windows XP � Windows 10) en mode 32bits et 64bits ;
    • +
    • Linux (les distributions les plus populaires sont support�es, Ubuntu, Fedora, + etc...) en mode 32bits et 64bits ;
    • +
    • Mac OS X (64bits uniquement).
    • +
    + Pour Windows, QxEntityEditor est disponible avec un setup d'installation ou bien un fichier ZIP + portable.
    + Pour Linux et Mac OS X, QxEntityEditor est disponible uniquement en fichier ZIP portable.
    + Aucune d�pendance suppl�mentaire n'est requise, l'installation est tr�s simple : il suffit de + d�zipper le fichier t�l�charg� et de lancer l'ex�cutable QxEntityEditor. +

    +
    +

    Vid�os de pr�sentation + de l'application QxEntityEditor

    +
    + Voici une vid�o de + pr�sentation de l'application QxEntityEditor :
    +
    +
    +
    + Cette vid�o pr�sente les �tapes suivantes : +
      +
    • T�l�charger et installer la biblioth�que QxOrm (10s) ;
    • +
    • T�l�charger et installer l'application QxEntityEditor (56s) ;
    • +
    • Cr�er un projet QxEntityEditor (1m 46s) ;
    • +
    • Exporter les entit�s vers un projet C++/Qt (8m 26s) ;
    • +
    • Exporter les entit�s vers un script DDL SQL de base de donn�es (10m 22s) ;
    • +
    • Cr�er une application client/serveur pour transf�rer son mod�le de donn�es sur le r�seau + (12m 31s) ; +
    • +
    • Exporter un projet QxEntityEditor vers un fichier XML ou JSON (17m 13s) ;
    • +
    • Ex�cuter QxEntityEditor en ligne de commande (sans IHM) (18m 07s).
    • +
    +
    +
    +
    + Voici une autre vid�o de + l'application QxEntityEditor pour montrer comment importer une structure de base de + donn�es existante (projet MySQL Workbench) :
    +
    +
    +
    + Cette vid�o pr�sente les �tapes suivantes : +
      +
    • Projet MySQL Workbench - exemple de base de donn�es Sakila (10s) ;
    • +
    • Cr�ation d'un DSN pour se connecter � MySQL par ODBC (46s) ;
    • +
    • Import de la structure de base de donn�es dans un projet QxEntityEditor (1m 15s) ; +
    • +
    • Export du projet QxEntityEditor vers un projet C++ Qt (3m 10s) ;
    • +
    • Compilation des classes g�n�r�es du projet C++ Qt (4m 22s).
    • +
    +
    +
    +
    + +

    Pr�sentation g�n�rale +

    +
    + QxEntityEditor est un �diteur graphique permettant de g�rer : entit�s, propri�t�s, relations + entre entit�s, �num�rations, espaces de nom (namespace). + Pour faire le lien entre la base de donn�es et le code C++ : +
      +
    • une entit� correspond � une table c�t� base de donn�es, et � une classe c�t� code C++ ;
    • +
    • une propri�t� correspond � une colonne d'une table c�t� base de donn�es, et � une donn�e + membre d'une classe c�t� code C++ ;
    • +
    • une relation (1-n, n-1, 1-1 ou n-n) correspond � une liaison + entre 2 tables de la base de donn�es (cl� �trang�re), et � une liaison entre 2 classes C++ ; +
    • +
    • une �num�ration correspond � une liste de valeurs disponibles c�t� code C++ (pour le moment + pas de notion c�t� base de donn�es : converti en valeur num�rique) ;
    • +
    • un espace de nom (namespace) correspond � un sch�ma de base de donn�es, et un namespace o� + se trouve une classe c�t� code C++.
    • +
    +
    +

    Vues de l'�cran principal + de QxEntityEditor

    +
    + Voici une copie �cran pr�sentant les diff�rentes zones d'affichage de l'application + QxEntityEditor : +

    + All views +

    +
      +
    • Zone 1 : menu principal et barre d'outils de l'application QxEntityEditor ==> + toutes les actions sont accessibles par ces boutons ;
    • +
    • Zone 2 : liste d�roulante permettant d'acc�der rapidement aux projets charg�s + r�cemment ;
    • +
    • Zone 3 : liste sous forme d'arbre (tree view) pr�sentant les �l�ments du projet + charg�s (entit�s, propri�t�s, relations, �num�rations, espaces de nom) ;
    • +
    • Zone 4 : vue "Navigator" permettant de se d�placer rapidement dans le + diagramme d'entit�s (particuli�rement utile pour des projets volumineux) ;
    • +
    • Zone 5 : diagramme d'entit�s affichant tous les �l�ments du projet dans un vue + graphique ;
    • +
    • Zone 6 : �cran de param�trage (sous forme d'onglets) des entit�s, propri�t�s, + relations, �num�rations + pr�visualisation du code C++ ;
    • +
    • Zone 7 : zoom sur le diagramme d'entit�s.
    • +
    +
    +

    Zoomer/d�zoomer le + diagramme d'entit�s

    +
    + Il est possible de zoomer/d�zoomer le diagramme d'entit�s : +
      +
    • par le menu principal : View >> Zoom view to XX% ;
    • +
    • par la petite fen�tre (slider) tout en bas � droite de l'�cran ;
    • +
    • par menu contextuel suite � un clic-droit sur le diagramme d'entit�s, sous menu : + View >> Zoom view to XX% ; +
    • +
    • en maintenant clic-gauche + molette de la souris sur le diagramme d'entit�s.
    • +
    +
    + Zoom +

    +
    +

    Affichage en mode + "splitter"

    +
    + Le menu View >> Show/hide splitter mode permet de basculer l'affichage des + onglets des param�tres des entit�s, propri�t�s, relations, �num�rations + pr�visualisation du + code C++ : +
      +
    • sur la partie droite de l'�cran permettant d'avoir un acc�s rapide � la fois sur le + diagramme d'entit�s et le d�tails des param�tres de l'�l�ment s�lectionn� ;
    • +
    • masqu� sous forme d'oglets permettant d'afficher le diagramme d'entit�s en mode plein + �cran (valeur par d�faut).
    • +
    +
    + Splitter mode +

    +
    +
    +

    Param�trage de + l'application QxEntityEditor

    +
    + L'acc�s au param�trage global (pour tous les projets) de l'application QxEntityEditor se fait + par le menu principal Tools >> Global settings : +

    + Global settings +
    +
      +
    • Champ � QxOrm library path � : chemin d'acc�s au r�pertoire de la biblioth�que + QxOrm. Ce param�tre est requis pour d�marrer un export C++ afin de trouver le fichier de + configuration QxOrm.pri (ou QxOrm.cmake). Il est possible de renseigner un + chemin absolu, ou bien d'indiquer une variable d'environnement, par exemple : + $$(QXORM_DIR) ; +
    • +
    • Champ � Auto load last opened project at startup � : d�marre QxEntityEditor en + chargeant automatiquement le dernier projet ouvert ;
    • +
    • Champ � Display property type in entities viewer � : affiche sur le diagramme + d'entit�s le type des propri�t�s ;
    • +
    • Champ � Use old style to draw relationships � : simplifie l'affichage des + relations (1-n, n-1, 1-1 ou n-n) ;
    • +
    • Champ � Disable undo/redo feature � : d�sactive la fonctionnalit� + undo/redo, peut �tre utile pour optimiser les performances sur des projets + volumineux ; +
    • +
    • Champs � Entity/Enum/Comment width � : permet de r�gler la largeur par d�faut des + �l�ments graphiques.
    • +
    +
    +
    +

    Enregistrer un code + licence

    +
    + Sans cl� de licence valide, un projet QxEntityEditor est limit� � 5 entit�s par projet. + Afin de supprimer cette limitation, l'acc�s � l'enregistrement d'un code licence se fait par le + menu principal Help >> License information : +

    + License +

    + Il suffit d'entrer la cl� de license fournie dans le champ License key, puis d'appuyer + sur le bouton Save. + Un acc�s � internet est requis pour l'enregistrement d'une cl� de licence. + Si aucune erreur n'apparait, la cl� de licence est enregistr�e et l'application QxEntityEditor + peut alors �tre utilis�e sans aucune limitation jusqu'� la date indiqu�e dans le champ + Expiration date. +

    + Remarque : si une erreur de connexion internet apparait au moment de l'enregistrement, + vous pouvez essayer de cocher la case If you are behind a proxy.... +

    + Autre remarque : pour acqu�rir une cl� de licence valide, vous pouvez nous contacter � + l'adresse suivante : ic-east.com +

    +
    +

    Cr�er/ouvrir un + projet *.qxee

    +
    + La cr�ation d'un nouveau projet QxEntityEditor se fait par le menu principal File >> + New project : +

    + Project new +
    +
      +
    • Champ � Project name � : nom du projet QxEntityEditor. Le fichier du nouveau + projet portera le m�me nom avec l'extension *.qxee ;
    • +
    • Champ � Project location � : chemin d'acc�s au fichier associ� au projet + QxEntityEditor. Il est possible d'utiliser le bouton "..." afin de s�lectionner le + r�pertoire de destination.
    • +
    +
    + L'ouverture d'un projet QxEntityEditor se fait par le menu principal File >> Open + project : il suffit de s�lectionner un fichier projet avec l'extension *.qxee. + Sous la barre d'outils du menu principal, une liste des projets r�cents est disponible et permet + de basculer rapidement d'un projet � un autre : +

    + Project list +

    +

    Architecture d'un + fichier projet *.qxee (base de donn�es SQLite)

    +
    + Un fichier projet *.qxee de l'application QxEntityEditor est une base de donn�es + SQLite.
    + Un m�me projet peut �tre partag� sur tous les environnements support�s par QxEntityEditor : + Windows, Linux et Mac OS X.
    + Un fichier projet *.qxee peut �tre ouvert par un outil de gestion de base de donn�es + SQLite : par exemple, le plugin gratuit de Firefox SQLite + Manager : +

    + Project database +

    + Remarque : en utilisant la fonctionnalit� Ex�cution de + scripts personnalis�s avant/apr�s ex�cution d'un plugin, il est possible par exemple + d'appliquer un script (.bat ou .sh) modifiant certaines donn�es du projet + SQLite *.qxee apr�s un import de base de donn�es par exemple. +

    +
    +
    +

    Param�trage d'un + projet *.qxee

    +
    + Le param�trage d'un projet QxEntityEditor *.qxee se fait par le menu principal Tools + >> Project settings : +

    + Project settings tab 1 +

    +
      +
    • Champ � Default entity namespace � : espace de nom par d�faut des entit�s � leur + cr�ation (il est possible de d�finir un autre espace de nom dans l'�cran de param�trage d'une entit�) ;
    • +
    • Champ � Table name prefix � : pr�fixe � appliquer sur la table associ�e � une + entit� ;
    • +
    • Champ � Table name suffix � : suffixe � appliquer sur la table associ�e � une + entit� ;
    • +
    • Champ � Primary key prefix � : pr�fixe par d�faut de la cl� primaire d'une entit� + ;
    • +
    • Champ � Primary key suffix � : suffixe par d�faut de la cl� primaire d'une entit� + ;
    • +
    • Champ � Default primary key type � : type C++ par d�faut pour les cl�s primaires ; +
    • +
    • Champ � Default property type � : type C++ par d�faut pour les propri�t�s des + entit�s.
    • +
    +
    + Le 2�me onglet de l'�cran de param�trage d'un projet permet de g�rer les couleurs des diff�rents + �l�ments du diagramme d'entit�s au niveau projet : +

    + Project settings tab 2 +

    + Remarque : les couleurs des �l�ments du diagramme d'entit�s peuvent �tre g�r�es � + plusieurs niveaux (du niveau le plus global au plus sp�cifique) : + +
    +
    +

    Gestion des entit�s

    +
    + Une entit� dans l'application QxEntityEditor correspond � une table de la base de donn�es, et + correspond �galement � une classe (persistante et enregistr�e dans le contexte QxOrm) dans le + code C++. + L'application QxEntityEditor permet de cr�er, modifier, supprimer et cloner une entit�. +

    +

    Cr�er une + entit�

    +
    + La cr�ation d'une nouvelle entit� se fait par : +
      +
    • Le menu principal : Actions >> New entity ;
    • +
    • Un menu contextuel sur clic-droit dans le diagramme d'entit�s : Entity >> New + entity ;
    • +
    • Le bouton Create a new entity sur l'�cran de param�trage d'une entit� lorsque + aucune entit� n'est s�lectionn�e.
    • +
    +
    + Entity new +

    + Remarque : ces actions ouvrent l'�cran de param�trage d'une + entit� en mode cr�ation. L'entit� sera r�ellement cr��e et ajout�e au projet + *.qxee au moment de l'enregistrement de l'�cran de param�trage. +

    +
    +

    Modifier une + entit�

    +
    + La modification d'une entit� se fait par : +
      +
    • Le menu principal : Actions >> Modify entity ;
    • +
    • Un menu contextuel sur clic-droit dans le diagramme d'entit�s : Entity >> + Modify entity ;
    • +
    • Le double-clic sur le nom d'une entit� dans le diagramme.
    • +
    +
    + Entity modify +

    + Remarque : ces actions ouvrent l'�cran de param�trage d'une + entit� en mode modification. Les modifications de l'entit� seront r�ellement + appliqu�es au projet *.qxee au moment de l'enregistrement de l'�cran de param�trage. +

    +
    +

    Supprimer une + entit�

    +
    + La suppression d'une (ou plusieurs) entit� se fait par : +
      +
    • Le menu principal : Actions >> Delete entity ;
    • +
    • Un menu contextuel sur clic-droit dans le diagramme d'entit�s : Entity >> + Delete entity ;
    • +
    • La touche "Suppr" du clavier lorsqu'une (ou plusieurs) entit� est s�lectionn�e. +
    • +
    +
    + Entity delete +

    +
    +

    Cloner une + entit�

    +
    + Le clonage d'une entit� se fait par : +
      +
    • Le menu principal : Actions >> Clone entity ;
    • +
    • Un menu contextuel sur clic-droit dans le diagramme d'entit�s : Entity >> + Clone entity ;
    • +
    +
    + Entity clone +

    + Remarque : une entit� clon�e dispose de toutes les propri�t�s de l'entit� source. Les + relations ne sont pas clon�es. +

    +
    +

    Param�tres d'une + entit�

    +
    + Les param�tres de l'entit� s�lectionn�e sont accessibles dans l'onglet de param�trage + Entity : +

    + Entity settings +

    + Cet �cran de param�trage est divis� en plusieurs sections : +

    + -- Section Entity information (version XX) : la version de l'entit� est + incr�ment�e � chaque historique de projet cr��. +
      +
    • Champ � Name � : nom de l'entit� et de la classe C++ g�n�r�e ;
    • +
    • Champ � Table name � : nom de la table en base de donn�es associ�e � l'entit� + (utilise le pr�fixe/suffixe par d�faut d�fini dans les + param�tres du projet) ;
    • +
    • Champ � Namespace � : espace de nom de l'entit� et de la classe C++ associ�e, + chaque sous-namespace doit �tre s�par� par ::, par exemple : + ns1::ns2::ns3 ; +
    • +
    • Champ � Description � : description de l'entit� (apparait dans le code C++ + g�n�r�) ;
    • +
    • Champ � Inherit from � : entit� parente (ou classe C++ de base) : l'entit� + fille r�cup�re automatiquement la cl� primaire et la liste des propri�t�s d�finies dans + l'entit� parente ;
    • +
    • Champ � Read only � : si coch�, alors entit� en lecture seule : la biblioth�que + QxOrm autorise uniquement des requ�tes SQL de type SELECT sur l'entit� (fonctions de r�cup�ration/fetch du namespace + qx::dao de QxOrm). Si coch�, alors les requ�tes SQL de type + INSERT, UPDATE et DELETE ne sont pas disponibles. Ce param�tre + peut par exemple �tre utilis� pour mapper une entit� � une vue (lecture seule) de la + base de donn�es ; +
    • +
    • Champ � Abstract � : une entit� abstraite n'a pas de table associ�e dans la + base de donn�es, et la classe C++ g�n�r�e ne peut pas �tre instanti�e (classe abstraite) : ce param�tre peut �tre utile + par exemple pour cr�er des entit�s parentes contenant une liste de propri�t�s communes + � toutes les autres entit�s ;
    • +
    • Champ � Soft delete column � : si vide, une suppression de l'entit� en base de + donn�es correspond � une suppression physique. Si non vide, ce champ correspond � la colonne en base de donn�es utilis�e pour + effectuer une suppression logique (conserve les informations de suppression et + l'entit� reste en base de donn�es).
    • +
    • Champ � Validator method name � : nom de la m�thode de validation (par exemple + IsValid) utilis�e pour valider une entit� avant insertion et modification en + base de donn�es. L'impl�mentation de la m�thode de validation est � la charge du + d�veloppeur dans son propre fichier source *.cpp : voir le module QxValidator de la biblioth�que QxOrm + pour plus de d�tails. +
    • +
    +
    + -- Section Entity triggers : +
      +
    • Cases � cocher � Before fetch, After fetch, Before insert, etc... + � : si coch�(s), ajoute les d�finitions dans le code C++ des m�thodes + onBeforeFetch(), onAfterFetch(), onBeforeInsert(), etc... + L'impl�mentation des m�thodes de trigger est � la charge du d�veloppeur dans son propre + fichier source *.cpp : voir le manuel + utilisateur de la biblioth�que QxOrm pour plus de d�tails. +
    • +
    +
    + -- Section Primary key property : d�finition rapide de la cl� primaire (un param�trage d�taill� de la cl� primaire de l'entit� est + disponible). + +
    + -- Section List of properties : d�finition rapide de la liste des propri�t�s + (un param�trage d�taill� des propri�t�s de l'entit� est + disponible). Cette liste peut �tre ordonn�e par les 2 boutons fl�che haut et + fl�che bas. +
      +
    • Colonne � Name � : nom de la propri�t� de l'entit�, et nom de la donn�e membre + associ�e dans le code C++ ;
    • +
    • Colonne � Type � : type C++ de la propri�t� (le type par d�faut peut �tre + d�fini dans les param�tres du projet) ;
    • +
    • Colonne � Decoration � : ajoute une d�coration au type C++ (par exemple boost::optional pour g�rer les valeurs + NULL) ;
    • +
    • Colonne � Collection � : si la propri�t� est une liste, alors d�fini le type de + liste C++ ;
    • +
    • Colonne � Default value � : valeur par d�faut de la propri�t� ;
    • +
    • Colonne � Index � : optimisation des requ�tes SQL de type SELECT pour + les recherches en base de donn�es ;
    • +
    • Colonne � Transient � : si coch�, alors la donn�e membre C++ n'est pas mapp�e � + une colonne de la base de donn�es (la propri�t� est enregistr�e dans le contexte QxOrm + mais n'est pas persistante) ;
    • +
    • Bouton � Delete � : supprime la propri�t� de la liste.
    • +
    +
    + -- Section List of relationships : d�finition rapide de la liste des relations + (un param�trage d�taill� des relations de l'entit� est + disponible). Cette liste peut �tre ordonn�e par les 2 boutons fl�che haut et + fl�che bas. +
      +
    • Colonne � Name � : nom de la relation, et nom de la donn�e membre associ�e dans + le code C++ ;
    • +
    • Colonne � Target � : entit� cible ;
    • +
    • Colonne � n-1 � : type de relation many-to-one ;
    • +
    • Colonne � 1-n � : type de relation one-to-many ;
    • +
    • Colonne � n-n � : type de relation many-to-many ;
    • +
    • Colonne � 1-1 � : type de relation one-to-one ;
    • +
    • Colonne � Decoration � : ajoute une d�coration au type C++ (pour les relations, + la biblioth�que QxOrm recommande l'utilisation de pointeurs intelligents : + boost::shared_ptr, std::shared_ptr ou QSharedPointer) ; +
    • +
    • Colonne � Collection � : si le type de relation n�cessite une liste (1-n + et n-n), alors d�fini le type de liste C++ ;
    • +
    • Colonne � Foreign key � : cl� �trang�re de la relation : nom d'une propri�t� de + l'entit� cible ;
    • +
    • Bouton � Delete � : supprime la relation de la liste.
    • +
    +
    + -- Section List of meta-data : les m�ta-donn�es permettent d'ajouter des + param�tres suppl�mentaires et sont accessibles dans le code C++ (moteur d'introspection de QxOrm) et le moteur Javascript de personnalisation des exports + de QxEntityEditor. +
      +
    • Colonne � Key � : cl� de la m�ta-donn�e ;
    • +
    • Colonne � Value � : valeur de la m�ta-donn�e ;
    • +
    • Bouton � Delete � : supprime la m�ta-donn�e de la liste.
    • +
    +
    +
    +

    Couleur + associ�e � une entit�

    +
    + Les couleurs d'une entit� peuvent �tre modifi�es par le menu contextuel sur un clic-droit sur + une entit� Define entity colors : +

    + Entity colors +

    + Remarque : les couleurs des �l�ments du diagramme d'entit�s peuvent �tre g�r�es � + plusieurs niveaux (du niveau le plus global au plus sp�cifique) : + +
    +
    +
    +

    Gestion des propri�t�s + associ�es � une entit�

    +
    + Une propri�t� dans l'application QxEntityEditor correspond � une colonne d'une table de la base + de donn�es, et correspond �galement � une donn�e membre d'une classe C++ (enregistr�e dans le + contexte QxOrm). + L'application QxEntityEditor permet de cr�er, modifier, supprimer et ordonner les propri�t�s + d'une entit�. +

    +

    Ajouter/modifier/supprimer une propri�t�

    +
    + L'ajout et la suppression d'une propri�t� se fait depuis l'�cran de + param�trage d'une entit� : la section List of properties de cet �cran de + param�trage permet d'ajouter et supprimer des propri�t�s. + Il est �galement possible d'ordonner cette liste pour positionner les propri�t�s dans l'ordre + voulu. +

    + La modification d'une propri�t� est accessible par un bouton qui s'affiche sur le diagramme + d'entit�s lorsque la souris est positionn�e sur une propri�t� : +

    + Property settings access +

    +
    +

    Param�trage + d�taill� d'une propri�t�

    +
    + Les param�tres d�taill�s d'une propri�t� s�lectionn�e sont accessibles dans l'onglet de + param�trage Property : +

    + Property settings +

    + Cet �cran de param�trage est divis� en plusieurs sections : +

    + -- Section Property information (version XX) : la version de la propri�t� + d�pend du niveau d'historique du projet. +
      +
    • Champ � Name � : nom de la propri�t� de l'entit�, et nom de la donn�e membre + associ�e dans le code C++ ;
    • +
    • Champ � Column name � : nom de la colonne dans la base de donn�es (si vide, + alors Column name == Name) ;
    • +
    • Champ � Description � : description de la propri�t� (apparait dans le code C++ + g�n�r�) ;
    • +
    • Champ � Index � : optimisation des requ�tes SQL de type SELECT pour les + recherches en base de donn�es ;
    • +
    • Champ � Transient � : si coch�, alors la donn�e membre C++ n'est pas mapp�e � + une colonne de la base de donn�es (la propri�t� est enregistr�e dans le contexte QxOrm + mais n'est pas persistante) ;
    • +
    • Champ � Serializable � : si coch�, la propri�t� est prise en compte par le moteur de s�rialisation de la biblioth�que + QxOrm ;
    • +
    • Champ � Obsolete � : cocher cette option pour supprimer une propri�t� sans casser la compatibilit� ascendante du moteur de + s�rialisation de la biblioth�que QxOrm : la propri�t� devient priv�e sans + accesseur Get/Set.
    • +
    +
    + -- Section Property type : +
      +
    • Champ � Type � : type C++ de la propri�t� (le type par d�faut peut �tre d�fini + dans les param�tres du projet) ; +
    • +
    • Champ � Decoration � : ajoute une d�coration au type C++ (par exemple boost::optional pour g�rer les valeurs + NULL) ;
    • +
    • Champ � Default value � : valeur par d�faut de la propri�t� ;
    • +
    • Champ � Collection � : si la propri�t� est une liste, alors d�fini le type de + liste C++.
    • +
    +
    + -- Section Property validation : validation d'une propri�t� avant + insertion/modification en base de donn�es par le module + QxValidator de la biblioth�que QxOrm. +
      +
    • Champ � Min value � : valeur minimale autoris�e pour une propri�t� de type + num�rique ;
    • +
    • Champ � Max value � : valeur maximale autoris�e pour une propri�t� de type + num�rique ;
    • +
    • Champ � Min length � : longueur minimale autoris�e pour une propri�t� de type + chaine de caract�res ;
    • +
    • Champ � Max length � : longueur maximale autoris�e pour une propri�t� de type + chaine de caract�res ;
    • +
    • Champ � Regular expression � : expression r�guli�re utilis�e pour valider la + valeur d'une propri�t� ;
    • +
    • Champ � Not NULL � : v�rifie que la valeur + de la propri�t� n'est pas NULL ;
    • +
    • Champ � Unique � : valeur unique en base de donn�es : 2 lignes dans la table ne + peuvent avoir la m�me valeur sur la colonne associ�e � cette propri�t�.
    • +
    +
    + -- Section Advanced : +
      +
    • Champ � Accessibility �, valeurs � public, protected ou + private � : une propri�t� public est accessible sans accesseur + Get/Set, une propri�t� protected est accessible uniquement par les + classes d�riv�es, une propri�t� private est accessible uniquement par les + accesseurs Get/Set ; +
    • +
    • Champ � SQL type � : la biblioth�que QxOrm associe automatiquement un type SQL en fonction du + type C++ de la propri�t�, il est possible de surcharger le type SQL par d�faut + avec ce param�tre ;
    • +
    • Champ � SQL alias � : force un alias sur cette colonne utilis� par la + biblioth�que QxOrm lors de la construction des requ�tes SQL ;
    • +
    • Champ � Format � : d�fini le format (syntaxe printf) de la propri�t� + pour le stockage en base de donn�es (peut �tre utile pour g�rer les dates et heures par + exemple) ;
    • +
    • Champ � Get/Set accessors � : si coch�, des accesseurs Get/Set seront + disponibles dans la classe C++ pour acc�der/modifier la valeur de la propri�t� ;
    • +
    • Champ � Read only � : si coch�, un seul accesseur Get est disponible + dans la classe C++, ce qui rend la propri�t� en lecture seule ;
    • +
    • Champ � Get method implementation � : permet de surcharger l'impl�mentation par + d�faut de la m�thode Get dans la classe C++ pour acc�der � la valeur de la + propri�t� ;
    • +
    • Champ � Set method implementation � : permet de surcharger l'impl�mentation par + d�faut de la m�thode Set dans la classe C++ pour modifier la valeur de la + propri�t�.
    • +
    +
    + -- Section List of meta-data : les m�ta-donn�es permettent d'ajouter des + param�tres suppl�mentaires et sont accessibles dans le code C++ (moteur d'introspection de QxOrm) et le moteur Javascript de personnalisation des exports + de QxEntityEditor. +
      +
    • Colonne � Key � : cl� de la m�ta-donn�e ;
    • +
    • Colonne � Value � : valeur de la m�ta-donn�e ;
    • +
    • Bouton � Delete � : supprime la m�ta-donn�e de la liste.
    • +
    +
    +
    +
    +

    Gestion des relations + entre entit�s

    +
    + Une relation entre 2 entit�s (1-n, n-1, 1-1 ou n-n) dans + l'application QxEntityEditor permet de lier 2 tables dans la base de donn�es, et 2 classes dans + le code C++. + L'application QxEntityEditor permet de cr�er, modifier, supprimer et ordonner les relations + d'une entit�. +

    +

    Ajouter/modifier/supprimer une relation

    +
    + L'ajout et la suppression d'une relation se fait depuis l'�cran de + param�trage d'une entit� : la section List of relationships de cet �cran de + param�trage permet d'ajouter et supprimer des relations. + Il est �galement possible d'ordonner cette liste pour positionner les relations dans l'ordre + voulu. +

    + La modification d'une relation est accessible par un bouton qui s'affiche sur le diagramme + d'entit�s lorsque la souris est positionn�e sur une relation : +

    + Relation settings access +

    +
    +

    Param�trage + d�taill� d'une relation

    +
    + Les param�tres d�taill�s d'une relation s�lectionn�e sont accessibles dans l'onglet de + param�trage Relationship : +

    + Relation settings +

    + Cet �cran de param�trage est divis� en plusieurs sections : +

    + -- Section Relationship information (version XX) : la version de la relation + d�pend du niveau d'historique du projet. +
      +
    • Champ � Name � : nom de la relation, et nom de la donn�e membre associ�e dans + le code C++ ;
    • +
    • Champ � Target � : entit� cible ;
    • +
    • Champ � Description � : description de la relation (apparait dans le code C++ + g�n�r�) ;
    • +
    • Champs � 1-n, n-1, 1-1 ou n-n � : type de relation, suivant le type s�lectionn� + certains param�tres sont actifs ou inactifs ;
    • +
    • Champ � Column name � : actif uniquement pour les relations de type n-1, + nom de la colonne dans la base de donn�es (si vide, alors Column name == + Name) ; +
    • +
    • Champ � Decoration � : ajoute une d�coration au type C++ (pour les relations, + la biblioth�que QxOrm recommande l'utilisation de pointeurs intelligents : + boost::shared_ptr, std::shared_ptr ou QSharedPointer) ; +
    • +
    • Champ � Collection � : si le type de relation n�cessite une liste (1-n + et n-n), alors d�fini le type de liste C++ ;
    • +
    • Champ � Foreign key � : cl� �trang�re de la relation : nom d'une propri�t� de + l'entit� cible ;
    • +
    • Champ � FK owner � : utilis� uniquement pour les relations de type n-n ; +
    • +
    • Champ � Extra-table � : utilis� uniquement pour les relations de type + n-n, correspond au nom de la table qui lie les 2 entit�s ; +
    • +
    • Champ � Index � : optimisation des requ�tes SQL de type SELECT pour les + recherches en base de donn�es ;
    • +
    • Champ � Serializable � : si coch�, la relation est prise en compte par le moteur de s�rialisation de la biblioth�que + QxOrm ;
    • +
    • Champ � Obsolete � : cocher cette option pour supprimer une relation sans casser la compatibilit� ascendante du moteur de + s�rialisation de la biblioth�que QxOrm : la relation devient priv�e sans + accesseur Get/Set.
    • +
    +
    + -- Section Advanced : +
      +
    • Champ � Accessibility �, valeurs � public, protected ou + private � : une relation public est accessible sans accesseur + Get/Set, une relation protected est accessible uniquement par les classes + d�riv�es, une relation private est accessible uniquement par les accesseurs + Get/Set ; +
    • +
    • Champ � Get/Set accessors � : si coch�, des accesseurs Get/Set seront + disponibles dans la classe C++ pour acc�der/modifier la valeur de la relation ;
    • +
    • Champ � Read only � : si coch�, un seul accesseur Get est disponible + dans la classe C++, ce qui rend la relation en lecture seule ;
    • +
    • Champ � Get method implementation � : permet de surcharger l'impl�mentation par + d�faut de la m�thode Get dans la classe C++ pour acc�der � la valeur de la + relation ;
    • +
    • Champ � Set method implementation � : permet de surcharger l'impl�mentation par + d�faut de la m�thode Set dans la classe C++ pour modifier la valeur de la + relation.
    • +
    +
    + -- Section List of meta-data : les m�ta-donn�es permettent d'ajouter des + param�tres suppl�mentaires et sont accessibles dans le code C++ (moteur d'introspection de QxOrm) et le moteur Javascript de personnalisation des exports + de QxEntityEditor. +
      +
    • Colonne � Key � : cl� de la m�ta-donn�e ;
    • +
    • Colonne � Value � : valeur de la m�ta-donn�e ;
    • +
    • Bouton � Delete � : supprime la m�ta-donn�e de la liste.
    • +
    +
    +
    +
    +

    Gestion des �num�rations +

    +
    + Une �num�ration dans l'application QxEntityEditor correspond � une liste de valeurs disponibles + c�t� code C++ (pour le moment pas de notion c�t� base de donn�es : converti en valeur + num�rique). + L'application QxEntityEditor permet de cr�er, modifier, supprimer et cloner une �num�ration. +

    +

    Ajouter/modifier/supprimer/cloner une �num�ration

    +
    + L'ajout, la modification, la suppression et le clonage d'une �num�ration se fait de la m�me + fa�on que pour une entit�.
    + Une �num�ration dispose des m�mes menus qu'une entit�. +

    +
    +

    Param�tres + d'une �num�ration

    +
    + Les param�tres de l'�num�ration s�lectionn�e sont accessibles dans l'onglet de param�trage + Enumeration : +

    + Enumeration settings +

    + Cet �cran de param�trage est divis� en plusieurs sections : +

    + -- Section Enumeration information (version XX) : la version de l'�num�ration + est incr�ment�e � chaque historique de projet cr��. +
      +
    • Champ � Name � : nom de l'�num�ration et de la classe C++ g�n�r�e ;
    • +
    • Champ � Description � : description de l'�num�ration (apparait dans le code C++ + g�n�r�) ;
    • +
    • Champ � Namespace � : espace de nom de l'�num�ration et de la classe C++ + associ�e, chaque sous-namespace doit �tre s�par� par ::, par exemple : + ns1::ns2::ns3 ; +
    • +
    • Champ � Use Qt macro Q_ENUM � : utilise la macro Q_ENUM + fournie par Qt pour d�finir une �num�ration compatible avec le moteur + d'introspection de Qt ;
    • +
    +
    + -- Section List of values : liste des valeurs de l'�num�ration. +
      +
    • Colonne � Key � : cl� associ� � la valeur ;
    • +
    • Colonne � Value � : valeur ;
    • +
    • Bouton � Delete � : supprime la valeur de la liste.
    • +
    +
    + -- Section List of meta-data : les m�ta-donn�es permettent d'ajouter des + param�tres suppl�mentaires et sont accessibles dans le code C++ (moteur d'introspection de QxOrm) et le moteur Javascript de personnalisation des exports + de QxEntityEditor. +
      +
    • Colonne � Key � : cl� de la m�ta-donn�e ;
    • +
    • Colonne � Value � : valeur de la m�ta-donn�e ;
    • +
    • Bouton � Delete � : supprime la m�ta-donn�e de la liste.
    • +
    +
    +
    +

    Couleur associ�e � + une �num�ration

    +
    + Les couleurs d'une �num�ration peuvent �tre modifi�es par le menu contextuel sur un + clic-droit sur une �num�ration Define enumeration colors : +

    + Enumeration colors +

    + Remarque : les couleurs des �l�ments du diagramme d'entit�s peuvent �tre g�r�es � + plusieurs niveaux (du niveau le plus global au plus sp�cifique) : + +
    +
    +
    +

    Gestion des espaces de nom + (namespace)

    +
    + Un espace de nom (ou namespace) dans l'application QxEntityEditor permet de regrouper plusieurs + entit�s et �num�rations dans une m�me zone. + C�t� base de donn�es, un espace de nom repr�sente g�n�ralement un sch�ma. + C�t� code C++, un espace de nom regroupe plusieurs classes ou fonctions C++ dans une m�me zone. +

    + La notion d'espace de nom est utilis�e par la fonction : Organisation automatique du diagramme d'entit�s (permet + un regroupement automatique). +

    + La gestion des espaces de nom dans QxEntityEditor se fait depuis l'arborescence du projet : +

    + Namespace menu +

    +

    Renommer un + espace de nom

    +
    + Pour renommer un espace de nom, il faut faire un clic-droit sur l'arborescence du projet, + puis menu contextuel Move entities to another namespace : +

    + Namespace rename +

    +
    +

    Gestion des couleurs d'un espace de nom

    +
    + Pour d�finir les couleurs des �l�ments d'un espace de nom, il faut faire un clic-droit sur + l'arborescence du projet, puis menu contextuel Define colors by namespace : +

    + Namespace colors +

    + La partie gauche de l'�cran liste tous les espaces de nom disponibles et d�finis dans le + projet.
    + La partie droite permet de d�finir les couleurs des �l�ments faisant partie de l'espace de + nom. + Il est �galement possible de d�finir une couleur de fond (de pr�f�rence, une couleur claire), + permettant de regrouper visuellement les �l�ments dans le diagramme d'entit�s. +

    + Remarque : les couleurs des �l�ments du diagramme d'entit�s peuvent �tre g�r�es � + plusieurs niveaux (du niveau le plus global au plus sp�cifique) : + +
    +
    +
    +

    Gestion des notes (ou + post-it)

    +
    + Une note (ou post-it) dans l'application QxEntityEditor permet de coller une �tiquette dans le + diagramme d'entit�s pour fournir des informations sur le mod�le de donn�es ou le projet en + cours. + Cette note peut �tre positionn�e n'importe o� dans le diagramme d'entit�s. + Une note dispose d'un titre et d'un texte libre. Il est possible d'�crire du texte au format + HTML pour ajouter des couleurs, mettre en gras, en italique, etc... +

    + L'application QxEntityEditor permet de cr�er, modifier, supprimer et cloner une note. +

    + Post-it / comments +

    +

    Ajouter/modifier/supprimer/cloner une note

    +
    + L'ajout, la modification, la suppression et le clonage d'une note se fait de la m�me fa�on + que pour une entit�.
    + Une note dispose des m�mes menus qu'une entit� (il suffit de + remplacer le terme entity par comment dans les menus). +

    +
    +

    Couleur associ�e + � une note

    +
    + Les couleurs d'une note peuvent �tre modifi�es par le menu contextuel sur un clic-droit sur + une note Define comment colors : +

    + Post-it colors +

    + Remarque : les couleurs des �l�ments du diagramme d'entit�s peuvent �tre g�r�es � + plusieurs niveaux (du niveau le plus global au plus sp�cifique) : + +
    +
    +
    +

    Organisation + automatique du diagramme d'entit�s

    +
    + Une fonction permet de regrouper automatiquement les �l�ments du diagramme d'entit�s en fonction + des espaces de nom, et en fonction des relations entre entit�s : menu principal View >> + Organize diagram layout. + Cette fonction est particuli�rement utile pour organiser le diagramme d'entit�s apr�s un processus d'import par exemple. +

    + Organize diagram layout +

    +
    +

    Historique d'un + projet *.qxee

    +
    + Un projet QxEntityEditor peut �tre historis� : menu principal Actions >> Tag project + state. +

    + Chaque historique de projet fait �voluer les n� de version des diff�rents �l�ments du projet + (entit�s, propri�t�s, relations, etc...). + Ces n� de version sont utiles pour assurer une compatibilit� + ascendante avec le moteur de s�rialisation de la biblioth�que QxOrm. + L'historique de projet est �galement utilis� par l'export DDL de + sch�ma de base de donn�es : permet de suivre l'�volution du sch�ma de base de donn�es + (ajout d'une table, suppression d'une colonne, ajout d'un index, etc...). +

    + Tag project state tab 1 +

    + Le 1er onglet de l'�cran d'historique permet de poser une �tiquette (par exemple : date/heure, + num�ro de version du projet, etc...) et un commentaire en texte libre. +

    + Tag project state tab 2 +

    + Le 2�me onglet de l'�cran d'historique liste tous les historiques du projet, et permet de + r�cup�rer l'�tat d'un projet � un instant donn� sous plusieurs formats (XML, JSON ou bien + fichier projet *.qxee). +

    +
    +

    Aper�u du code C++ + d'une entit�/�num�ration

    +
    + Il est possible de pr�visualiser � tout moment le code C++ (fichier header *.h et fichier + source *.cpp) associ� � une entit� et une �num�ration (ce code C++ peut �tre g�n�r� par + le processus d'export C++). + Cette pr�visualisation est accessible en s�lectionnant un �l�ment dans le diagramme d'entit�s, + puis aller dans l'onglet C++ preview : +

    + C++ preview +

    +
    +

    Convention de + nommage (snake_case, camelCase)

    +
    + L'application QxEntityEditor fournit une fonction permettant d'appliquer rapidement une + convention de nommage sur tous les �l�ments du projet (entit�s, propri�t�s, relations, etc...). + Cette fonction conserve (ne casse pas) le mapping vers la base de donn�es. L'acc�s � cette + fonction se fait par le menu principal : Naming convention. + 3 styles de convention de nommage sont propos�s : snake_case, camelCase et + PascalCase. +

    + Naming convention +

    + Remarque : cette fonction peut �tre utile suite � un processus + d'import pour harmoniser le code C++ g�n�r�. +

    +
    +

    Liste des plugins + disponibles

    +
    + L'application QxEntityEditor est bas�e sur un syst�me de plugins pour g�rer les processus d'import/export. + Une liste des plugins disponibles avec les n� de version et une description de chaque plugin est + accessible depuis le menu principal : Help >> About plugins. +

    + List of plugins +

    +
    +
    + +

    Plugins d'import

    +
    + Tous les processus d'import de l'application QxEntityEditor sont accessibles depuis le menu + principal Import : +

    + Import plugins +

    +

    Importer + un projet � partir d'un r�pertoire associ� � un gestionnaire de code source (Git, Perforce, + CVS, etc.)

    +
    + Un projet QxEntityEditor (fichier *.qxee) peut �tre g�r� par une �quipe de d�veloppeurs : + le code source d'un projet QxEntityEditor peut �tre export�/import� (manuellement ou en ligne de + commande) avec les plugins QxEESourceControlExport et QxEESourceControlImport.
    +
    + Associ� � un gestionnaire de code source (Git, Perforce, CVS, etc.), ces 2 plugins permettent + ainsi : +
      +
    • plusieurs personnes peuvent travailler simultan�ment sur un m�me projet ;
    • +
    • un projet peut facilement �tre versionn� dans le gestionnaire de code source ;
    • +
    • chaque �l�ment d'un projet peut �tre compar� (diff�rence entre 2 versions d'une m�me + entit� par exemple).
    • +
    + Voici un exemple d'utilisation des plugins QxEESourceControlExport et QxEESourceControlImport, avec 2 d�veloppeurs + nomm�s 'dev A' et 'dev B' (l'exemple pouvant �tre �tendu � X d�veloppeurs) : +
      +
    • dev A et dev B travaillent sur un m�me projet QxEntityEditor (fichier *.qxee) ; +
    • +
    • dev A cr��/modifie/supprime certaines entit�s dans l'application QxEntityEditor ;
    • +
    • dev B cr��/modifie/supprime d'autres entit�s dans l'application QxEntityEditor ;
    • +
    • dev A et dev B exportent le projet *.qxee en utilisant le plugin QxEESourceControlExport (manuellement + ou en ligne de commande) ;
    • +
    • une fois le projet export� dans un r�pertoire, dev A et dev B enregistrent + (check-in ou submit) les fichiers JSON g�n�r�s dans le gestionnaire de code + source (Git, Perforce, CVS, etc.) sur leur propre branche de travail ;
    • +
    • depuis le gestionnaire de code source, dev A et dev B int�grent (merge ou + integrate) leur branche de travail sur la branche de d�veloppement DEV (ou MAIN ou + MASTER ou LATEST); Remarque : m�me si il y a des conflits � r�soudre, ils seront + simples � corriger car le format JSON est lisible facilement ; +
    • +
    • dev A et dev B peuvent � pr�sent obtenir la derni�re version des fichiers JSON (get + latest depuis le gestionnaire de code source) � partir de la branche DEV (ou MAIN + ou MASTER ou LATEST) : les fichiers JSON obtenus contiennent � la fois les + modifications de dev A et dev B ;
    • +
    • dev A et dev B peuvent importer ces fichiers JSON dans l'application QxEntityEditor en + utilisant le plugin d'import QxEESourceControlImport (manuellement ou + en ligne de commande).
    • +
    + Les param�tres du plugin d'import sont accessibles depuis le menu principal Import >> + Import from Source Control repository (Git, Perforce, CVS, etc.) : +

    + Source Control import plugin +

    + Le param�tre � renseigner correspond au fichier JSON pr�sent � la racine du r�pertoire o� a �t� export� le projet + *.qxee.
    + Ce fichier est nomm� <nom_du_projet>.qxee.export.json, par exemple pour le + projet de test qxBlog : qxBlog.qxee.export.json. +

    + Source Control output directory +

    + Attention : un import efface tous les �l�ments du projet courant (entit�s, propri�t�s, + relations, etc...). +

    + Remarque : il est possible de d�marrer QxEntityEditor en ligne de commande pour charger + automatiquement un r�pertoire associ� � un gestionnaire de code source au d�marrage de + l'application. +

    + Exemple de ligne de commande :
    +
    + +
    QxEntityEditor --project="<path_to_your_qxee_project_file>" --plugin=QxEESourceControlImport --QxEESourceControlImport_path="<path_to_the_root_source_control_json_file>"
    +
    +
    +

    +
    +

    Importer un projet � + partir d'un fichier texte au format JSON

    +
    + + Remarque : + pour travailler avec un gestionnaire de code source (Git, Perforce, CVS, etc.), il est + conseill� d'utiliser les plugins QxEESourceControlExport et QxEESourceControlImport (au lieu de cr�er un + fichier unique).
    +
    + Il est possible de g�rer un projet QxEntityEditor avec un fichier texte au format JSON : menu + principal Import >> Import from JSON file.
    + Pour connaitre la structure du fichier JSON � respecter, il est conseill� au pr�alable + d'effectuer un 1er export de projet au format JSON. +

    + Import JSON +

    + L'�cran d'import dispose d'un seul champ � renseigner : le fichier JSON � importer. +

    + Attention : un import JSON efface tous les �l�ments du projet courant (entit�s, + propri�t�s, relations, etc...). +

    + Remarque : il est possible de d�marrer QxEntityEditor en ligne de commande pour charger + automatiquement un fichier JSON au d�marrage de l'application. +

    + Exemple de ligne de commande :
    +
    + +
    QxEntityEditor --project="<path_to_your_qxee_project_file>" --plugin=QxEEJsonImport --QxEEJsonImport_file="<path_to_your_json_file>"
    +
    +
    +
    + Exemple de fichier JSON (pr�sent dans le dossier ./samples/qxBlog.json du package + QxEntityEditor) :
    +
    +
    +{
    +    "app_version": 0,
    +    "description": "",
    +    "dt_creation": "20131206221737",
    +    "dt_modification": "20131206221737",
    +    "id": 1,
    +    "list_all_entities": [
    +        {
    +            "app_version": 0,
    +            "description": "",
    +            "dt_creation": "20131206221810",
    +            "dt_modification": "20140617214144",
    +            "id": 1,
    +            "is_abstract": false,
    +            "is_read_only": false,
    +            "key": "",
    +            "list_functions": null,
    +            "list_functions_static": null,
    +            "list_meta_data": null,
    +            "list_properties": [
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": false,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221810",
    +                    "dt_modification": "20140617214144",
    +                    "entity_id": {
    +                        "id": 1
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 1,
    +                    "is_index": true,
    +                    "is_obsolete": false,
    +                    "is_primary_key": true,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "author_id",
    +                    "order_level": 0,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221810",
    +                        "dt_modification": "20140617214144",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 1,
    +                        "list_meta_data": null,
    +                        "primitive_type": "long",
    +                        "property_id": {
    +                            "id": 1
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221810",
    +                    "dt_modification": "20140617214144",
    +                    "entity_id": {
    +                        "id": 1
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 2,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "firstname",
    +                    "order_level": 1,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221810",
    +                        "dt_modification": "20140617214144",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 2,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QString",
    +                        "property_id": {
    +                            "id": 2
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221810",
    +                    "dt_modification": "20140617214144",
    +                    "entity_id": {
    +                        "id": 1
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 3,
    +                    "is_index": true,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "lastname",
    +                    "order_level": 2,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221810",
    +                        "dt_modification": "20140617214144",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 3,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QString",
    +                        "property_id": {
    +                            "id": 3
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221810",
    +                    "dt_modification": "20140617214144",
    +                    "entity_id": {
    +                        "id": 1
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 4,
    +                    "is_index": true,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "birthdate",
    +                    "order_level": 3,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221810",
    +                        "dt_modification": "20140617214144",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 4,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QDateTime",
    +                        "property_id": {
    +                            "id": 4
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221810",
    +                    "dt_modification": "20140617214144",
    +                    "entity_id": {
    +                        "id": 1
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 5,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "sex",
    +                    "order_level": 4,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221810",
    +                        "dt_modification": "20140617214144",
    +                        "entity_id": null,
    +                        "enumeration_id": {
    +                            "id": 1
    +                        },
    +                        "id": 5,
    +                        "list_meta_data": null,
    +                        "primitive_type": "sex::enum_sex",
    +                        "property_id": {
    +                            "id": 5
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20140617214144",
    +                    "entity_id": {
    +                        "id": 1
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 18,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "list_of_blog",
    +                    "order_level": 17,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "qx::QxCollection",
    +                        "decoration": "boost::shared_ptr",
    +                        "default_value": "",
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214144",
    +                        "entity_id": {
    +                            "id": 4
    +                        },
    +                        "enumeration_id": null,
    +                        "id": 18,
    +                        "list_meta_data": null,
    +                        "primitive_type": "",
    +                        "property_id": {
    +                            "id": 18
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": {
    +                        "app_version": 0,
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214144",
    +                        "extra_table": "",
    +                        "foreign_key": "author",
    +                        "foreign_key_owner": "",
    +                        "id": 2,
    +                        "inverse_property_id": {
    +                            "id": 15
    +                        },
    +                        "list_meta_data": null,
    +                        "property_id": {
    +                            "id": 18
    +                        },
    +                        "type_relation": "one-to-many",
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                }
    +            ],
    +            "name": "author",
    +            "namespace": "",
    +            "parent_id": null,
    +            "project_id": {
    +                "id": 1
    +            },
    +            "project_version": 0,
    +            "property_id": {
    +                "accessibility": "protected",
    +                "allow_null": false,
    +                "app_version": 0,
    +                "column_name": "",
    +                "description": "",
    +                "dt_creation": "20131206221810",
    +                "dt_modification": "20140617214144",
    +                "entity_id": {
    +                    "id": 1
    +                },
    +                "force_sql_alias": "",
    +                "force_sql_type": "",
    +                "format": "",
    +                "get_method": "",
    +                "id": 1,
    +                "is_index": true,
    +                "is_obsolete": false,
    +                "is_primary_key": true,
    +                "is_read_only": false,
    +                "is_serializable": true,
    +                "is_transient": false,
    +                "is_unique": false,
    +                "key": "",
    +                "list_meta_data": null,
    +                "max_length": "",
    +                "max_value": "",
    +                "min_length": "",
    +                "min_value": "",
    +                "name": "author_id",
    +                "order_level": 0,
    +                "project_version": 0,
    +                "property_type_id": {
    +                    "app_version": 0,
    +                    "collection": "",
    +                    "decoration": "",
    +                    "default_value": "",
    +                    "dt_creation": "20131206221810",
    +                    "dt_modification": "20140617214144",
    +                    "entity_id": null,
    +                    "enumeration_id": null,
    +                    "id": 1,
    +                    "list_meta_data": null,
    +                    "primitive_type": "long",
    +                    "property_id": {
    +                        "id": 1
    +                    },
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0
    +                },
    +                "qt_property": "",
    +                "reg_exp": "",
    +                "relation_id": null,
    +                "set_method": "",
    +                "user_id_creation": 0,
    +                "user_id_modification": 0,
    +                "version": 0,
    +                "write_accessors": true
    +            },
    +            "soft_delete_column": "",
    +            "table_name": "t_author",
    +            "trigger_after_delete": false,
    +            "trigger_after_fetch": false,
    +            "trigger_after_insert": false,
    +            "trigger_after_update": false,
    +            "trigger_before_delete": false,
    +            "trigger_before_fetch": false,
    +            "trigger_before_insert": false,
    +            "trigger_before_update": false,
    +            "user_id_creation": 0,
    +            "user_id_modification": 0,
    +            "validator_method": "",
    +            "version": 0
    +        },
    +        {
    +            "app_version": 0,
    +            "description": "",
    +            "dt_creation": "20131206222031",
    +            "dt_modification": "20140617214043",
    +            "id": 4,
    +            "is_abstract": false,
    +            "is_read_only": false,
    +            "key": "",
    +            "list_functions": null,
    +            "list_functions_static": null,
    +            "list_meta_data": null,
    +            "list_properties": [
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": false,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20140617214043",
    +                    "entity_id": {
    +                        "id": 4
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 12,
    +                    "is_index": true,
    +                    "is_obsolete": false,
    +                    "is_primary_key": true,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "blog_id",
    +                    "order_level": 11,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214043",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 12,
    +                        "list_meta_data": null,
    +                        "primitive_type": "long",
    +                        "property_id": {
    +                            "id": 12
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": false,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20140617214043",
    +                    "entity_id": {
    +                        "id": 4
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 13,
    +                    "is_index": true,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "title",
    +                    "order_level": 12,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214043",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 13,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QString",
    +                        "property_id": {
    +                            "id": 13
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20140617214043",
    +                    "entity_id": {
    +                        "id": 4
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 14,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "text",
    +                    "order_level": 13,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214043",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 14,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QString",
    +                        "property_id": {
    +                            "id": 14
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20140617214043",
    +                    "entity_id": {
    +                        "id": 4
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 15,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "author",
    +                    "order_level": 14,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "qx::QxCollection",
    +                        "decoration": "boost::shared_ptr",
    +                        "default_value": "",
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214043",
    +                        "entity_id": {
    +                            "id": 1
    +                        },
    +                        "enumeration_id": null,
    +                        "id": 15,
    +                        "list_meta_data": null,
    +                        "primitive_type": "",
    +                        "property_id": {
    +                            "id": 15
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": {
    +                        "app_version": 0,
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214043",
    +                        "extra_table": "",
    +                        "foreign_key": "",
    +                        "foreign_key_owner": "",
    +                        "id": 1,
    +                        "inverse_property_id": {
    +                            "id": 18
    +                        },
    +                        "list_meta_data": null,
    +                        "property_id": {
    +                            "id": 15
    +                        },
    +                        "type_relation": "many-to-one",
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20140617214043",
    +                    "entity_id": {
    +                        "id": 4
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 16,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "list_of_comment",
    +                    "order_level": 15,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "qx::QxCollection",
    +                        "decoration": "boost::shared_ptr",
    +                        "default_value": "",
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214043",
    +                        "entity_id": {
    +                            "id": 3
    +                        },
    +                        "enumeration_id": null,
    +                        "id": 16,
    +                        "list_meta_data": null,
    +                        "primitive_type": "",
    +                        "property_id": {
    +                            "id": 16
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": {
    +                        "app_version": 0,
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214043",
    +                        "extra_table": "",
    +                        "foreign_key": "blog_id",
    +                        "foreign_key_owner": "",
    +                        "id": 3,
    +                        "inverse_property_id": {
    +                            "id": 19
    +                        },
    +                        "list_meta_data": null,
    +                        "property_id": {
    +                            "id": 16
    +                        },
    +                        "type_relation": "one-to-many",
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20140617214043",
    +                    "entity_id": {
    +                        "id": 4
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 17,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "list_of_category",
    +                    "order_level": 16,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "qx::QxCollection",
    +                        "decoration": "boost::shared_ptr",
    +                        "default_value": "",
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214043",
    +                        "entity_id": {
    +                            "id": 2
    +                        },
    +                        "enumeration_id": null,
    +                        "id": 17,
    +                        "list_meta_data": null,
    +                        "primitive_type": "",
    +                        "property_id": {
    +                            "id": 17
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": {
    +                        "app_version": 0,
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214043",
    +                        "extra_table": "t_qxee_blog_category",
    +                        "foreign_key": "blog_id",
    +                        "foreign_key_owner": "category_id",
    +                        "id": 5,
    +                        "inverse_property_id": {
    +                            "id": 20
    +                        },
    +                        "list_meta_data": null,
    +                        "property_id": {
    +                            "id": 17
    +                        },
    +                        "type_relation": "many-to-many",
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                }
    +            ],
    +            "name": "blog",
    +            "namespace": "",
    +            "parent_id": null,
    +            "project_id": {
    +                "id": 1
    +            },
    +            "project_version": 0,
    +            "property_id": {
    +                "accessibility": "protected",
    +                "allow_null": false,
    +                "app_version": 0,
    +                "column_name": "",
    +                "description": "",
    +                "dt_creation": "20131206222031",
    +                "dt_modification": "20140617214043",
    +                "entity_id": {
    +                    "id": 4
    +                },
    +                "force_sql_alias": "",
    +                "force_sql_type": "",
    +                "format": "",
    +                "get_method": "",
    +                "id": 12,
    +                "is_index": true,
    +                "is_obsolete": false,
    +                "is_primary_key": true,
    +                "is_read_only": false,
    +                "is_serializable": true,
    +                "is_transient": false,
    +                "is_unique": false,
    +                "key": "",
    +                "list_meta_data": null,
    +                "max_length": "",
    +                "max_value": "",
    +                "min_length": "",
    +                "min_value": "",
    +                "name": "blog_id",
    +                "order_level": 11,
    +                "project_version": 0,
    +                "property_type_id": {
    +                    "app_version": 0,
    +                    "collection": "",
    +                    "decoration": "",
    +                    "default_value": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20140617214043",
    +                    "entity_id": null,
    +                    "enumeration_id": null,
    +                    "id": 12,
    +                    "list_meta_data": null,
    +                    "primitive_type": "long",
    +                    "property_id": {
    +                        "id": 12
    +                    },
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0
    +                },
    +                "qt_property": "",
    +                "reg_exp": "",
    +                "relation_id": null,
    +                "set_method": "",
    +                "user_id_creation": 0,
    +                "user_id_modification": 0,
    +                "version": 0,
    +                "write_accessors": true
    +            },
    +            "soft_delete_column": "",
    +            "table_name": "t_blog",
    +            "trigger_after_delete": false,
    +            "trigger_after_fetch": false,
    +            "trigger_after_insert": false,
    +            "trigger_after_update": false,
    +            "trigger_before_delete": false,
    +            "trigger_before_fetch": false,
    +            "trigger_before_insert": false,
    +            "trigger_before_update": false,
    +            "user_id_creation": 0,
    +            "user_id_modification": 0,
    +            "validator_method": "",
    +            "version": 0
    +        },
    +        {
    +            "app_version": 0,
    +            "description": "",
    +            "dt_creation": "20131206221933",
    +            "dt_modification": "20140617214115",
    +            "id": 2,
    +            "is_abstract": false,
    +            "is_read_only": false,
    +            "key": "",
    +            "list_functions": null,
    +            "list_functions_static": null,
    +            "list_meta_data": null,
    +            "list_properties": [
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": false,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221933",
    +                    "dt_modification": "20140617214115",
    +                    "entity_id": {
    +                        "id": 2
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 6,
    +                    "is_index": true,
    +                    "is_obsolete": false,
    +                    "is_primary_key": true,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "category_id",
    +                    "order_level": 5,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221933",
    +                        "dt_modification": "20140617214115",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 6,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QString",
    +                        "property_id": {
    +                            "id": 6
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221933",
    +                    "dt_modification": "20140617214115",
    +                    "entity_id": {
    +                        "id": 2
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 7,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "name",
    +                    "order_level": 6,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221933",
    +                        "dt_modification": "20140617214115",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 7,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QString",
    +                        "property_id": {
    +                            "id": 7
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221933",
    +                    "dt_modification": "20140617214115",
    +                    "entity_id": {
    +                        "id": 2
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 8,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "description",
    +                    "order_level": 7,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221933",
    +                        "dt_modification": "20140617214115",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 8,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QString",
    +                        "property_id": {
    +                            "id": 8
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20140617214115",
    +                    "entity_id": {
    +                        "id": 2
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 20,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "list_of_blog",
    +                    "order_level": 19,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "qx::QxCollection",
    +                        "decoration": "boost::shared_ptr",
    +                        "default_value": "",
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214115",
    +                        "entity_id": {
    +                            "id": 4
    +                        },
    +                        "enumeration_id": null,
    +                        "id": 20,
    +                        "list_meta_data": null,
    +                        "primitive_type": "",
    +                        "property_id": {
    +                            "id": 20
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": {
    +                        "app_version": 0,
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20140617214115",
    +                        "extra_table": "t_qxee_blog_category",
    +                        "foreign_key": "category_id",
    +                        "foreign_key_owner": "blog_id",
    +                        "id": 6,
    +                        "inverse_property_id": {
    +                            "id": 17
    +                        },
    +                        "list_meta_data": null,
    +                        "property_id": {
    +                            "id": 20
    +                        },
    +                        "type_relation": "many-to-many",
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                }
    +            ],
    +            "name": "category",
    +            "namespace": "",
    +            "parent_id": null,
    +            "project_id": {
    +                "id": 1
    +            },
    +            "project_version": 0,
    +            "property_id": {
    +                "accessibility": "protected",
    +                "allow_null": false,
    +                "app_version": 0,
    +                "column_name": "",
    +                "description": "",
    +                "dt_creation": "20131206221933",
    +                "dt_modification": "20140617214115",
    +                "entity_id": {
    +                    "id": 2
    +                },
    +                "force_sql_alias": "",
    +                "force_sql_type": "",
    +                "format": "",
    +                "get_method": "",
    +                "id": 6,
    +                "is_index": true,
    +                "is_obsolete": false,
    +                "is_primary_key": true,
    +                "is_read_only": false,
    +                "is_serializable": true,
    +                "is_transient": false,
    +                "is_unique": false,
    +                "key": "",
    +                "list_meta_data": null,
    +                "max_length": "",
    +                "max_value": "",
    +                "min_length": "",
    +                "min_value": "",
    +                "name": "category_id",
    +                "order_level": 5,
    +                "project_version": 0,
    +                "property_type_id": {
    +                    "app_version": 0,
    +                    "collection": "",
    +                    "decoration": "",
    +                    "default_value": "",
    +                    "dt_creation": "20131206221933",
    +                    "dt_modification": "20140617214115",
    +                    "entity_id": null,
    +                    "enumeration_id": null,
    +                    "id": 6,
    +                    "list_meta_data": null,
    +                    "primitive_type": "QString",
    +                    "property_id": {
    +                        "id": 6
    +                    },
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0
    +                },
    +                "qt_property": "",
    +                "reg_exp": "",
    +                "relation_id": null,
    +                "set_method": "",
    +                "user_id_creation": 0,
    +                "user_id_modification": 0,
    +                "version": 0,
    +                "write_accessors": true
    +            },
    +            "soft_delete_column": "",
    +            "table_name": "t_category",
    +            "trigger_after_delete": false,
    +            "trigger_after_fetch": false,
    +            "trigger_after_insert": false,
    +            "trigger_after_update": false,
    +            "trigger_before_delete": false,
    +            "trigger_before_fetch": false,
    +            "trigger_before_insert": false,
    +            "trigger_before_update": false,
    +            "user_id_creation": 0,
    +            "user_id_modification": 0,
    +            "validator_method": "",
    +            "version": 0
    +        },
    +        {
    +            "app_version": 0,
    +            "description": "",
    +            "dt_creation": "20131206221951",
    +            "dt_modification": "20131206221951",
    +            "id": 3,
    +            "is_abstract": false,
    +            "is_read_only": false,
    +            "key": "",
    +            "list_functions": null,
    +            "list_functions_static": null,
    +            "list_meta_data": null,
    +            "list_properties": [
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": false,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221951",
    +                    "dt_modification": "20131206221951",
    +                    "entity_id": {
    +                        "id": 3
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 9,
    +                    "is_index": true,
    +                    "is_obsolete": false,
    +                    "is_primary_key": true,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "comment_id",
    +                    "order_level": 8,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221951",
    +                        "dt_modification": "20131206221951",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 9,
    +                        "list_meta_data": null,
    +                        "primitive_type": "long",
    +                        "property_id": {
    +                            "id": 9
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221951",
    +                    "dt_modification": "20131206221951",
    +                    "entity_id": {
    +                        "id": 3
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 10,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "title",
    +                    "order_level": 9,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221951",
    +                        "dt_modification": "20131206221951",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 10,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QString",
    +                        "property_id": {
    +                            "id": 10
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206221951",
    +                    "dt_modification": "20131206221951",
    +                    "entity_id": {
    +                        "id": 3
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 11,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "text",
    +                    "order_level": 10,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "",
    +                        "decoration": "",
    +                        "default_value": "",
    +                        "dt_creation": "20131206221951",
    +                        "dt_modification": "20131206221951",
    +                        "entity_id": null,
    +                        "enumeration_id": null,
    +                        "id": 11,
    +                        "list_meta_data": null,
    +                        "primitive_type": "QString",
    +                        "property_id": {
    +                            "id": 11
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": null,
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                },
    +                {
    +                    "accessibility": "protected",
    +                    "allow_null": true,
    +                    "app_version": 0,
    +                    "column_name": "",
    +                    "description": "",
    +                    "dt_creation": "20131206222031",
    +                    "dt_modification": "20131206222031",
    +                    "entity_id": {
    +                        "id": 3
    +                    },
    +                    "force_sql_alias": "",
    +                    "force_sql_type": "",
    +                    "format": "",
    +                    "get_method": "",
    +                    "id": 19,
    +                    "is_index": false,
    +                    "is_obsolete": false,
    +                    "is_primary_key": false,
    +                    "is_read_only": false,
    +                    "is_serializable": true,
    +                    "is_transient": false,
    +                    "is_unique": false,
    +                    "key": "",
    +                    "list_meta_data": null,
    +                    "max_length": "",
    +                    "max_value": "",
    +                    "min_length": "",
    +                    "min_value": "",
    +                    "name": "blog_id",
    +                    "order_level": 18,
    +                    "project_version": 0,
    +                    "property_type_id": {
    +                        "app_version": 0,
    +                        "collection": "qx::QxCollection",
    +                        "decoration": "boost::shared_ptr",
    +                        "default_value": "",
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20131206222031",
    +                        "entity_id": {
    +                            "id": 4
    +                        },
    +                        "enumeration_id": null,
    +                        "id": 19,
    +                        "list_meta_data": null,
    +                        "primitive_type": "",
    +                        "property_id": {
    +                            "id": 19
    +                        },
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "qt_property": "",
    +                    "reg_exp": "",
    +                    "relation_id": {
    +                        "app_version": 0,
    +                        "dt_creation": "20131206222031",
    +                        "dt_modification": "20131206222031",
    +                        "extra_table": "",
    +                        "foreign_key": "",
    +                        "foreign_key_owner": "",
    +                        "id": 4,
    +                        "inverse_property_id": {
    +                            "id": 16
    +                        },
    +                        "list_meta_data": null,
    +                        "property_id": {
    +                            "id": 19
    +                        },
    +                        "type_relation": "many-to-one",
    +                        "user_id_creation": 0,
    +                        "user_id_modification": 0
    +                    },
    +                    "set_method": "",
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0,
    +                    "version": 0,
    +                    "write_accessors": true
    +                }
    +            ],
    +            "name": "comment",
    +            "namespace": "",
    +            "parent_id": null,
    +            "project_id": {
    +                "id": 1
    +            },
    +            "project_version": 0,
    +            "property_id": {
    +                "accessibility": "protected",
    +                "allow_null": false,
    +                "app_version": 0,
    +                "column_name": "",
    +                "description": "",
    +                "dt_creation": "20131206221951",
    +                "dt_modification": "20131206221951",
    +                "entity_id": {
    +                    "id": 3
    +                },
    +                "force_sql_alias": "",
    +                "force_sql_type": "",
    +                "format": "",
    +                "get_method": "",
    +                "id": 9,
    +                "is_index": true,
    +                "is_obsolete": false,
    +                "is_primary_key": true,
    +                "is_read_only": false,
    +                "is_serializable": true,
    +                "is_transient": false,
    +                "is_unique": false,
    +                "key": "",
    +                "list_meta_data": null,
    +                "max_length": "",
    +                "max_value": "",
    +                "min_length": "",
    +                "min_value": "",
    +                "name": "comment_id",
    +                "order_level": 8,
    +                "project_version": 0,
    +                "property_type_id": {
    +                    "app_version": 0,
    +                    "collection": "",
    +                    "decoration": "",
    +                    "default_value": "",
    +                    "dt_creation": "20131206221951",
    +                    "dt_modification": "20131206221951",
    +                    "entity_id": null,
    +                    "enumeration_id": null,
    +                    "id": 9,
    +                    "list_meta_data": null,
    +                    "primitive_type": "long",
    +                    "property_id": {
    +                        "id": 9
    +                    },
    +                    "user_id_creation": 0,
    +                    "user_id_modification": 0
    +                },
    +                "qt_property": "",
    +                "reg_exp": "",
    +                "relation_id": null,
    +                "set_method": "",
    +                "user_id_creation": 0,
    +                "user_id_modification": 0,
    +                "version": 0,
    +                "write_accessors": true
    +            },
    +            "soft_delete_column": "",
    +            "table_name": "t_comment",
    +            "trigger_after_delete": false,
    +            "trigger_after_fetch": false,
    +            "trigger_after_insert": false,
    +            "trigger_after_update": false,
    +            "trigger_before_delete": false,
    +            "trigger_before_fetch": false,
    +            "trigger_before_insert": false,
    +            "trigger_before_update": false,
    +            "user_id_creation": 0,
    +            "user_id_modification": 0,
    +            "validator_method": "",
    +            "version": 0
    +        }
    +    ],
    +    "list_comments": [
    +        {
    +            "app_version": 0,
    +            "comment_text": "- Design the entity model in few minutes\n\n- Generate C++ persistent classes automatically\n\n- Generate DDL SQL script automatically\n\n- Manage schema evolution for each project version\n\n- Compile C++ native code everywhere : Windows, Linux, MacOSX, Android, iOS, etc...\n\n- Transfer your data model over network and create quickly client/server applications",
    +            "comment_text_html": false,
    +            "comment_title": "qxBlog project",
    +            "dt_creation": "20131206222130",
    +            "dt_modification": "20140107093142",
    +            "id": 1,
    +            "list_meta_data": null,
    +            "user_id_creation": 0,
    +            "user_id_modification": 0
    +        }
    +    ],
    +    "list_enumerations": [
    +        {
    +            "app_version": 0,
    +            "description": "",
    +            "dt_creation": "20131206221840",
    +            "dt_modification": "20131210205552",
    +            "id": 1,
    +            "key": "",
    +            "list_meta_data": null,
    +            "list_of_key_value": {
    +                "female": 2,
    +                "male": 1,
    +                "unknown": 3
    +            },
    +            "name": "sex",
    +            "namespace": "",
    +            "project_id": {
    +                "id": 1
    +            },
    +            "project_version": 0,
    +            "qt_enum": false,
    +            "user_id_creation": 0,
    +            "user_id_modification": 0,
    +            "version": 0
    +        }
    +    ],
    +    "list_groups": null,
    +    "list_meta_data": null,
    +    "list_namespaces": null,
    +    "location": "C:/Temp/qxee",
    +    "name": "qxBlog",
    +    "project_guid": "{16335d56-73ac-48cf-8fcd-f74cc7d97201}",
    +    "project_parameters": {
    +        "app_version": 0,
    +        "default_entity_namespace": "",
    +        "default_primary_key_type": "long",
    +        "default_property_type": "QString",
    +        "dt_creation": "20131206221737",
    +        "dt_modification": "20131206221737",
    +        "id": 1,
    +        "list_meta_data": null,
    +        "lst_plugin_script": {
    +        },
    +        "primary_key_prefix": "",
    +        "primary_key_suffix": "_id",
    +        "project_id": {
    +            "id": 1
    +        },
    +        "table_name_prefix": "t_",
    +        "table_name_suffix": "",
    +        "user_id_creation": 0,
    +        "user_id_modification": 0
    +    },
    +    "user_id_creation": 0,
    +    "user_id_modification": 0,
    +    "version": 0
    +}
    +
    +
    +

    +
    +

    Importer � partir + d'une base de donn�es MySQL ou MariaDB

    +
    + L'import d'une base de donn�es MySQL (ou MariaDB) dans l'application QxEntityEditor se fait par + le menu principal : Import >> Import from MySQL (or MariaDB) database : +

    + Import MySQL or MariaDB +

    + Cet �cran d'import est divis� en plusieurs sections : +

    + -- Section MySQL (or MariaDB) database connection : +
      +
    • Champ � Database server address (IP) � : adresse IP ou nom du serveur MySQL ;
    • +
    • Champ � Database port � : n� de port pour se connecter � la base de donn�es MySQL + ;
    • +
    • Champ � Database name � : nom de la base de donn�es qu'on souhaite importer ;
    • +
    • Champ � Database login � : nom d'utilisateur pour se connecter � la base de + donn�es ;
    • +
    • Champ � Database password � : mot de passe pour se connecter � la base de donn�es + ;
    • +
    • Bouton � Connect to database � : d�marre la connexion � la base de donn�es : si + une erreur se produit, un message d'avertissement est affich�, sinon la liste des tables + de la base de donn�es apparait dans la section � Database items to import to + QxEntityEditor project �.
    • +
    +
    + -- Section Import settings : +
      +
    • Champ � Namespace � : espace de nom utilis� pour regrouper + les entit�s import�es ;
    • +
    • Champ � Delete all entities in the namespace before importing � : supprime les + entit�s de l'espace de nom ci-dessus avant de d�marrer l'import des tables de la base de + donn�es ;
    • +
    • Champ � Import tables/columns comment to entities/properties description � : + importe les commentaires des tables et colonnes d�finis dans la base de donn�es ;
    • +
    • Champ � Add boost::optional<T> decoration if a column definition allows NULL + value � : ajoute automatiquement la d�coration + boost::optional pour g�rer la valeur NULL ;
    • +
    • Champ � Import columns default value � : importe les valeurs par d�faut d�finies + au niveau des colonnes de la base de donn�es ;
    • +
    • Champ � Organize diagram layout after import process � : d�marre automatiquement + une r�organisation du diagramme d'entit�s apr�s le + processus d'import. +
    • +
    +
    + -- Section Relationship import settings : +
      +
    • Champ � Decoration � : d�coration par d�faut du type C++ (pour les relations, la + biblioth�que QxOrm recommande l'utilisation de pointeurs intelligents : + boost::shared_ptr, std::shared_ptr ou QSharedPointer) ; +
    • +
    • Champ � Collection � : si le type de relation n�cessite une liste (1-n et + n-n), alors d�fini le type de liste C++ par d�faut ; +
    • +
    +
    + -- Section Mapping database SQL type to C++ type : QxEntityEditor d�finit un + mapping par d�faut pour associer un type SQL � un type C++. + Il est possible d'utiliser cette liste pour surcharger le comportement par d�faut : +
      +
    • Colonne � SQL type � : type SQL � mapper. La recherche du type SQL n'est pas + sensible � la casse, et s'effectue sur les 1er caract�res : par exemple, mettre + varchar va mapper VARCHAR(255) ; +
    • +
    • Colonne � C++ type � : type C++ correspondant au type SQL ;
    • +
    • Bouton � Supprimer � : supprime une association type SQL / type C++.
    • +
    +
    + -- Section Database items to import to QxEntityEditor project : lorsque + QxEntityEditor est connect� � la base de donn�es, la liste de tous les �l�ments (tables, + colonnes, cl�s primaires, relations, vues, etc...) pouvant �tre import�s dans le projet + QxEntityEditor sont affich�s dans cette section. + Par d�faut, aucun �l�ment n'est s�lectionn�. + Il est possible de tout s�lectionner en 1 clic en cochant l'�l�ment racine de l'arborescence. + Il est �galement possible de s�lectionner uniquement les �l�ments appartenant � un sch�ma. + Une fois les �l�ments s�lectionn�s, le bouton � Ok � devient actif pour d�marrer le + processus d'import. +


    + Remarque : tous les param�tres d'import (connexion � la base de donn�es, param�tres + d'import et des relations, liste mapping type SQL / type C++, etc...) sont enregistr�s � la + fermeture de cette fen�tre d'import. + Tous les param�tres d'import sont automatiquement valoris�s � la r�-ouverture de la fen�tre + d'import. +

    + Autre remarque : une vid�o de QxEntityEditor est disponible + pour montrer un exemple d'import de la base de donn�es de test MySQL : sakila. +

    +
    +

    Importer � + partir d'une base de donn�es PostgreSQL

    +
    + L'import d'une base de donn�es PostgreSQL dans l'application QxEntityEditor se fait par le menu + principal : Import >> Import from PostgreSQL database : +

    + Import PostgreSQL +

    + Cet �cran d'import est divis� en plusieurs sections : +

    + -- Section PostgreSQL database connection : +
      +
    • Champ � Database server address (IP) � : adresse IP ou nom du serveur PostgreSQL ; +
    • +
    • Champ � Database port � : n� de port pour se connecter � la base de donn�es + PostgreSQL ;
    • +
    • Champ � Database name � : nom de la base de donn�es qu'on souhaite importer ;
    • +
    • Champ � Database login � : nom d'utilisateur pour se connecter � la base de + donn�es ;
    • +
    • Champ � Database password � : mot de passe pour se connecter � la base de donn�es + ;
    • +
    • Bouton � Connect to database � : d�marre la connexion � la base de donn�es : si + une erreur se produit, un message d'avertissement est affich�, sinon la liste des tables + de la base de donn�es apparait dans la section � Database items to import to + QxEntityEditor project �.
    • +
    +
    + -- Section Import settings : +
      +
    • Champ � Namespace � : espace de nom utilis� pour regrouper + les entit�s import�es ;
    • +
    • Champ � Delete all entities in the namespace before importing � : supprime les + entit�s de l'espace de nom ci-dessus avant de d�marrer l'import des tables de la base de + donn�es ;
    • +
    • Champ � Import tables/columns comment to entities/properties description � : + importe les commentaires des tables et colonnes d�finis dans la base de donn�es ;
    • +
    • Champ � Add boost::optional<T> decoration if a column definition allows NULL + value � : ajoute automatiquement la d�coration + boost::optional pour g�rer la valeur NULL ;
    • +
    • Champ � Import columns default value � : importe les valeurs par d�faut d�finies + au niveau des colonnes de la base de donn�es ;
    • +
    • Champ � Organize diagram layout after import process � : d�marre automatiquement + une r�organisation du diagramme d'entit�s apr�s le + processus d'import. +
    • +
    +
    + -- Section Relationship import settings : +
      +
    • Champ � Decoration � : d�coration par d�faut du type C++ (pour les relations, la + biblioth�que QxOrm recommande l'utilisation de pointeurs intelligents : + boost::shared_ptr, std::shared_ptr ou QSharedPointer) ; +
    • +
    • Champ � Collection � : si le type de relation n�cessite une liste (1-n et + n-n), alors d�fini le type de liste C++ par d�faut ; +
    • +
    +
    + -- Section Mapping database SQL type to C++ type : QxEntityEditor d�finit un + mapping par d�faut pour associer un type SQL � un type C++. + Il est possible d'utiliser cette liste pour surcharger le comportement par d�faut : +
      +
    • Colonne � SQL type � : type SQL � mapper. La recherche du type SQL n'est pas + sensible � la casse, et s'effectue sur les 1er caract�res : par exemple, mettre + varchar va mapper VARCHAR(255) ; +
    • +
    • Colonne � C++ type � : type C++ correspondant au type SQL ;
    • +
    • Bouton � Supprimer � : supprime une association type SQL / type C++.
    • +
    +
    + -- Section Database items to import to QxEntityEditor project : lorsque + QxEntityEditor est connect� � la base de donn�es, la liste de tous les �l�ments (tables, + colonnes, cl�s primaires, relations, vues, etc...) pouvant �tre import�s dans le projet + QxEntityEditor sont affich�s dans cette section. + Par d�faut, aucun �l�ment n'est s�lectionn�. + Il est possible de tout s�lectionner en 1 clic en cochant l'�l�ment racine de l'arborescence. + Il est �galement possible de s�lectionner uniquement les �l�ments appartenant � un sch�ma. + Une fois les �l�ments s�lectionn�s, le bouton � Ok � devient actif pour d�marrer le + processus d'import. +


    + Remarque : tous les param�tres d'import (connexion � la base de donn�es, param�tres + d'import et des relations, liste mapping type SQL / type C++, etc...) sont enregistr�s � la + fermeture de cette fen�tre d'import. + Tous les param�tres d'import sont automatiquement valoris�s � la r�-ouverture de la fen�tre + d'import. +

    + Autre remarque : une vid�o de QxEntityEditor est disponible + pour montrer un exemple d'import de la base de donn�es de test MySQL : sakila. +

    +
    +

    Importer � partir + d'une base de donn�es SQLite

    +
    + L'import d'une base de donn�es SQLite dans l'application QxEntityEditor se fait par le menu + principal : Import >> Import from SQLite database : +

    + Import SQLite +

    + Cet �cran d'import est divis� en plusieurs sections : +

    + -- Section Database connection : +
      +
    • Champ � Database path � : chemin d'acc�s au fichier base de donn�es SQLite ;
    • +
    • Bouton � Connect to database � : d�marre la connexion � la base de donn�es : si + une erreur se produit, un message d'avertissement est affich�, sinon la liste des tables + de la base de donn�es apparait dans la section � Database items to import to + QxEntityEditor project �.
    • +
    +
    + -- Section Import settings : +
      +
    • Champ � Namespace � : espace de nom utilis� pour regrouper + les entit�s import�es ;
    • +
    • Champ � Delete all entities in the namespace before importing � : supprime les + entit�s de l'espace de nom ci-dessus avant de d�marrer l'import des tables de la base de + donn�es ;
    • +
    • Champ � Import tables/columns comment to entities/properties description � : + importe les commentaires des tables et colonnes d�finis dans la base de donn�es ;
    • +
    • Champ � Add boost::optional<T> decoration if a column definition allows NULL + value � : ajoute automatiquement la d�coration + boost::optional pour g�rer la valeur NULL ;
    • +
    • Champ � Import columns default value � : importe les valeurs par d�faut d�finies + au niveau des colonnes de la base de donn�es ;
    • +
    • Champ � Organize diagram layout after import process � : d�marre automatiquement + une r�organisation du diagramme d'entit�s apr�s le + processus d'import. +
    • +
    +
    + -- Section Relationship import settings : +
      +
    • Champ � Decoration � : d�coration par d�faut du type C++ (pour les relations, la + biblioth�que QxOrm recommande l'utilisation de pointeurs intelligents : + boost::shared_ptr, std::shared_ptr ou QSharedPointer) ; +
    • +
    • Champ � Collection � : si le type de relation n�cessite une liste (1-n et + n-n), alors d�fini le type de liste C++ par d�faut ; +
    • +
    +
    + -- Section Mapping database SQL type to C++ type : QxEntityEditor d�finit un + mapping par d�faut pour associer un type SQL � un type C++. + Il est possible d'utiliser cette liste pour surcharger le comportement par d�faut : +
      +
    • Colonne � SQL type � : type SQL � mapper. La recherche du type SQL n'est pas + sensible � la casse, et s'effectue sur les 1er caract�res : par exemple, mettre + varchar va mapper VARCHAR(255) ; +
    • +
    • Colonne � C++ type � : type C++ correspondant au type SQL ;
    • +
    • Bouton � Supprimer � : supprime une association type SQL / type C++.
    • +
    +
    + -- Section Database items to import to QxEntityEditor project : lorsque + QxEntityEditor est connect� � la base de donn�es, la liste de tous les �l�ments (tables, + colonnes, cl�s primaires, relations, vues, etc...) pouvant �tre import�s dans le projet + QxEntityEditor sont affich�s dans cette section. + Par d�faut, aucun �l�ment n'est s�lectionn�. + Il est possible de tout s�lectionner en 1 clic en cochant l'�l�ment racine de l'arborescence. + Il est �galement possible de s�lectionner uniquement les �l�ments appartenant � un sch�ma. + Une fois les �l�ments s�lectionn�s, le bouton � Ok � devient actif pour d�marrer le + processus d'import. +


    + Remarque : tous les param�tres d'import (connexion � la base de donn�es, param�tres + d'import et des relations, liste mapping type SQL / type C++, etc...) sont enregistr�s � la + fermeture de cette fen�tre d'import. + Tous les param�tres d'import sont automatiquement valoris�s � la r�-ouverture de la fen�tre + d'import. +

    + Autre remarque : une vid�o de QxEntityEditor est disponible + pour montrer un exemple d'import de la base de donn�es de test MySQL : sakila. +

    +
    +

    Importer � partir + d'une base de donn�es en utilisant un driver ODBC (Oracle, MS SQL Server, etc.)

    +
    + L'application QxEntityEditor fournit un import g�n�rique de base de donn�es par connexion ODBC : + menu principal Import >> Import from database using ODBC driver. + Ce type d'import n�cessite de d�fnir un DSN (Data Source Name) dans l'environnement. + L'import par ODBC de l'application QxEntityEditor permet d'importer des bases de donn�es de type + : Oracle, Microsoft SQL Server, SQLite, MySQL/MariaDB, PostgreSQL, etc... +

    + Import ODBC +

    + Cet �cran d'import est divis� en plusieurs sections : +

    + -- Section Database connection : +
      +
    • Champ � DSN or connection string � : DSN (Data Source + Name) d�fini dans l'environnement permettant de se connecter � la base de donn�es + par ODBC ;
    • +
    • Champ � Login � : nom d'utilisateur pour se connecter � la base de donn�es + (optionnel suivant les DSN) ;
    • +
    • Champ � Password � : mot de passe pour se connecter � la base de donn�es + (optionnel suivant les DSN) ;
    • +
    • Champ � Database type � : type de base de donn�es : le type Generic permet + de se connecter � tous types de bases mais avec certaines limitations (pas de relation, + pas de cl� primaire sur plusieurs colonnes). Il est conseill� de s�lectionner le type r��l + de base de donn�es avant de d�marrer la connexion � la base (Oracle, Microsoft SQL Server, + SQLite, MySQL/MariaDB, PostgreSQL) ;
    • +
    • Bouton � Connect to database � : d�marre la connexion � la base de donn�es : si + une erreur se produit, un message d'avertissement est affich�, sinon la liste des tables + de la base de donn�es apparait dans la section � Database items to import to + QxEntityEditor project �.
    • +
    +
    + -- Section Import settings : +
      +
    • Champ � Namespace � : espace de nom utilis� pour regrouper + les entit�s import�es ;
    • +
    • Champ � Delete all entities in the namespace before importing � : supprime les + entit�s de l'espace de nom ci-dessus avant de d�marrer l'import des tables de la base de + donn�es ;
    • +
    • Champ � Import tables/columns comment to entities/properties description � : + importe les commentaires des tables et colonnes d�finis dans la base de donn�es ;
    • +
    • Champ � Add boost::optional<T> decoration if a column definition allows NULL + value � : ajoute automatiquement la d�coration + boost::optional pour g�rer la valeur NULL ;
    • +
    • Champ � Import columns default value � : importe les valeurs par d�faut d�finies + au niveau des colonnes de la base de donn�es ;
    • +
    • Champ � Organize diagram layout after import process � : d�marre automatiquement + une r�organisation du diagramme d'entit�s apr�s le + processus d'import. +
    • +
    +
    + -- Section Relationship import settings : +
      +
    • Champ � Decoration � : d�coration par d�faut du type C++ (pour les relations, la + biblioth�que QxOrm recommande l'utilisation de pointeurs intelligents : + boost::shared_ptr, std::shared_ptr ou QSharedPointer) ; +
    • +
    • Champ � Collection � : si le type de relation n�cessite une liste (1-n et + n-n), alors d�fini le type de liste C++ par d�faut ; +
    • +
    +
    + -- Section Mapping database SQL type to C++ type : QxEntityEditor d�finit un + mapping par d�faut pour associer un type SQL � un type C++. + Il est possible d'utiliser cette liste pour surcharger le comportement par d�faut : +
      +
    • Colonne � SQL type � : type SQL � mapper. La recherche du type SQL n'est pas + sensible � la casse, et s'effectue sur les 1er caract�res : par exemple, mettre + varchar va mapper VARCHAR(255) ; +
    • +
    • Colonne � C++ type � : type C++ correspondant au type SQL ;
    • +
    • Bouton � Supprimer � : supprime une association type SQL / type C++.
    • +
    +
    + -- Section Database items to import to QxEntityEditor project : lorsque + QxEntityEditor est connect� � la base de donn�es, la liste de tous les �l�ments (tables, + colonnes, cl�s primaires, relations, vues, etc...) pouvant �tre import�s dans le projet + QxEntityEditor sont affich�s dans cette section. + Par d�faut, aucun �l�ment n'est s�lectionn�. + Il est possible de tout s�lectionner en 1 clic en cochant l'�l�ment racine de l'arborescence. + Il est �galement possible de s�lectionner uniquement les �l�ments appartenant � un sch�ma. + Une fois les �l�ments s�lectionn�s, le bouton � Ok � devient actif pour d�marrer le + processus d'import. +


    + Remarque : tous les param�tres d'import (connexion � la base de donn�es, param�tres + d'import et des relations, liste mapping type SQL / type C++, etc...) sont enregistr�s � la + fermeture de cette fen�tre d'import. + Tous les param�tres d'import sont automatiquement valoris�s � la r�-ouverture de la fen�tre + d'import. +

    + Autre remarque : une vid�o de QxEntityEditor est disponible + pour montrer un exemple d'import de la base de donn�es de test MySQL : sakila. +

    +
    +
    + +

    Plugins d'export

    +
    + Tous les processus d'export de l'application QxEntityEditor sont accessibles depuis le menu + principal Export : +

    + Export plugins +

    +

    Exporter en + projet C++

    +
    + L'export C++ g�n�re un projet complet C++ pr�t � �tre compil� (par qmake et/ou + cmake) et contenant toutes les entit�s/propri�t�s/relations/�num�rations enregistr�es + dans le contexte de la biblioth�que QxOrm. + Toutes les fonctionnalit�s de la biblioth�que QxOrm (persistance, s�rialisation, introspection, etc...) sont ainsi disponibles pour + l'ensemble du projet C++ g�n�r�. +

    +

    Param�tres de l'export C++

    +
    + Les param�tres de l'export C++ sont accessibles par le menu principal : Tools >> + Export to C++ project (settings). + Cet �cran de param�trage apparait automatiquement au 1er export si aucun param�tre n'a �t� + enregistr� au niveau du projet. + Lorsqu'un export C++ a d�j� �t� r�alis�, alors cette fen�tre de param�trage est accessible + uniquement par le menu principal : Tools >> Export to C++ project (settings) : +

    + Export C++ +

    + Cet �cran d'export est divis� en plusieurs sections : +

    + -- Section C++ export settings : +
      +
    • Champ � C++ project location � : r�pertoire de destination qui contiendra tous les fichiers du projet C++ g�n�r�. Il + est possible de renseigner dans ce champ un chemin relatif par rapport au fichier + projet *.qxee ;
    • +
    • Option � Relative path to QxOrm library � : si coch�, le projet C++ r�f�rence + la biblioth�que QxOrm avec un chemin relatif, sinon la d�pendance � la biblioth�que + QxOrm est d�finie avec un chemin absolu. Il est conseill� d'utiliser une variable d'environnement dans les param�tres de + l'application QxEntityEditor (par exemple : $$(QXORM_DIR)) � la place de + cette option ;
    • +
    • Option � Generate a custom directory with custom files for each entity � : si + coch�, le projet C++ g�n�r� contient un fichier personnalis� par entit�. + Par d�faut, ces fichiers custom sont vides et ne seront jamais �cras�s par un nouvel + export C++. + Ces fichiers custom peuvent �tre utilis�s par exemple pour impl�menter les m�thodes de validation, ou bien les m�thodes de trigger.
    • +
    • Option � Generate all persistent classes using the PIMPL (Private Implementation) + idiom � : si coch�, toutes les classes C++ g�n�r�es et enregistr�es dans le + contexte QxOrm seront cod�es en utilisant le pattern + C++ PIMPL (ou d-pointer).
    • +
    +
    + -- Section Custom script (Javascript file) to change the default behaviour of the + export process : param�trage du moteur de personnalisation + de l'export. +
      +
    • Champ � Custom script file � : chemin d'acc�s au fichier Javascript permettant + de personnaliser l'export C++. Si vide, aucune + personnalisation n'est appliqu�e (export par d�faut). + Il est possible de renseigner dans ce champ un chemin relatif par rapport au fichier + projet *.qxee, par exemple ./my_script.js si le fichier + my_script.js se trouve dans le m�me r�pertoire que le fichier projet + *.qxee ; +
    • +
    • Bouton � Debug custom Javascript file � : si un fichier Javascript est d�fini, + un clic sur ce bouton d�clenchera le mode d�bogage + Javascript au prochain lancement d'export C++. + Au lieu d'utiliser ce bouton, il est �galement possible d'activer le mode d�bogage Javascript en maintenant la touche + SHIFT au moment de lancer un export C++. +
    • +
    +
    + -- Section C++ template files : cette section d�finit le code C++ des entit�s + pour les fichiers Header *.h et Source *.cpp. + Le moteur d'export utilise des mots-cl� (placeholder sous la forme @@ACTION@@) + pour remplacer le code par la valeur correspondante. + Cette notion de placeholder est la base du moteur de + personnalisation Javascript. + Il est possible de renseigner des placeholder + personnalis�s en prefixant par @@CUSTOM_, par exemple : + @@CUSTOM_MY_ACTION@@. +
      +
    • Option � No inheritance � : si coch� (option par d�faut), la classe C++ n'a pas + d'h�ritage (aucune classe de base) ;
    • +
    • Option � qx::IxPersistable � : si coch�, chaque classe C++ g�n�r�e h�rite de l'interface qx::IxPersistable (fourni une + interface commune pour les actions de persistance � la base de donn�es). Cette option + rallonge les temps de compilation du projet C++ ;
    • +
    • Option � qx::IxPersistable + QObject � : si coch�, chaque classe C++ g�n�r�e + h�rite de l'interface qx::IxPersistable et de la classe Qt QObject. + Cette option rallonge les temps de compilation du projet C++ ;
    • +
    • Option � Custom � : si coch�, possibilit� de fournir son propre code C++ (il + est cependant conseill� de commencer � copier/coller Header *.h et + Source *.cpp d'une des 3 autres options pour r�cup�rer les + placeholder � utiliser). +
    • +
    +
    +
    +

    Pr�sentation du projet C++ g�n�r�

    +
    + Le projet C++ g�n�r� dispose de l'arborescence suivante : +

    + Export C++ output +

    +
      +
    • le dossier � bin � est vide par d�faut, et contiendra le binaire compil� + (*.dll sous Windows, *.so sous Linux, etc...) ;
    • +
    • le dossier � build � est vide par d�faut, il peut �tre utilis� pour produire + les fichiers CMake ;
    • +
    • le dossier � custom � est aliment� si l'option � Generate a custom directory with custom + files for each entity � est coch�. + Ces fichiers custom peuvent �tre utilis�s par exemple pour impl�menter les m�thodes de validation, ou bien les m�thodes de trigger ;
    • +
    • le dossier � include � contient les d�finitions de toutes les classes entit�s + et �num�rations du projet ;
    • +
    • le dossier � src � contient les impl�mentations de toutes les classes entit�s + et �num�rations du projet ;
    • +
    • le fichier � CMakeLists.txt � peut �tre utilis� pour compiler le projet C++ + avec CMake ;
    • +
    • les fichiers � *.gen.pri � et � *.gen.pro � peuvent �tre utilis�s pour + compiler le projet C++ avec qmake ;
    • +
    +
    +
    +

    Compilation du projet C++ g�n�r� (par qmake ou + cmake)

    +
    + Le projet C++ g�n�r� peut �tre compil� avec CMake ou qmake.
    + La biblioth�que QxOrm dispose de certaines options de + compilation document�es dans les fichiers QxOrm.pri et QxOrm.cmake (il est possible de + conserver les options par d�faut).
    + Le manuel utilisateur de la biblioth�que QxOrm indique comment proc�der � la compilation du projet.
    + Un tutoriel est �galement disponible pour expliquer l'installation d'un environnement de d�veloppement sous Windows + avec MSVC.
    + Une fois le projet C++ compil� avec succ�s, le binaire est disponible dans le dossier � + bin � du projet export� (*.dll sous Windows, *.so sous Linux, etc...). +

    +
    +

    Exemple d'utilisation

    +
    + Le dossier ./samples/ du package QxEntityEditor fournit un projet de test : + qxBlog.qxee. + Ce projet de test peut �tre ouvert m�me avec une version de QxEntityEditor sans cl� de + licence active. + Une fois export� en projet C++, il est possible de compiler le projet afin d'obtenir une + biblioth�que partag�e qxBlog.dll (ou qxBlog.so sous Linux). + Cette biblioth�que partag�e poss�de toutes les entit�s et �num�rations du projet enregistr�es + dans le contexte QxOrm : toutes les fonctionnalit�s de la + biblioth�que QxOrm sont ainsi disponibles. +

    + Le dossier ./samples/ du package QxEntityEditor fournit �galement un projet d'exemple + d'utilisation du binaire compil� : qxBlogExec.zip. + Une fois d�zipp�, le fichier readme.txt � la racine contient les instructions + d'installation et compilation de ce projet d'exemple : +

    +
    +
    +--------------------------------------------
    +-- To build qxBlogExec project with qmake --
    +--------------------------------------------
    +
    +1- Export the 'qxBlog.qxee' project as a C++ project with QxEntityEditor and build it
    +2- Open the 'qxBlogExec.pro' file
    +3- Replace '$$(QXORM_DIR)' by your QxOrm library installation path
    +4- Replace 'C:/Temp/qxee/cpp/' by the path where you have exported the 'qxBlog.qxee' project as a C++ project
    +5- run command line : qmake
    +6- run command line : make debug (or make release)
    +
    +
    +--------------------------------------------
    +-- To build qxBlogExec project with CMake --
    +--------------------------------------------
    +
    +1- Export the 'qxBlog.qxee' project as a C++ project with QxEntityEditor and build it
    +2- Open the 'CMakeLists.txt' file
    +3- Replace '$ENV{QXORM_DIR}' by your QxOrm library installation path
    +4- Replace 'C:/Temp/qxee/cpp/' by the path where you have exported the 'qxBlog.qxee' project as a C++ project
    +5- run cmake or cmake-gui to configure and generate your make files
    +
    +
    +

    + Voici le contenu comment� de la fonction main() qui montre un exemple d'utilisation + (proche du tutoriel qxBlog) : +

    +
    +
    +int main(int argc, char * argv[])
    +{
    +   // Qt application
    +   QCoreApplication app(argc, argv);
    +   QFile::remove("./qxBlog.sqlite");
    +
    +   // Parameters to connect to database
    +   qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE");
    +   qx::QxSqlDatabase::getSingleton()->setDatabaseName("./qxBlog.sqlite");
    +   qx::QxSqlDatabase::getSingleton()->setHostName("localhost");
    +   qx::QxSqlDatabase::getSingleton()->setUserName("root");
    +   qx::QxSqlDatabase::getSingleton()->setPassword("");
    +
    +   // Only for debug purpose : assert if invalid offset detected fetching a relation
    +   qx::QxSqlDatabase::getSingleton()->setVerifyOffsetRelation(true);
    +
    +   // !!! TO CREATE TABLES, PLEASE USE THE C++ DDL SQL EXPORT PLUGIN OF QXENTITYEDITOR !!!
    +   // Create all tables in database (you should not use 'qx::dao::create_table<T>()' function in a production software, use it just for prototypes or samples)
    +   QSqlError daoError = qx::dao::create_table<author>();
    +   daoError = qx::dao::create_table<comment>();
    +   daoError = qx::dao::create_table<category>();
    +   daoError = qx::dao::create_table<blog>();
    +
    +   // Create a list of 3 author
    +   author_ptr author_1; author_1.reset(new author());
    +   author_ptr author_2; author_2.reset(new author());
    +   author_ptr author_3; author_3.reset(new author());
    +
    +   author_1->setlastname("author_1");
    +   author_1->setsex(sex::male);
    +   author_1->setbirthdate(QDateTime::currentDateTime());
    +   author_2->setlastname("author_2");
    +   author_2->setsex(sex::female);
    +   author_2->setbirthdate(QDateTime::currentDateTime());
    +   author_3->setlastname("author_3");
    +   author_3->setsex(sex::female);
    +   author_3->setbirthdate(QDateTime::currentDateTime());
    +
    +   list_of_author authorX;
    +   authorX.insert(1, author_1);
    +   authorX.insert(2, author_2);
    +   authorX.insert(3, author_3);
    +
    +   // Insert list of 3 author into database
    +   daoError = qx::dao::insert(authorX);
    +   qAssert(qx::dao::count<author>() == 3);
    +
    +   // Clone author n�2 : 'author_id_2'
    +   author_ptr author_clone = qx::clone(* author_2);
    +   qAssert(author_clone->getauthor_id() == author_2->getauthor_id());
    +   qAssert(author_clone->getsex() == sex::female);
    +
    +   // Create a query to fetch only female author : 'author_id_2' and 'author_id_3'
    +   qx::QxSqlQuery query("WHERE t_author.sex = :sex");
    +   query.bind(":sex", sex::female);
    +
    +   list_of_author list_of_female_author;
    +   daoError = qx::dao::fetch_by_query(query, list_of_female_author);
    +   qAssert(list_of_female_author.count() == 2);
    +
    +   // Dump list of female author (xml serialization)
    +   qx::dump(list_of_female_author);
    +
    +   // Create 3 categories
    +   category_ptr category_1 = category_ptr(new category("cat_1"));
    +   category_ptr category_2 = category_ptr(new category("cat_2"));
    +   category_ptr category_3 = category_ptr(new category("cat_3"));
    +
    +   category_1->setname("category_1"); category_1->setdescription("desc_1");
    +   category_2->setname("category_2"); category_2->setdescription("desc_2");
    +   category_3->setname("category_3"); category_3->setdescription("desc_3");
    +
    +   { // Create a scope to destroy temporary connexion to database
    +
    +   // Open a transaction to database
    +   QSqlDatabase db = qx::QxSqlDatabase::getDatabase();
    +   bool bCommit = db.transaction();
    +
    +   // Insert 3 categories into database, use 'db' parameter for the transaction
    +   daoError = qx::dao::insert(category_1, (& db));    bCommit = (bCommit && ! daoError.isValid());
    +   daoError = qx::dao::insert(category_2, (& db));    bCommit = (bCommit && ! daoError.isValid());
    +   daoError = qx::dao::insert(category_3, (& db));    bCommit = (bCommit && ! daoError.isValid());
    +
    +   qAssert(bCommit);
    +   qAssert(category_1->getcategory_id() != "");
    +   qAssert(category_2->getcategory_id() != "");
    +   qAssert(category_3->getcategory_id() != "");
    +
    +   // Terminate transaction => commit or rollback if there is error
    +   if (bCommit) { db.commit(); }
    +   else { db.rollback(); }
    +
    +   } // End of scope : 'db' is destroyed
    +
    +   // Create a blog with the class name (factory)
    +   boost::any blog_any = qx::create("blog");
    +   blog_ptr blog_1;
    +   try { blog_1 = boost::any_cast<blog_ptr>(blog_any); }
    +   catch (...) { blog_1.reset(new blog()); }
    +   blog_1->settext("blog_text_1");
    +   blog_1->settitle("blog_title_1");
    +   blog_1->setauthor(author_1);
    +
    +   // Insert 'blog_1' into database with 'save()' method
    +   daoError = qx::dao::save(blog_1);
    +
    +   // Modify 'blog_1' properties and save into database
    +   blog_1->settext("update blog_text_1");
    +   blog_1->setauthor(author_2);
    +   daoError = qx::dao::save(blog_1);
    +
    +   // Add 2 comments to 'blog_1'
    +   comment_ptr comment_1; comment_1.reset(new comment());
    +   comment_ptr comment_2; comment_2.reset(new comment());
    +
    +   comment_1->settext("comment_1 text");
    +   comment_1->setblog_id(blog_1);
    +   comment_2->settext("comment_2 text");
    +   comment_2->setblog_id(blog_1);
    +
    +   daoError = qx::dao::insert(comment_1);
    +   daoError = qx::dao::insert(comment_2);
    +   qAssert(qx::dao::count<comment>() == 2);
    +
    +   // Add 2 categories to 'blog_1' => must insert into extra-table 'category_blog'
    +   blog::type_list_of_category lst_category;
    +   lst_category.insert(category_1->getcategory_id(), category_1);
    +   lst_category.insert(category_3->getcategory_id(), category_3);
    +   blog_1->setlist_of_category(lst_category);
    +   daoError = qx::dao::save_with_relation("list_of_category", blog_1);
    +
    +   // Fetch blog into a new variable with all relation : 'author', 'comment' and 'category'
    +   blog_ptr blog_tmp; blog_tmp.reset(new blog());
    +   blog_tmp->setblog_id(blog_1->getblog_id());
    +   daoError = qx::dao::fetch_by_id_with_all_relation(blog_tmp);
    +
    +   qAssert(blog_tmp->list_of_comment().count() == 2);
    +   qAssert(blog_tmp->list_of_category().count() == 2);
    +   qAssert(blog_tmp->gettext() == "update blog_text_1");
    +   qAssert(blog_tmp->getauthor() && blog_tmp->getauthor()->getauthor_id() == author_2->getauthor_id());
    +
    +   // Fetch blog into a new variable with many relations using "*->*->*->*" (4 levels of relationships)
    +   blog_tmp.reset(new blog());
    +   blog_tmp->setblog_id(blog_1->getblog_id());
    +   daoError = qx::dao::fetch_by_id_with_relation("*->*->*->*", blog_tmp);
    +
    +   qAssert(blog_tmp->list_of_comment().count() == 2);
    +   qAssert(blog_tmp->list_of_category().count() == 2);
    +   qAssert(blog_tmp->gettext() == "update blog_text_1");
    +   qAssert(blog_tmp->getauthor() && blog_tmp->getauthor()->getauthor_id() == author_2->getauthor_id());
    +
    +   // Dump 'blog_tmp' result from database (xml serialization)
    +   qx::dump(blog_tmp);
    +
    +   // Check qx::dao::save_with_relation_recursive() function
    +   daoError = qx::dao::save_with_relation_recursive(blog_tmp);
    +   qAssert(! daoError.isValid());
    +   daoError = qx::dao::save_with_relation_recursive(blog_tmp, qx::dao::save_mode::e_update_only);
    +   qAssert(! daoError.isValid());
    +
    +   // Call 'age()' method with class name and method name (reflexion)
    +   //qx_bool bInvokeOk = qx::QxClassX::invoke("author", "age", author_1);
    +   //qAssert(bInvokeOk);
    +
    +   // Test 'isDirty()' method
    +   qx::dao::ptr<blog> blog_isdirty = qx::dao::ptr<blog>(new blog());
    +   blog_isdirty->setblog_id(blog_1->getblog_id());
    +   daoError = qx::dao::fetch_by_id(blog_isdirty);
    +   qAssert(! daoError.isValid() && ! blog_isdirty.isDirty());
    +
    +   blog_isdirty->settext("blog property 'text' modified => blog is dirty !!!");
    +   QStringList lstDiff; bool bDirty = blog_isdirty.isDirty(lstDiff);
    +   qAssert(bDirty && (lstDiff.count() == 1) && (lstDiff.at(0) == "text"));
    +   if (bDirty) { qDebug("[QxOrm] test dirty 1 : blog is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
    +
    +   // Update only property 'm_text' of 'blog_isdirty'
    +   daoError = qx::dao::update_optimized(blog_isdirty);
    +   qAssert(! daoError.isValid() && ! blog_isdirty.isDirty());
    +   qx::dump(blog_isdirty);
    +
    +   // Test 'isDirty()' method with a container
    +   typedef qx::dao::ptr< QList<author_ptr> > type_lst_author_test_is_dirty;
    +   type_lst_author_test_is_dirty container_isdirty = type_lst_author_test_is_dirty(new QList<author_ptr>());
    +   daoError = qx::dao::fetch_all(container_isdirty);
    +   qAssert(! daoError.isValid() && ! container_isdirty.isDirty() && (container_isdirty->count() == 3));
    +
    +   author_ptr author_ptr_dirty = container_isdirty->at(1);
    +   author_ptr_dirty->setlastname("author name modified at index 1 => container is dirty !!!");
    +   bDirty = container_isdirty.isDirty(lstDiff);
    +   qAssert(bDirty && (lstDiff.count() == 1));
    +   if (bDirty) { qDebug("[QxOrm] test dirty 2 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
    +
    +   author_ptr_dirty = container_isdirty->at(2);
    +   author_ptr_dirty->setfirstname("firstname changed");
    +   bDirty = container_isdirty.isDirty(lstDiff);
    +   qAssert(bDirty && (lstDiff.count() == 2));
    +   if (bDirty) { qDebug("[QxOrm] test dirty 3 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
    +
    +   // Update only property 'm_name' at position 1, only property 'm_birthdate' at position 2 and nothing at position 0
    +   daoError = qx::dao::update_optimized(container_isdirty);
    +   qAssert(! daoError.isValid() && ! container_isdirty.isDirty());
    +   qx::dump(container_isdirty);
    +
    +   // Fetch only property 'm_text' of blog
    +   QStringList lstColumns = QStringList() << "text";
    +   QList<blog_ptr> lst_blog_with_only_text;
    +   daoError = qx::dao::fetch_all(lst_blog_with_only_text, NULL, lstColumns);
    +   qAssert(! daoError.isValid() && (lst_blog_with_only_text.size() > 0));
    +   if ((lst_blog_with_only_text.size() > 0) && (lst_blog_with_only_text[0].get() != NULL))
    +   { qAssert(lst_blog_with_only_text[0]->gettitle().isEmpty()); }
    +   qx::dump(lst_blog_with_only_text);
    +
    +   // Dump all registered classes into QxOrm context (introspection engine)
    +   qx::QxClassX::dumpAllClasses();
    +
    +   // Call a custom SQL query or a stored procedure
    +   qx_query testStoredProc("SELECT * FROM t_author");
    +   daoError = qx::dao::call_query(testStoredProc);
    +   qAssert(! daoError.isValid());
    +   testStoredProc.dumpSqlResult();
    +
    +   // Call a custom SQL query or a stored procedure and fetch automatically properties (with a collection of items)
    +   qx_query testStoredProcBis("SELECT * FROM t_author");
    +   authorX.clear();
    +   daoError = qx::dao::execute_query(testStoredProcBis, authorX);
    +   qAssert(! daoError.isValid()); qAssert(authorX.count() > 0);
    +   qx::dump(authorX);
    +
    +   // Call a custom SQL query or a stored procedure and fetch automatically properties
    +   qx_query testStoredProcThird("SELECT name, category_id FROM t_category");
    +   category_ptr category_tmp = category_ptr(new category());
    +   daoError = qx::dao::execute_query(testStoredProcThird, category_tmp);
    +   qAssert(! daoError.isValid()); qAssert(category_tmp->getcategory_id() != "");
    +   qx::dump(category_tmp);
    +
    +   return 0;
    +}
    +
    +
    +

    +
    +
    +

    Exporter en projet C++ de type model/view

    +
    + La biblioth�que QxOrm fournit le module QxModelView. + Une documentation sur le module QxModelView est disponible dans le manuel utilisateur de la biblioth�que QxOrm. + Le module QxModelView + permet d'utiliser le moteur + model/view de Qt avec toutes les classes enregistr�es dans le contexte QxOrm : +
      +
    • QML : toute propri�t� enregistr�e dans le contexte + QxOrm est accessible en QML : le module QxModelView + permet ainsi de faciliter l'int�raction entre QML et les bases de donn�es ;
    • +
    • Qt widgets : utilisation de QTableView ou + QListView par exemple pour afficher/modifier le contenu d'une table de la base de + donn�es. +
    • +
    +
    + L'application QxEntityEditor permet de g�n�rer un projet C++ de type model/view complet et pr�t + � �tre compil� : ainsi toutes les entit�s et leurs relations sont accessibles dans des vues QML + par exemple. +

    +

    Param�tres de l'export

    +
    + Les param�tres de l'export C++ de type model/view sont accessibles par le menu principal : + Tools >> Export to C++ model/view project (settings). + Cet �cran de param�trage apparait automatiquement au 1er export si aucun param�tre n'a �t� + enregistr� au niveau du projet. + Lorsqu'un export C++ model/view a d�j� �t� r�alis�, alors cette fen�tre de param�trage est + accessible uniquement par le menu principal : Tools >> Export to C++ model/view + project (settings) : +

    + Export C++ model/view +

    + Cet �cran d'export est divis� en plusieurs sections : +

    + -- Section C++ model/view export settings : +
      +
    • Champ � C++ model/view project location � : r�pertoire de destination qui + contiendra tous les fichiers du + projet C++ model/view g�n�r�. Il est possible de renseigner dans ce champ un + chemin relatif par rapport au fichier projet *.qxee ;
    • +
    • Champ � C++ entities project location � : lecture seule uniquement, indique le + r�pertoire de destination du projet C++ g�n�r� + (entit�s enregistr�es dans le contexte QxOrm) dont d�pend le projet C++ model/view ; +
    • +
    • Champ � Namespace for model/view � : espace de nom utilis� pour les classes C++ + g�n�r�es de type model/view (ces classes impl�mentent l'interface qx::IxModel) ; +
    • +
    • Champ � Generate custom files � : si coch�, le projet C++ g�n�r� contient un + fichier personnalis� par entit�. + Par d�faut, ces fichiers custom sont vides et ne seront jamais �cras�s par un nouvel + export C++.
    • +
    +
    + -- Section Custom script (Javascript file) to change the default behaviour of the + export process : param�trage du moteur de personnalisation + de l'export. +
      +
    • Champ � Custom script file � : chemin d'acc�s au fichier Javascript permettant + de personnaliser l'export C++. Si vide, aucune + personnalisation n'est appliqu�e (export par d�faut). + Il est possible de renseigner dans ce champ un chemin relatif par rapport au fichier + projet *.qxee, par exemple ./my_script.js si le fichier + my_script.js se trouve dans le m�me r�pertoire que le fichier projet + *.qxee ; +
    • +
    • Bouton � Debug custom Javascript file � : si un fichier Javascript est d�fini, + un clic sur ce bouton d�clenchera le mode d�bogage + Javascript au prochain lancement d'export C++. + Au lieu d'utiliser ce bouton, il est �galement possible d'activer le mode d�bogage Javascript en maintenant la touche + SHIFT au moment de lancer un export C++. +
    • +
    +
    + -- Section C++ model/view template files : cette section d�finit le code C++ + model/view pour les fichiers Header *.h et Source *.cpp. + Le moteur d'export utilise des mots-cl� (placeholder sous la forme @@ACTION@@) + pour remplacer le code par la valeur correspondante. + Cette notion de placeholder est la base du moteur de + personnalisation Javascript. + Il est possible de renseigner des placeholder + personnalis�s en prefixant par @@CUSTOM_, par exemple : + @@CUSTOM_MY_ACTION@@. +
      +
    • Option � Default � : si coch� (option par d�faut), les mod�les g�n�r�s h�ritent + de qx::QxModel<T> (les mod�les peuvent ex�cuter des requ�tes + SQL, m�me dans des vues QML) ;
    • +
    • Option � qx::QxModelService<T, S> � : si coch�, les mod�les g�n�r�s + h�ritent de qx::QxModelService<T, S> (les mod�les peuvent ex�cuter des requ�tes client-serveur avec le module + QxService, m�me dans des vues QML) ;
    • +
    • Option � Custom � : si coch�, possibilit� de fournir son propre code C++ pour + g�n�rer les mod�les (il est cependant conseill� de commencer � copier/coller + Header *.h et Source *.cpp d'une des 2 autres options pour + r�cup�rer les placeholder � utiliser). +
    • +
    +
    +
    +

    Pr�sentation du projet g�n�r�

    +
    + Le projet C++ model/view g�n�r� dispose de l'arborescence suivante : +

    + Export C++ model/view output +

    +
      +
    • le dossier � bin � est vide par d�faut, et contiendra le binaire compil� + (*.dll sous Windows, *.so sous Linux, etc...) ;
    • +
    • le dossier � build � est vide par d�faut, il peut �tre utilis� pour produire + les fichiers CMake ;
    • +
    • le dossier � custom � est aliment� si l'option � Generate custom files � + est coch�e ;
    • +
    • le dossier � include � contient les d�finitions de tous les mod�les du projet ; +
    • +
    • le dossier � src � contient les impl�mentations de tous les mod�les du projet ; +
    • +
    • le fichier � CMakeLists.txt � peut �tre utilis� pour compiler le projet C++ + avec CMake ;
    • +
    • les fichiers � *.gen.pri � et � *.gen.pro � peuvent �tre utilis�s pour + compiler le projet C++ avec qmake ;
    • +
    +
    +
    +
    +

    Exporter en projet C++ de type services

    +
    + La biblioth�que QxOrm fournit le module QxService. + Une documentation sur le module QxService est disponible dans le manuel utilisateur de la biblioth�que QxOrm. + Le module QxService de la + biblioth�que QxOrm permet de cr�er rapidement un serveur d'applications C++ performant + (notion de services avec demande du client et r�ponse du serveur). + Un tutoriel est disponible sur le site QxOrm afin de pr�senter + un exemple d'utilisation du module QxService. +

    + L'application QxEntityEditor permet de g�n�rer un projet C++ de type services (un projet C++ + pour g�rer la couche cliente et un projet C++ pour publier les services c�t� serveur) complet et + pr�t � �tre compil� : ainsi toutes les entit�s et leurs relations peuvent �tre transf�r�es sur + le r�seau pour une application de type client/serveur. +

    +

    Param�tres de l'export

    +
    + Les param�tres de l'export C++ de type services sont accessibles par le menu principal : + Tools >> Export to C++ services project (settings). + Cet �cran de param�trage apparait automatiquement au 1er export si aucun param�tre n'a �t� + enregistr� au niveau du projet. + Lorsqu'un export C++ de type services a d�j� �t� r�alis�, alors cette fen�tre de param�trage + est accessible uniquement par le menu principal : Tools >> Export to C++ services + project (settings) : +

    + Export C++ services +

    + Cet �cran d'export est divis� en plusieurs sections : +

    + -- Section C++ services export settings : +
      +
    • Champ � C++ services project location � : r�pertoire de destination qui + contiendra tous les fichiers du + projet C++ de type services g�n�r�. Il est possible de renseigner dans ce champ + un chemin relatif par rapport au fichier projet *.qxee ;
    • +
    • Champ � C++ entities project location � : lecture seule uniquement, indique le + r�pertoire de destination du projet C++ g�n�r� + (entit�s enregistr�es dans le contexte QxOrm) dont d�pend le projet C++ services ;
    • +
    • Champ � Namespace for services � : espace de nom utilis� pour les classes C++ + g�n�r�es de type services (ces classes permettent de transf�rer les entit�s sur le r�seau, couche + cliente et serveur) ;
    • +
    • Champ � Generate custom files � : si coch�, le projet C++ g�n�r� contient un + fichier personnalis� par entit�. + Par d�faut, ces fichiers custom sont vides et ne seront jamais �cras�s par un nouvel + export C++. + Ces fichiers custom peuvent par exemple �tre utilis�s pour impl�menter des nouveaux + services ;
    • +
    • Champ � Generate server application � : si coch�, un projet C++ d'exemple de type + serveur d'applications est g�n�r� et permet de charger et publier les services + c�t� serveur avec un syst�me de plugins ;
    • +
    • Champ � Server application location � : si l'option � Generate server + application � est coch�e, d�fini le r�pertoire de destination du projet + d'exemple serveur + d'applications.
    • +
    +
    + -- Section Custom script (Javascript file) to change the default behaviour of the + export process : param�trage du moteur de personnalisation + de l'export. +
      +
    • Champ � Custom script file � : chemin d'acc�s au fichier Javascript permettant + de personnaliser l'export C++. Si vide, aucune + personnalisation n'est appliqu�e (export par d�faut). + Il est possible de renseigner dans ce champ un chemin relatif par rapport au fichier + projet *.qxee, par exemple ./my_script.js si le fichier + my_script.js se trouve dans le m�me r�pertoire que le fichier projet + *.qxee ; +
    • +
    • Bouton � Debug custom Javascript file � : si un fichier Javascript est d�fini, + un clic sur ce bouton d�clenchera le mode d�bogage + Javascript au prochain lancement d'export C++. + Au lieu d'utiliser ce bouton, il est �galement possible d'activer le mode d�bogage Javascript en maintenant la touche + SHIFT au moment de lancer un export C++. +
    • +
    +
    + -- Section C++ services template files : cette section d�finit le code C++ des + services pour les fichiers Header *.h et Source *.cpp (client et + serveur). + Le moteur d'export utilise des mots-cl� (placeholder sous la forme @@ACTION@@) + pour remplacer le code par la valeur correspondante. + Cette notion de placeholder est la base du moteur de + personnalisation Javascript. + Il est possible de renseigner des placeholder + personnalis�s en prefixant par @@CUSTOM_, par exemple : + @@CUSTOM_MY_ACTION@@. +
      +
    • Option � Default � : si coch� (option par d�faut), g�n�re les services (couche + cliente et serveur) par d�faut : les entit�s peuvent �tre transf�r�es sur le r�seau et + les actions CRUD sur la base de donn�es sont support�es ;
    • +
    • Option � Custom � : si coch�, possibilit� de fournir son propre code C++ pour + g�n�rer les services (il est cependant conseill� de commencer � copier/coller + Header *.h et Source *.cpp de l'option par d�faut pour + r�cup�rer les placeholder � utiliser). +
    • +
    +
    +
    +

    Pr�sentation du projet g�n�r�

    +
    + Le projet C++ de type services (client et serveur) g�n�r� dispose de l'arborescence suivante + : +

    + Export C++ services output +

    +
      +
    • le dossier � bin � est vide par d�faut, et contiendra les binaires compil�s + (*.dll sous Windows, *.so sous Linux, etc...) pour la partie cliente et + serveur ;
    • +
    • le dossier � build � est vide par d�faut, il peut �tre utilis� pour produire + les fichiers CMake ;
    • +
    • le dossier � client � contient les fichiers qmake et CMake pour compiler la couche cliente + du projet ;
    • +
    • le dossier � custom � est aliment� si l'option � Generate custom files � + est coch�e ;
    • +
    • le dossier � include � contient les d�finitions de tous les services du projet + ;
    • +
    • le dossier � server � contient les fichiers qmake et CMake pour compiler la couche serveur + du projet ;
    • +
    • le dossier � src � contient les impl�mentations de tous les services du projet + ;
    • +
    • le fichier � CMakeLists.txt � peut �tre utilis� pour compiler le projet C++ + (client et serveur) avec CMake ;
    • +
    • les fichiers � *.gen.pri � et � *.gen.pro � peuvent �tre utilis�s pour + compiler le projet C++ (client et serveur) avec qmake ;
    • +
    • le fichier � *.gen.json � est utilis� par la couche serveur pour g�n�rer un + plugin pouvant �tre charg� dynamiquement par le serveur d'applications.
    • +
    +
    +
    +

    Serveur d'applications g�n�rique

    +
    + Si l'option � Generate server + application � est coch�e, l'export C++ de type services g�n�re �galement un + exemple de serveur d'applications g�n�rique pouvant publier des services en chargeant des + plugins. + Tout comme les autres projets C++ g�n�r�s par QxEntityEditor, le serveur d'applications + g�n�rique peut �tre compil� qmake et CMake. + Ce projet d'exemple dispose de l'arborescence suivante : +

    + Export C++ services server app output +

    + Une fois compil�, l'ex�cutable se trouve dans le dossier � bin �. + Le lancement du serveur d'applications g�n�rique ouvre la fen�tre suivante : +

    + Export C++ services server app +

    + Cet �cran est divis� en plusieurs sections : +

    + -- Section Plugins services : +
      +
    • Champ � Plugins path � : il est n�cessaire de d�finir le r�pertoire contenant + les plugins � charger. + Ces plugins correspondent � la couche + serveur du projet C++ de type services. + Une fois charg�, les plugins publient des services pouvant �tre appel�s par les + applications clientes. + Si aucun plugin n'est charg� apr�s la s�lection du r�pertoire, il est possible de + d�finir la + variable d'environnement QT_DEBUG_PLUGINS afin d'avoir des logs sur + l'erreur de chargement de plugin (en g�n�ral, une d�pendance du plugin non trouv�). +
    • +
    +
    + -- Section Database connection parameters : +
      +
    • Cette section contient tous les param�tres n�cessaires pour initialiser la connexion � + la base de donn�es utilis�e par les services (utilisation de la classe qx::QxSqlDatabase).
    • +
    +
    + -- Section Server parameters : +
      +
    • Champ � Port number � : d�fini le n� de port utilis� par le serveur + d'applications pour publier les services. Les applications clientes devront se + connecter sur ce port pour effectuer des requ�tes au serveur ;
    • +
    • Champ � Thread count � : nombre de threads utilis�s par le serveur pour traiter + les requ�tes provenant des applications clientes ;
    • +
    • Champ � Serialization type � : type de s�rialisation utilis� pour renvoyer les + r�ponses du serveur au client ;
    • +
    • Champ � Compress data � : si coch�, les donn�es renvoy�es au client sont + compress�es ;
    • +
    • Champ � Encrypt data � : si coch�, les donn�es renvoy�es au client sont + crypt�es (possibilit� de d�finir une cl� de cryptage) ;
    • +
    • Bouton � Start server � : bouton permettant de d�marrer et arr�ter le serveur. +
    • +
    +
    + -- Section Log last client-server reply-request transaction : +
      +
    • Affiche dans un flux XML ou JSON les d�tails de la derni�re transaction client-serveur + trait�e par le serveur d'applications.
    • +
    +
    + -- Section Log last server error : +
      +
    • Si une erreur se produit sur le serveur d'applications, elle est automatiquement + loggu�e dans cette section.
    • +
    +
    +
    +
    +

    Exporter le + sch�ma de base de donn�es SQL DDL

    +
    + L'application QxEntityEditor permet de g�n�rer le sch�ma de base de donn�es dans un format DDL + SQL. + L'application QxEntityEditor supporte les bases de donn�es les plus fr�quemment utilis�es : + SQLite, MySQL, MariaDB, PostgreSQL, Oracle et Microsoft SQL Server. + Le script DDL SQL g�n�r� par QxEntityEditor peut �tre import� par la base de donn�es pour cr�er + automatiquement toutes les tables, colonnes, cl�s primaires, relations, index, etc... + Les param�tres de l'export DDL SQL sont accessibles par le menu principal : Tools >> + Export to DDL SQL script file (settings) : +

    + Export SQL DDL +

    + Cet �cran d'export est divis� en plusieurs sections : +

    + -- Section DDL export settings : +
      +
    • Champ � File location � : r�pertoire de destination qui contiendra le script DDL + SQL g�n�r� ;
    • +
    • Champ � Database type � : type de base de donn�es qui sera utilis� pour importer + le sch�ma DDL SQL (SQLite, MySQL, MariaDB, PostgreSQL, Oracle ou Microsoft SQL Server) ; +
    • +
    • Champ � Relationships � : si coch�, des cl�s �trang�res (foreign keys) sont cr��es + pour g�rer les relations entre entit�s ;
    • +
    • Champ � Schema � : d�fini le type d'export DDL : 1 fichier contenant toute la + structure du projet *.qxee, ou bien 1 fichier par + version du projet *.qxee (gestion de l'�volution du sch�ma de base de + donn�es).
    • +
    +
    + -- Section Mapping C++ type to database SQL type : +
      +
    • Liste permettant d'associer un type C++ (renseign� au niveau des propri�t�s du projet + *.qxee) � un type SQL. +
    • +
    +
    + -- Section Custom script (Javascript file) to change the default behaviour of the export + process : param�trage du moteur de personnalisation de + l'export. +
      +
    • Champ � Custom script file � : chemin d'acc�s au fichier Javascript permettant de + personnaliser l'export DDL. Si vide, aucune personnalisation + n'est appliqu�e (export par d�faut). + Il est possible de renseigner dans ce champ un chemin relatif par rapport au fichier + projet *.qxee, par exemple ./my_script.js si le fichier my_script.js + se trouve dans le m�me r�pertoire que le fichier projet *.qxee ; +
    • +
    • Bouton � Debug custom Javascript file � : si un fichier Javascript est d�fini, un + clic sur ce bouton d�clenchera le mode d�bogage Javascript + au prochain lancement d'export DDL. + Au lieu d'utiliser ce bouton, il est �galement possible d'activer le mode d�bogage Javascript en maintenant la touche + SHIFT au moment de lancer un export DDL. +
    • +
    +
    +
    +

    Imprimer le + diagramme d'entit�s

    +
    + L'application QxEntityEditor permet d'imprimer le diagramme d'entit�s dans un format PNG et/ou + PDF. + Les param�tres d'impression sont accessibles par le menu principal : Tools >> Print the + entities diagram (settings) : +

    + Export print PNG/PDF +

    +
    +

    Exporter le projet dans un r�pertoire associ� � un + gestionnaire de code source (Git, Perforce, CVS, etc.)

    +
    + Un projet QxEntityEditor (fichier *.qxee) peut �tre g�r� par une �quipe de d�veloppeurs : + le code source d'un projet QxEntityEditor peut �tre export�/import� (manuellement ou en ligne de + commande) avec les plugins QxEESourceControlExport et QxEESourceControlImport.
    +
    + Associ� � un gestionnaire de code source (Git, Perforce, CVS, etc.), ces 2 plugins permettent + ainsi : +
      +
    • plusieurs personnes peuvent travailler simultan�ment sur un m�me projet ;
    • +
    • un projet peut facilement �tre versionn� dans le gestionnaire de code source ;
    • +
    • chaque �l�ment d'un projet peut �tre compar� (diff�rence entre 2 versions d'une m�me + entit� par exemple).
    • +
    + Voici un exemple d'utilisation des plugins QxEESourceControlExport et QxEESourceControlImport, avec 2 d�veloppeurs + nomm�s 'dev A' et 'dev B' (l'exemple pouvant �tre �tendu � X d�veloppeurs) : +
      +
    • dev A et dev B travaillent sur un m�me projet QxEntityEditor (fichier *.qxee) ; +
    • +
    • dev A cr��/modifie/supprime certaines entit�s dans l'application QxEntityEditor ;
    • +
    • dev B cr��/modifie/supprime d'autres entit�s dans l'application QxEntityEditor ;
    • +
    • dev A et dev B exportent le projet *.qxee en utilisant le plugin QxEESourceControlExport (manuellement + ou en ligne de commande) ;
    • +
    • une fois le projet export� dans un r�pertoire, dev A et dev B enregistrent + (check-in ou submit) les fichiers JSON g�n�r�s dans le gestionnaire de code + source (Git, Perforce, CVS, etc.) sur leur propre branche de travail ;
    • +
    • depuis le gestionnaire de code source, dev A et dev B int�grent (merge ou + integrate) leur branche de travail sur la branche de d�veloppement DEV (ou MAIN ou + MASTER ou LATEST); Remarque : m�me si il y a des conflits � r�soudre, ils seront + simples � corriger car le format JSON est lisible facilement ; +
    • +
    • dev A et dev B peuvent � pr�sent obtenir la derni�re version des fichiers JSON (get + latest depuis le gestionnaire de code source) � partir de la branche DEV (ou MAIN + ou MASTER ou LATEST) : les fichiers JSON obtenus contiennent � la fois les + modifications de dev A et dev B ;
    • +
    • dev A et dev B peuvent importer ces fichiers JSON dans l'application QxEntityEditor en + utilisant le plugin d'import QxEESourceControlImport (manuellement ou + en ligne de commande).
    • +
    + Les param�tres du plugin d'export sont accessibles depuis le menu principal Tools >> + Export to Source Control repository (Git, Perforce, CVS, etc.) as JSON files (settings) : +

    + Source Control export plugin settings +

    + Le param�tre � renseigner correspond au r�pertoire dans lequel seront g�n�r�s les fichiers + JSON.
    + Chaque �l�ment du projet *.qxee (entit�s, propri�t�s, relations, commentaires, etc.) est + export� dans un fichier JSON sp�cifique.
    + Le r�pertoire o� sont export�s les fichiers JSON a la structure suivante : +

    + Source Control output directory +

    + Remarque : il est possible d'exporter un projet en ligne de commande sans d�marrer + l'interface utilisateur de l'application QxEntityEditor.
    + Exemple de ligne de commande :
    +
    + +
    QxEntityEditor --no_gui --project="<path_to_your_qxee_project_file>" --plugin=QxEESourceControlExport --QxEESourceControlExport_path="<path_to_your_export_output_directory>"
    +
    +
    +

    +
    +

    Exporter le + projet sous format XML ou JSON

    +
    + + Remarque : + pour travailler avec un gestionnaire de code source (Git, Perforce, CVS, etc.), il est + conseill� d'utiliser les plugins QxEESourceControlExport et QxEESourceControlImport (au lieu de cr�er un + fichier unique).
    +
    + L'application QxEntityEditor permet d'exporter l'int�gralit� d'un projet *.qxee dans un + format XML ou JSON. + Les param�tres d'export XML/JSON sont accessibles par le menu principal : Tools >> + Export to XML or JSON file (settings) : +

    + Export XML/JSON +

    + Remarque : un export XML ne peut pas �tre import� dans un projet *.qxee. + Par contre, un fichier JSON peut �tre utilis� pour importer les donn�es dans un projet + *.qxee en utilisant le plugin d'import : Importer un projet � + partir d'un fichier texte au format JSON. +

    +
    +
    + +

    Moteur Javascript pour + personnaliser les exports

    +
    + L'application QxEntityEditor fournit un puissant moteur de personnalisation des exports. + Ce moteur utilise le langage + Javascript pour �crire des scripts pour personnaliser les exports. + Ces scripts Javascript ont + acc�s � l'ensemble des param�tres d'un projet *.qxee, et peuvent �tre d�bogu�s � l'aide d'un + d�bogueur int�gr� � QxEntityEditor. + Il est �galement possible d'�crire de nouveaux fichiers lors + des exports. +

    + Remarque : le dossier ./samples/ du package QxEntityEditor fournit 2 exemples de + scripts document�s : custom_script.js et q_property.js. +

    +

    Architecture + et fonctionnement du moteur de personnalisation Javascript

    +
    + Tous les plugins d'export de l'application QxEntityEditor dispose + d'un param�trage nomm� : Custom script (Javascript + file) to change the default behaviour of the export process. + Il est ainsi possible de param�trer un script Javascript utilis� au moment de l'export. + Le moteur d'export utilise des mots-cl� (placeholder sous la forme @@ACTION@@) + pour remplacer le code par la valeur correspondante. + Cette notion de placeholder est la base du moteur de + personnalisation Javascript. + Il est possible de renseigner des placeholder + personnalis�s en prefixant par @@CUSTOM_, par exemple : + @@CUSTOM_MY_ACTION@@. + Lors d'un export, l'application QxEntityEditor parcourt la liste de toutes les entit�s d�finies + dans le projet *.qxee. + Chaque fois qu'un placeholder (sous la forme @@ACTION@@) est rencontr�, le script + Javascript est appel� + avec tous les param�tres n�cessaires pour effectuer ou non une personnalisation. +

    + Voici un exemple de script Javascript minimal. + Ce script ne fait rien, il permet de montrer uniquement la structure minimale d'un Javascript valide pour + QxEntityEditor : + +
    +
    +
    ({
    +
    +// Here is the entry point of the QxEntityEditor javascript engine
    +// This function is called for each placeholder defined in the C++ template section
    +customProcess : function(params)
    +{
    +   try
    +   {
    +      // quit with 'params[0]' means : doesn't change the default export behaviour
    +      return params[0];
    +   }
    +   catch (err)
    +   { return ("[CustomScriptError] an unexpected error occurred : " + err); }
    +}
    +
    +});
    +
    +

    +

    Liste des + param�tres d'appel du moteur Javascript

    +
    + Le param�tre d'entr�e params de la fonction customProcess() est un tableau + contenant une liste de valeurs (ce qui correspond au contexte d'appel du script). + Voici un exemple de fonction qui peut �tre utilis�e dans vos scripts pour lister et donner + une signification aux valeurs d'entr�e : +

    +
    +
    function printParams(params)
    +{
    +   var log = "";
    +   log = log + "\n - default_value = " + params[0];
    +   log = log + "\n - project_name = " + params[1];
    +   log = log + "\n - project_file = " + params[2];
    +   log = log + "\n - plugin_name = " + params[3];
    +   log = log + "\n - current_file = " + params[4];
    +   log = log + "\n - action (or placeholder) = " + params[5];
    +   log = log + "\n - entity_name = " + params[6];
    +   log = log + "\n - entity_table_name = " + params[7];
    +   log = log + "\n - property_name = " + params[8];
    +   log = log + "\n - property_type = " + params[9];
    +   log = log + "\n - property_column_name = " + params[10];
    +   log = log + "\n - property_is_primary_key = " + params[11];
    +   log = log + "\n - enumeration_name = " + params[12];
    +   log = log + "\n - entity_id = " + params[13];
    +   log = log + "\n - property_id = " + params[14];
    +   log = log + "\n - enumeration_id = " + params[15];
    +   log = log + "\n - output_location = " + params[16];
    +
    +   print(log); // print value to the custom script debugger window
    +   helper.print(log); // print value to the standard output (for example, on Windows, use the 'DebugView' application to see all logs)
    +   return log;
    +}
    +
    +

    +
    +
    +

    Fonctions + disponibles par Javascript

    +
    + Le moteur de personnalisation des exports de QxEntityEditor supporte toutes les fonctionnalit�s + du langage Javascript + (ECMAScript 5). + L'application QxEntityEditor fournit en plus une instance d'objet nomm�e helper + qui peut �tre utilis�e pour acc�der � de nouvelles fonctionnalit�s. + Nous allons lister dans ce chapitre les cas d'usage les plus couramment utilis�s pour + personnaliser les exports. +

    +

    Obtenir les informations associ�es � une entit�

    +
    + Voici comment r�cup�rer le param�trage de l'entit� courante, + helper.getEntityDetails(entity_id) : +

    +
    +
    var entity_id = params[13];
    +printEntityDetails(entity_id);
    +
    +//...
    +
    +function printEntityDetails(entity_id)
    +{
    +   var details = helper.getEntityDetails(entity_id);
    +   if (details.length == 0) { return details; }
    +
    +   var log = "";
    +   log = log + "\n - entity_id = " + details[0];
    +   log = log + "\n - entity_key = " + details[1];
    +   log = log + "\n - entity_name = " + details[2];
    +   log = log + "\n - entity_namespace = " + details[3];
    +   log = log + "\n - entity_tablename = " + details[4];
    +   log = log + "\n - entity_description = " + details[5];
    +   log = log + "\n - entity_is_read_only = " + details[6];
    +   log = log + "\n - entity_is_abstract = " + details[7];
    +   log = log + "\n - entity_version = " + details[8];
    +   log = log + "\n - entity_primary_key_property_id = " + details[9];
    +   log = log + "\n - entity_list_of_properties_id = " + details[10];
    +   log = log + "\n - entity_has_triggers = " + details[11];
    +   log = log + "\n - entity_trigger_on_before_fetch = " + details[12];
    +   log = log + "\n - entity_trigger_on_after_fetch = " + details[13];
    +   log = log + "\n - entity_trigger_on_before_insert = " + details[14];
    +   log = log + "\n - entity_trigger_on_after_insert = " + details[15];
    +   log = log + "\n - entity_trigger_on_before_update = " + details[16];
    +   log = log + "\n - entity_trigger_on_after_update = " + details[17];
    +   log = log + "\n - entity_trigger_on_before_delete = " + details[18];
    +   log = log + "\n - entity_trigger_on_after_delete = " + details[19];
    +   log = log + "\n - entity_parent_id = " + details[20];
    +   log = log + "\n - entity_soft_delete_column = " + details[21];
    +   log = log + "\n - entity_validator_method = " + details[22];
    +
    +   print(log); // print value to the custom script debugger window
    +   helper.print(log); // print value to the standard output (for example, on Windows, use the 'DebugView' application to see all logs)
    +   return details;
    +}
    +
    +

    +
    +

    Parcourir la liste des propri�t�s d'une + entit�

    +
    + Voici comment parcourir la liste des propri�t�s de l'entit� courante : +

    +
    +
    var entity_id = params[13];
    +var entity_details = helper.getEntityDetails(entity_id);
    +var entity_list_of_properties_id = ((entity_details.length > 0) ? entity_details[10] : "");
    +var entity_list_of_properties_array = entity_list_of_properties_id.split("|");
    +for (var idx = 0; idx < entity_list_of_properties_array.length; idx++)
    +{
    +   var property_id = entity_list_of_properties_array[idx];
    +   var property_details = helper.getPropertyDetails(property_id);
    +   // ...
    +}
    +
    +

    +
    +

    Obtenir les informations associ�es � une propri�t� +

    +
    + Voici comment r�cup�rer le param�trage d'une propri�t�, + helper.getPropertyDetails(property_id) : +

    +
    +
    function printPropertyDetails(property_id)
    +{
    +   var details = helper.getPropertyDetails(property_id);
    +   if (details.length == 0) { return details; }
    +
    +   var log = "";
    +   log = log + "\n - property_id = " + details[0];
    +   log = log + "\n - property_key = " + details[1];
    +   log = log + "\n - property_name = " + details[2];
    +   log = log + "\n - property_column_name = " + details[3];
    +   log = log + "\n - property_description = " + details[4];
    +   log = log + "\n - property_type = " + details[5];
    +   log = log + "\n - property_version = " + details[6];
    +   log = log + "\n - property_entity_id = " + details[7];
    +   log = log + "\n - property_is_read_only = " + details[8];
    +   log = log + "\n - property_is_primary_key = " + details[9];
    +   log = log + "\n - property_is_serializable = " + details[10];
    +   log = log + "\n - property_is_transient = " + details[11];
    +   log = log + "\n - property_is_obsolete = " + details[12];
    +   log = log + "\n - property_is_index = " + details[13];
    +   log = log + "\n - property_is_unique = " + details[14];
    +   log = log + "\n - property_allow_null = " + details[15];
    +   log = log + "\n - property_order_level = " + details[16];
    +   log = log + "\n - property_default_value = " + details[17];
    +   log = log + "\n - property_format = " + details[18];
    +   log = log + "\n - property_force_sql_type = " + details[19];
    +   log = log + "\n - property_force_sql_alias = " + details[20];
    +   log = log + "\n - property_min_value = " + details[21];
    +   log = log + "\n - property_max_value = " + details[22];
    +   log = log + "\n - property_min_length = " + details[23];
    +   log = log + "\n - property_max_length = " + details[24];
    +   log = log + "\n - property_reg_exp = " + details[25];
    +   log = log + "\n - property_accessibility = " + details[26];
    +   log = log + "\n - property_is_relationship = " + details[27];
    +   log = log + "\n - property_relation_type = " + details[28];
    +   log = log + "\n - property_relation_entity_target_id = " + details[29];
    +   log = log + "\n - property_relation_inverse_property_id = " + details[30];
    +   log = log + "\n - property_relation_foreign_key = " + details[31];
    +   log = log + "\n - property_relation_foreign_key_owner = " + details[32];
    +   log = log + "\n - property_relation_extra_table = " + details[33];
    +   log = log + "\n - property_relation_type_desc = " + details[34];
    +
    +   print(log); // print value to the custom script debugger window
    +   helper.print(log); // print value to the standard output (for example, on Windows, use the 'DebugView' application to see all logs)
    +   return details;
    +}
    +
    +

    +
    +

    Obtenir + les informations associ�es � une �num�ration

    +
    + Voici comment r�cup�rer le param�trage de l'�num�ration courante, + helper.getEnumerationDetails(enumeration_id) : +

    +
    +
    var enumeration_id = params[15];
    +printEnumerationDetails(enumeration_id);
    +
    +//...
    +
    +function printEnumerationDetails(enumeration_id)
    +{
    +   var details = helper.getEnumerationDetails(enumeration_id);
    +   if (details.length == 0) { return details; }
    +
    +   var log = "";
    +   log = log + "\n - enumeration_id = " + details[0];
    +   log = log + "\n - enumeration_key = " + details[1];
    +   log = log + "\n - enumeration_name = " + details[2];
    +   log = log + "\n - enumeration_namespace = " + details[3];
    +   log = log + "\n - enumeration_description = " + details[4];
    +   log = log + "\n - enumeration_version = " + details[5];
    +   log = log + "\n - enumeration_use_qt_enum_macro = " + details[6];
    +   log = log + "\n - enumeration_list_of_keys = " + details[7];
    +   log = log + "\n - enumeration_list_of_values = " + details[8];
    +
    +   print(log); // print value to the custom script debugger window
    +   helper.print(log); // print value to the standard output (for example, on Windows, use the 'DebugView' application to see all logs)
    +   return details;
    +}
    +
    +

    +
    +

    Acc�s aux m�ta-donn�es d'une + entit�/propri�t�/�num�ration

    +
    + L'application QxEntityEditor permet de d�finir des m�ta-donn�es li�es � une entit�, propri�t� ou �num�ration. + Ces m�ta-donn�es peuvent �tre utilis�es par exemple pour d�finir du param�trage + suppl�mentaire non fourni par d�faut par QxEntityEditor. + Ces m�ta-donn�es sont accessibles dans le code C++ g�n�r� (moteur d'introspection de la biblioth�que QxOrm), et + sont accessibles �galement dans le moteur Javascript de QxEntityEditor. + Voici comment r�cup�rer des m�ta-donn�es en Javascript : +

    +
    +
    var entity_id = params[13];
    +var entity_meta_data = helper.getEntityMetaData(entity_id, "MY_ENTITY_META_DATA_KEY");
    +
    +// ...
    +
    +var property_meta_data = helper.getPropertyMetaData(property_id, "MY_PROPERTY_META_DATA_KEY");
    +
    +// ...
    +
    +var enumeration_id = params[15];
    +var enumeration_meta_data = helper.getEnumerationMetaData(enumeration_id, "MY_ENUM_META_DATA_KEY");
    +
    +

    +
    +

    Acc�der aux variables d'environnement

    +
    + Voici comment acc�der aux variables d'environnement, + helper.getEnvironmentVariable() et + helper.setEnvironmentVariable() : +

    +
    +
    var env_var = helper.getEnvironmentVariable("QT_DIR");
    +var set_env_var_ok = helper.setEnvironmentVariable("MY_ENV_VAR", "my_value");
    +
    +

    +
    +

    Gestion + des fichiers : lecture et �criture

    +
    + Le moteur de personnalisation des exports Javascript de QxEntityEditor permet de lire des + fichiers, et permet d'�crire dans des fichiers. + Les classes Javascript file et dir permettent d'instancier des + objets Javascript pour effectuer des traitements sur les fichiers. +

    + La classe file propose la m�me d�finition que la classe Qt QFile : +

    +
    +
    /*
    +   --- 'file' class methods available by script (QFile wrapper : http://doc.qt.io/qt-5/qfile.html) ---
    +
    +   bool copy(string fileName, string newName);
    +   bool exists(string fileName);
    +   bool link(string fileName, string linkName);
    +   bool remove(string fileName);
    +   bool rename(string oldName, string newName);
    +   string readAll(string fileName);
    +
    +   void setFileName(string name);
    +   string fileName();
    +   bool open(int mode); // enum QIODevice::OpenMode, for example : 26 = (QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text) = (2 + 8 + 16)
    +   int error(); // enum QFile::FileError
    +   void close();
    +   bool atEnd();
    +   string readLine();
    +   void write(string text);
    +*/
    +
    +

    + La classe dir propose la m�me d�finition que la classe Qt QDir : +

    +
    +
    /*
    +   --- 'dir' class methods available by script (QDir wrapper : http://doc.qt.io/qt-5/qdir.html) ---
    +
    +   void setPath(string path);
    +   string path();
    +   string appPath();
    +   string homePath();
    +   string rootPath();
    +   string tempPath();
    +
    +   string fromNativeSeparators(string pathName);
    +   string toNativeSeparators(string pathName);
    +
    +   string cleanPath(string path);
    +   bool isAbsolutePath(string path);
    +   bool isRelativePath(string path);
    +   bool match(string filter, string fileName);
    +
    +   bool mkdir(string dirName);
    +   bool mkpath(string dirPath);
    +   bool rmdir(string dirName);
    +   bool rmpath(string dirPath);
    +   bool exists(string name);
    +
    +   bool cdUp();
    +   bool cd(string dirName);
    +   string absoluteFilePath(string fileName);
    +   string absolutePath();
    +   string canonicalPath();
    +   string dirName();
    +   string filePath(string fileName);
    +   void refresh();
    +   string relativeFilePath(string fileName);
    +
    +   bool isAbsolute();
    +   bool isReadable();
    +   bool isRelative();
    +   bool isRoot();
    +*/
    +
    +

    + Exemple : lire le contenu d'un fichier : +

    +
    +
    var f1 = new file();
    +var f1_content = f1.readAll("C:\\Temp\\my_file.txt");
    +
    +

    + Autre exemple : �crire dans un fichier : +

    +
    +
    var f2 = new file();
    +f2.setFileName("C:\\Temp\\file_generated_by_script.txt");
    +f2.open(26); // (QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text) = (2 + 8 + 16)
    +f2.write("aaa");
    +f2.write("bbb");
    +f2.close();
    +
    +

    +
    +

    Obtenir + la liste des toutes les entit�s/�num�rations d'un projet

    +
    + Voici comment r�cup�rer la liste de toutes les entit�s et �num�rations d'un projet + *.qxee : +

    +
    +
    var listOfAllEntities = helper.getListOfAllEntities(); // 'listOfAllEntities' variable is an array, each item of this array contains : <entity_id>|<entity_name>
    +var listOfAllEnums = helper.getListOfAllEnums(); // 'listOfAllEnums' variable is an array, each item of this array contains : <enum_id>|<enum_name>
    +
    +

    +
    +

    R�cup�rer + le param�trage de l'application (niveau global, projet et plugin)

    +
    + Voici comment r�cup�rer tout le param�trage d�fini au niveau de l'application (au format + JSON) : + +
    +
    +
    var globalSettings = helper.getQxEEGlobalSettingsJson();    // the result is a string in JSON format ==> so just use JSON.parse() function to get a javascript object instance
    +var projectSettings = helper.getQxEEProjectSettingsJson();  // the result is a string in JSON format ==> so just use JSON.parse() function to get a javascript object instance
    +var pluginSettings = helper.getQxEEPluginSetingsJson();     // the result is a string in JSON format ==> so just use JSON.parse() function to get a javascript object instance
    +
    +print(globalSettings); helper.print(globalSettings);
    +print(projectSettings); helper.print(projectSettings);
    +print(pluginSettings); helper.print(pluginSettings);
    +
    +globalSettings = JSON.parse(globalSettings);    // Now 'globalSettings' is a javascript object instance
    +projectSettings = JSON.parse(projectSettings);  // Now 'projectSettings' is a javascript object instance
    +pluginSettings = JSON.parse(pluginSettings);    // Now 'pluginSettings' is a javascript object instance
    +
    +

    +
    +
    +

    Ajout d'une + action (placeholder) personnalis�e dans le template d'export C++

    +
    + Les plugins d'export C++ disposent d'un param�tre permettant de + d�finir le code source des fichiers Header *.h et Source *.cpp. + L'application QxEntityEditor propose plusieurs template par d�faut, et il est possible d'�crire + son propre template (option � Custom �). + Cette d�finition des fichiers Header *.h et Source *.cpp utilisent + des mots-cl� (placeholder sous la forme @@ACTION@@) pour remplacer le code par la + valeur correspondante. + Cette notion de placeholder est la base du moteur de + personnalisation Javascript. + Il est possible de renseigner des placeholder + personnalis�s en prefixant par @@CUSTOM_, par exemple : + @@CUSTOM_MY_ACTION@@. +

    + Voici comment tester le code action courant (placeholder) dans le moteur Javascript : +

    +
    + /* you can define your own placeholder in the template, it must start + with @@CUSTOM_, for example : @@CUSTOM_MY_ACTION@@ + ==> then, in the custom script, check if you are processing your custom action with this + code : */ +
    var action = params[5];
    +if (action == "CUSTOM_MY_ACTION")
    +{
    +   return "quit with my custom code here";
    +}
    +
    +

    +
    +

    Exemple + de script : ajout automatique de la d�finition Q_PROPERTY sur les propri�t�s C++ g�n�r�es +

    +
    + Voici un exemple de script document� (fourni dans le dossier ./samples/q_property.js du + package QxEntityEditor) pour montrer comment ajouter automatiquement la d�finition Q_PROPERTY pour chacune des + propri�t�s du projet *.qxee : +

    +
    +
    ({
    +
    +/* ----------------------------------------------------------------------------------------
    +   ----------------------------------------------------------------------------------------
    +   'q_property.js' : custom javascript file to customize QxEntityEditor C++ export process.
    +   This script is an example to show how to use QxEntityEditor javascript engine to add a Q_PROPERTY definition for each property generated by QxEntityEditor.
    +   More details about Q_PROPERTY macro on Qt web site : http://doc.qt.io/qt-5/properties.html
    +
    +   To use this javascript file :
    +      1- go to the main menu of QxEntityEditor 'Tools >> Export to C++ project (settings)' ;
    +      2- select the C++ template 'qx::IxPersistable + QObject' : now generated entities will inherit from QObject, which is required to use the Qt Q_PROPERTY macro ;
    +      3- in the field 'Custom script file' : put the location of this 'q_property.js' custom javascript file ;
    +      4- save the settings and start the C++ export process ;
    +      5- check generated files : Q_PROPERTY should be added automatically by the export process.
    +   ----------------------------------------------------------------------------------------
    +   ---------------------------------------------------------------------------------------- */
    +
    +// Here is the entry point of the QxEntityEditor javascript engine
    +// This function is called for each placeholder defined in the C++ template section
    +customProcess : function(params)
    +{
    +   try
    +   {
    +      // We use here the @@MACRO_QX_PERSISTABLE_HPP@@ placeholder which is present in the default C++ template 'qx::IxPersistable + QObject'
    +      // You could also create your own custom C++ template (based on 'qx::IxPersistable + QObject' template), and create your own placeholder (which must be prefixed by @@CUSTOM_), for example : @@CUSTOM_Q_PROPERTY@@
    +      var action = params[5];
    +      if (action != "MACRO_QX_PERSISTABLE_HPP") { return params[0]; } // quit with 'params[0]' means : doesn't change the default export behaviour
    +
    +      // Check if we have an entity
    +      var entity_id = params[13];
    +      if ((entity_id == "") || (entity_id == "0")) { return params[0]; } // quit with 'params[0]' means : doesn't change the default export behaviour
    +
    +      // Get the list of properties
    +      var entity_details = helper.getEntityDetails(entity_id);
    +      var entity_list_of_properties_id = ((entity_details.length > 0) ? entity_details[10] : "");
    +      var entity_list_of_properties_array = entity_list_of_properties_id.split("|");
    +      if (entity_list_of_properties_array.length <= 0) { return params[0]; } // quit with 'params[0]' means : doesn't change the default export behaviour
    +
    +      // Prepare output string
    +      var output = params[0] + "\n";
    +
    +      // Iterate over each property
    +      for (var idx = 0; idx < entity_list_of_properties_array.length; idx++)
    +      {
    +         // Get property details
    +         var property_id = entity_list_of_properties_array[idx];
    +         var property_details = helper.getPropertyDetails(property_id);
    +
    +         // Here you could also get property meta-data that you can define in QxEntityEditor, property parameters screen, section 'List of meta-data'
    +         // This is a way to manage your own property parameters which are not a part of QxEntityEditor
    +         // It could be useful for example to manage some Q_PROPERTY settings like : RESET, NOTIFY, REVISION, DESIGNABLE, SCRIPTABLE, FINAL, etc...
    +         // To get a property meta-data value, just write this code : var my_meta_data = helper.getPropertyMetaData(property_id, "MY_META_DATA_KEY");
    +
    +         // Get property type and name
    +         var property_type = property_details[5];
    +         var property_name = property_details[2];
    +
    +         // Check if property type can be used with Q_PROPERTY macro
    +         if (list_of_compatible_property_type.indexOf(property_type) == -1) { continue; }
    +
    +         // Create Q_PROPERTY definition
    +         output += "\n   Q_PROPERTY(" + property_type + " " + property_name + " READ get" + property_name + " WRITE set" + property_name + ")";
    +      }
    +
    +      return output;
    +   }
    +   catch (err)
    +   { return ("[CustomScriptError] an unexpected error occurred : " + err); }
    +}
    +
    +});
    +
    +// Here is a list of C++ types compatible with Qt Q_PROPERTY macro (C++ type can be converted to/from QVariant)
    +// You can of course register your own C++ types to be able to use them with Q_PROPERTY macro (using Qt Q_DECLARE_METATYPE() macro)
    +// So the following array is not fixed : you can add all C++ types you want...
    +var list_of_compatible_property_type = [ "QBitArray", "QBitmap", "bool", "QBrush", "QByteArray", "QChar", "QColor", "QDate", "QDateTime", "double", 
    +                                         "QUuid", "QFont", "QVariantHash", "QIcon", "QImage", "int", "QLine", "QLineF", "QVariantList", "qlonglong", 
    +                                         "QVariantMap", "QMatrix", "QMatrix4x4", "QPixmap", "QPoint", "QPointF", "QPolygon", "QPolygonF", "QRect", "QRectF", 
    +                                         "QRegExp", "QRegion", "QSize", "QSizeF", "QString", "QStringList", "QTime", "uint", "qulonglong", "QUrl", 
    +                                         "QVector2D", "QVector3D", "QVector4D" ];
    +
    +

    +
    +

    Activation du + d�bogueur Javascript int�gr�

    +
    + Le moteur Javascript de personnalisation des exports de QxEntityEditor int�gre un d�bogueur. + Cet environnement de d�bogage dispose de toutes les fonctionnalit�s n�cessaires pour vous aider + � d�velopper et corriger vos scripts : +
      +
    • d�finition de points d'arr�t (breakpoints) ;
    • +
    • mode pas � pas pour passer d'une ligne de code � une autre pendant l'ex�cution du script + ;
    • +
    • visualisation des variables et de leur valeur au fil de l'ex�cution ;
    • +
    • affichage d'une fen�tre de logs.
    • +
    +
    + Par d�faut, le d�bogueur est d�sactiv� lors d'un export. Pour l'activer et afficher l'�cran + suivant, vous devez : +
      +
    • aller dans les param�tres d'export, d�finir + un script � utiliser, puis cliquer sur le bouton � Debug custom Javascript file � ; +
    • +
    • ou bien maintenir la touche SHIFT au lancement d'un export.
    • +
    +
    + JS debug +

    +
    +
    + +

    Ex�cution de scripts + personnalis�s avant/apr�s ex�cution d'un plugin

    +
    + L'application QxEntityEditor permet de d�finir des scripts de type *.bat (Windows), + *.sh (Linux) ou m�me des ex�cutables � lancer avant ou apr�s ex�cution d'un plugin + QxEntityEditor. + Chaque script (ou processus) est appel� avec un param�tre d'entr�e : le chemin d'acc�s au fichier + projet *.qxee. + Cette fonctionnalit� peut �tre utilis�e par exemple pour : +
      +
    • int�grer les fichiers g�n�r�s par un export QxEntityEditor dans un gestionnaire de code + source (Git, Perforce, CVS, etc...) ;
    • +
    • d�marrer la compilation d'un projet C++ g�n�r� par QxEntityEditor ;
    • +
    • ex�cuter des tests unitaires ou bien lancer une int�gration continue avec un serveur Jenkins + par exemple ;
    • +
    • modifier des valeurs dans la base de donn�es SQLite d'un projet + *.qxee suite � un import de base de donn�es.
    • +
    +
    + Pour d�finir les scripts ou ex�cutables � lancer suite � un processus d'import ou d'export de + QxEntityEditor, aller dans le menu principal : Tools >> Plugins scripts. +

    + Plugin scripts +

    + Remarque : pour d�finir un script � ex�cuter dans cette liste, il est possible de renseigner + le chemin d'acc�s absolu au script, ou bien un chemin relatif par rapport au projet *.qxee. + Par exemple, en mettant le chemin relatif ./my_script.sh, le fichier my_script.sh + doit se trouver dans le m�me r�pertoire que le fichier projet *.qxee de QxEntityEditor. +

    +
    + +

    Ex�cuter + QxEntityEditor en ligne de commande

    +
    + L'application QxEntityEditor peut �tre d�marr�e en ligne de commande. Nous allons d�tailler dans ce + chapitre quelques exemples d'appels : +


    + -- Exemple n�1 : d�marrer QxEntityEditor en sp�cifiant un projet *.qxee � charger + (param�tre --project) : +
    +
    + +
    QxEntityEditor --project="c:\test\qxBlog.qxee"
    +
    +
    +

    + -- Exemple n�2 : d�marrer QxEntityEditor sans afficher l'interface graphique (param�tre + --no_gui), en sp�cifiant un projet *.qxee � charger (param�tre --project) et + en d�marrant automatiquement un processus d'export C++ (param�tre --plugin) : +
    +
    + +
    QxEntityEditor --no_gui --project="c:\test\qxBlog.qxee" --plugin=QxEECppExport
    +
    +
    +

    + -- Exemple n�3 : d�marrer QxEntityEditor en mode visionneuse (param�tre + --viewer_mode), ce mode permet d'ouvrir QxEntityEditor en mode lecture seule et permet de + charger des projets *.qxee sans cl� de licence : +
    +
    + +
    QxEntityEditor --viewer_mode
    +
    +
    +

    + -- Exemple n�4 : d�marrer QxEntityEditor avec affichage des logs SQL (param�tre + --log_sql), ce param�tre permet de tracer toutes les requ�tes SQL effectu�es sur la base de donn�es SQLite du projet *.qxee : +
    +
    + +
    QxEntityEditor --log_sql
    +
    +
    +

    + -- Exemple n�5 : d�marrer QxEntityEditor en chargeant un projet *.qxee � partir d'un + fichier JSON : +
    +
    + +
    QxEntityEditor --project="<path_to_your_qxee_project_file>" --plugin=QxEEJsonImport --QxEEJsonImport_file="<path_to_your_json_file>"
    +
    +
    +

    + -- Exemple n�6 : afficher la documentation de tous les param�tres en ligne de commande : +
    +
    + +
    QxEntityEditor --?
    +
    +
    +
    + Ce qui produit le r�sultat suivant : +
    +
    + +
    *** QxEntityEditor 1.2.5 application global command line parameters ***
    +
    +  --project="<full path to *.qxee project file>" : run QxEntityEditor defining a *.qxee project to load at startup
    +  --no_gui : run QxEntityEditor without displaying the user interface
    +  --viewer_mode : run QxEntityEditor in read-only mode, this parameter can be used to open large *.qxee projects without any registered license key
    +  --log_sql : run QxEntityEditor tracing all SQL logs, this parameter logs all SQL queries executed to *.qxee SQLite database
    +  --font : define application font (due to issues since macOS Catalina 10.15) with syntax <family>||<pointSize>||<weight>||<italic> (only <family> is required), for example : Courier New||14
    +  --style_sheet : define application style sheet (more details here : https://doc.qt.io/qt-5/stylesheet-reference.html), for example : QWidget { background-color: black }
    +  --plugin=<plugin name> : run QxEntityEditor and execute automatically a plugin process (see below for specific parameters per plugin)
    +
    +*** Import plugin QxEEJsonImport ***
    +
    +  --QxEEJsonImport_file="<full path to your json file>" : [Required] run QxEntityEditor loading a *.qxee project from a JSON file
    +
    +*** Import plugin QxEEMySQLImport ***
    +
    +  --QxEEMySQLImport_db_ip="<DB IP>" : [Required] Database server address (IP)
    +  --QxEEMySQLImport_db_port="<DB port>" : [Required] Port number to connect to database
    +  --QxEEMySQLImport_db_name="<DB name>" : [Required] Database name
    +  --QxEEMySQLImport_filter_regexp="<Regular Expression>" : [Optional] Filter to select tables from database to import
    +  --QxEEMySQLImport_login="<DB Login>" : [Optional] Login to connect to database
    +  --QxEEMySQLImport_pwd="<DB Password>" : [Optional] Password to connect to database
    +  --QxEEMySQLImport_namespace="<Namespace>" : [Optional] C++ namespace where imported classes will be located
    +  --QxEEMySQLImport_delete_namespace=0/1 : [Optional] Delete all entities in the namespace before importing
    +  --QxEEMySQLImport_import_comment=0/1 : [Optional] Import tables/columns comment to entities/properties description
    +  --QxEEMySQLImport_import_default_value=0/1 : [Optional] Import columns default value
    +  --QxEEMySQLImport_boost_optional=0/1 : [Optional] Add boost::optional<T> decoration if a column definition allows NULL value
    +  --QxEEMySQLImport_organize_diagram=0/1 : [Optional] Organize diagram layout after import process
    +  --QxEEMySQLImport_relation_decoration=<numeric value> : [Optional] Decoration used for relationships (0=no decoration, 1=boost::shared_ptr, 2=QSharedPointer, 5=std::shared_ptr)
    +  --QxEEMySQLImport_relation_collection=<numeric value> : [Optional] Collection used for relationships (1=qx::QxCollection, 2=std::vector, 3=std::list, 8=QHash, 10=QList)
    +  --QxEEMySQLImport_mapping_sql_to_cpp="sql1~cpp1;sql2~cpp2;etc..." : [Optional] List of mappings from SQL type to C++ type
    +  --QxEEMySQLImport_verbose=0/1 : [Optional] Display more details during the import process
    +
    +*** Import plugin QxEEOdbcImport ***
    +
    +  --QxEEOdbcImport_dsn="<ODBC DSN>" : [Required] ODBC DSN to connect to database
    +  --QxEEOdbcImport_db_type=<numeric value> : [Required] Database engine type (0=generic, 1=postgresql, 2=mysql, 3=oracle, 4=mssqlserver, 5=sqlite)
    +  --QxEEOdbcImport_filter_regexp="<Regular Expression>" : [Optional] Filter to select tables from database to import
    +  --QxEEOdbcImport_login="<DB Login>" : [Optional] Login to connect to database
    +  --QxEEOdbcImport_pwd="<DB Password>" : [Optional] Password to connect to database
    +  --QxEEOdbcImport_namespace="<Namespace>" : [Optional] C++ namespace where imported classes will be located
    +  --QxEEOdbcImport_delete_namespace=0/1 : [Optional] Delete all entities in the namespace before importing
    +  --QxEEOdbcImport_import_comment=0/1 : [Optional] Import tables/columns comment to entities/properties description
    +  --QxEEOdbcImport_import_default_value=0/1 : [Optional] Import columns default value
    +  --QxEEOdbcImport_boost_optional=0/1 : [Optional] Add boost::optional<T> decoration if a column definition allows NULL value
    +  --QxEEOdbcImport_organize_diagram=0/1 : [Optional] Organize diagram layout after import process
    +  --QxEEOdbcImport_relation_decoration=<numeric value> : [Optional] Decoration used for relationships (0=no decoration, 1=boost::shared_ptr, 2=QSharedPointer, 5=std::shared_ptr)
    +  --QxEEOdbcImport_relation_collection=<numeric value> : [Optional] Collection used for relationships (1=qx::QxCollection, 2=std::vector, 3=std::list, 8=QHash, 10=QList)
    +  --QxEEOdbcImport_mapping_sql_to_cpp="sql1~cpp1;sql2~cpp2;etc..." : [Optional] List of mappings from SQL type to C++ type
    +  --QxEEOdbcImport_verbose=0/1 : [Optional] Display more details during the import process
    +
    +*** Import plugin QxEEPostgreSQLImport ***
    +
    +  --QxEEPostgreSQLImport_db_ip="<DB IP>" : [Required] Database server address (IP)
    +  --QxEEPostgreSQLImport_db_port="<DB port>" : [Required] Port number to connect to database
    +  --QxEEPostgreSQLImport_db_name="<DB name>" : [Required] Database name
    +  --QxEEPostgreSQLImport_filter_regexp="<Regular Expression>" : [Optional] Filter to select tables from database to import
    +  --QxEEPostgreSQLImport_login="<DB Login>" : [Optional] Login to connect to database
    +  --QxEEPostgreSQLImport_pwd="<DB Password>" : [Optional] Password to connect to database
    +  --QxEEPostgreSQLImport_namespace="<Namespace>" : [Optional] C++ namespace where imported classes will be located
    +  --QxEEPostgreSQLImport_delete_namespace=0/1 : [Optional] Delete all entities in the namespace before importing
    +  --QxEEPostgreSQLImport_import_comment=0/1 : [Optional] Import tables/columns comment to entities/properties description
    +  --QxEEPostgreSQLImport_import_default_value=0/1 : [Optional] Import columns default value
    +  --QxEEPostgreSQLImport_boost_optional=0/1 : [Optional] Add boost::optional<T> decoration if a column definition allows NULL value
    +  --QxEEPostgreSQLImport_organize_diagram=0/1 : [Optional] Organize diagram layout after import process
    +  --QxEEPostgreSQLImport_relation_decoration=<numeric value> : [Optional] Decoration used for relationships (0=no decoration, 1=boost::shared_ptr, 2=QSharedPointer, 5=std::shared_ptr)
    +  --QxEEPostgreSQLImport_relation_collection=<numeric value> : [Optional] Collection used for relationships (1=qx::QxCollection, 2=std::vector, 3=std::list, 8=QHash, 10=QList)
    +  --QxEEPostgreSQLImport_mapping_sql_to_cpp="sql1~cpp1;sql2~cpp2;etc..." : [Optional] List of mappings from SQL type to C++ type
    +  --QxEEPostgreSQLImport_verbose=0/1 : [Optional] Display more details during the import process
    +
    +*** Import plugin QxEESQLiteImport ***
    +
    +  --QxEESQLiteImport_db_path="<DB File Path>" : [Required] SQLite database file path
    +  --QxEESQLiteImport_filter_regexp="<Regular Expression>" : [Optional] Filter to select tables from database to import
    +  --QxEESQLiteImport_namespace="<Namespace>" : [Optional] C++ namespace where imported classes will be located
    +  --QxEESQLiteImport_delete_namespace=0/1 : [Optional] Delete all entities in the namespace before importing
    +  --QxEESQLiteImport_import_comment=0/1 : [Optional] Import tables/columns comment to entities/properties description
    +  --QxEESQLiteImport_import_default_value=0/1 : [Optional] Import columns default value
    +  --QxEESQLiteImport_boost_optional=0/1 : [Optional] Add boost::optional<T> decoration if a column definition allows NULL value
    +  --QxEESQLiteImport_organize_diagram=0/1 : [Optional] Organize diagram layout after import process
    +  --QxEESQLiteImport_relation_decoration=<numeric value> : [Optional] Decoration used for relationships (0=no decoration, 1=boost::shared_ptr, 2=QSharedPointer, 5=std::shared_ptr)
    +  --QxEESQLiteImport_relation_collection=<numeric value> : [Optional] Collection used for relationships (1=qx::QxCollection, 2=std::vector, 3=std::list, 8=QHash, 10=QList)
    +  --QxEESQLiteImport_mapping_sql_to_cpp="sql1~cpp1;sql2~cpp2;etc..." : [Optional] List of mappings from SQL type to C++ type
    +  --QxEESQLiteImport_verbose=0/1 : [Optional] Display more details during the import process
    +
    +*** Export plugin QxEECppExport ***
    +
    +  !!! Note : this plugin loads automatically previous parameters values before starting the export process, but you can use following parameters to override them !!!
    +  --QxEECppExport_path="<Export Path>" : [Optional] C++ export path parameter
    +  --QxEECppExport_template_type=<numeric value> : [Optional] C++ template type selected to build C++ files (0=no_inheritance, 1=ix_persistable, 2=qx_persistable, 3=ix_persistable_and_q_object, 4=custom)
    +  --QxEECppExport_template_header="<Template Header>" : [Optional] Custom C++ template header file path (QxEECppExport_template_type parameter must be equal to 4, which means custom)
    +  --QxEECppExport_template_source="<Template Source>" : [Optional] Custom C++ template source file path (QxEECppExport_template_type parameter must be equal to 4, which means custom)
    +  --QxEECppExport_qxorm_relative_path=0/1 : [Optional] Relative path to QxOrm library
    +  --QxEECppExport_generate_custom_files=0/1 : [Optional] Generate a custom directory with custom files for each entity
    +  --QxEECppExport_custom_javacript="<Custom JS>" : [Optional] Custom script (javascript file) to change the default behaviour of the export process
    +
    +*** Export plugin QxEECppModelViewExport ***
    +
    +  !!! Note : this plugin loads automatically previous parameters values before starting the export process, but you can use following parameters to override them !!!
    +  --QxEECppModelViewExport_path="<Export Path>" : [Optional] C++ model/view export path parameter
    +  --QxEECppModelViewExport_namespace="<Namespace>" : [Optional] Namespace where to put all model/view classes
    +  --QxEECppModelViewExport_template_type=<numeric value> : [Optional] C++ model/view template type selected to build C++ files (0=default, 1=custom, 2=qx_model_service)
    +  --QxEECppModelViewExport_template_header="<Template Header>" : [Optional] Custom C++ template header file path (QxEECppModelViewExport_template_type parameter must be equal to 1, which means custom)
    +  --QxEECppModelViewExport_template_source="<Template Source>" : [Optional] Custom C++ template source file path (QxEECppModelViewExport_template_type parameter must be equal to 1, which means custom)
    +  --QxEECppModelViewExport_generate_custom_files=0/1 : [Optional] Generate a custom directory with custom files for each entity
    +  --QxEECppModelViewExport_custom_javacript="<Custom JS>" : [Optional] Custom script (javascript file) to change the default behaviour of the export process
    +
    +*** Export plugin QxEECppServicesExport ***
    +
    +  !!! Note : this plugin loads automatically previous parameters values before starting the export process, but you can use following parameters to override them !!!
    +  --QxEECppServicesExport_path="<Export Path>" : [Optional] C++ services export path parameter
    +  --QxEECppServicesExport_namespace="<Namespace>" : [Optional] Namespace where to put all services classes
    +  --QxEECppServicesExport_template_type=<numeric value> : [Optional] C++ services template type selected to build C++ files (0=default, 1=custom)
    +  --QxEECppServicesExport_template_header="<Template Header>" : [Optional] Custom C++ template header file path (QxEECppServicesExport_template_type parameter must be equal to 1, which means custom)
    +  --QxEECppServicesExport_template_source="<Template Source>" : [Optional] Custom C++ template source file path (QxEECppServicesExport_template_type parameter must be equal to 1, which means custom)
    +  --QxEECppServicesExport_generate_custom_files=0/1 : [Optional] Generate a custom directory with custom files for each entity
    +  --QxEECppServicesExport_custom_javacript="<Custom JS>" : [Optional] Custom script (javascript file) to change the default behaviour of the export process
    +  --QxEECppServicesExport_generate_server_app=0/1 : [Optional] Generate a sample server application
    +  --QxEECppServicesExport_server_app_path="<Server App Path>" : [Optional] Server application location
    +
    +*** Export plugin QxEEGenericDDLExport ***
    +
    +  !!! Note : this plugin loads automatically previous parameters values before starting the export process, but you can use following parameters to override them !!!
    +  --QxEEGenericDDLExport_path="<Export Path>" : [Optional] DDL export path parameter
    +  --QxEEGenericDDLExport_db_type=<numeric value> : [Optional] Database type (0=default, 1=sqlite, 2=mysql, 3=postgresql, 4=oracle, 5=mssqlserver)
    +  --QxEEGenericDDLExport_relation_as_fk=0/1 : [Optional] Export relationships as foreign keys in database
    +  --QxEEGenericDDLExport_schema_type=<numeric value> : [Optional] Way to export database schema (0=full, 1=evolution, 2=full_and_evolution)
    +  --QxEEGenericDDLExport_custom_javacript="<Custom JS>" : [Optional] Custom script (javascript file) to change the default behaviour of the export process
    +
    +*** Export plugin QxEEPrinter ***
    +
    +  !!! Note : this plugin loads automatically previous parameters values before starting the export process !!!
    +
    +*** Export plugin QxEEXmlExport ***
    +
    +  !!! Note : this plugin loads automatically previous parameters values before starting the export process, but you can use following parameters to override them !!!
    +  --QxEEXmlExport_path="<Export Path>" : [Optional] XML or JSON export path parameter
    +  --QxEEXmlExport_as_json=0/1 : [Optional] Export project as JSON format
    +
    +
    +
    +
    + Remarque : pour faire apparaitre les logs g�n�r�s par l'application QxEntityEditor : +
      +
    • Windows : utiliser un outil externe comme par exemple DebugView ;
    • +
    • Linux : d�marrer QxEntityEditor depuis un terminal ;
    • +
    • Mac OS X : d�marrer QxEntityEditor depuis un terminal en allant dans le sous-dossier + QxEntityEditor.app, par exemple : cd QxEntityEditor.app/Content/MacOS/, puis : + ./QxEntityEditor +
    • +
    +

    + -- Exemple n�7 : importer en ligne de commande un sch�ma de base de donn�es par ODBC (plugin + QxEEOdbcImport) : +
    +
    + +
    QxEntityEditor --no_gui --project="<project_path>" --plugin=QxEEOdbcImport --QxEEOdbcImport_db_type=1 --QxEEOdbcImport_dsn="<your_dsn>" --QxEEOdbcImport_login="<your_login>" --QxEEOdbcImport_pwd="<your_password>" --QxEEOdbcImport_delete_namespace=1
    +
    +
    +

    + -- Exemple n�8 : importer en ligne de commande un sch�ma de base de donn�es PostgreSQL + (plugin QxEEPostgreSQLImport) : +
    +
    + +
    QxEntityEditor --no_gui --project="<project_path>" --plugin=QxEEPostgreSQLImport --QxEEPostgreSQLImport_db_ip="<ip>" --QxEEPostgreSQLImport_db_port=5432 --QxEEPostgreSQLImport_db_name="<dbname>" --QxEEPostgreSQLImport_login="<your_login>" --QxEEPostgreSQLImport_pwd="<your_password>" --QxEEPostgreSQLImport_delete_namespace=1
    +
    +
    +

    + -- Exemple n�9 : exporter en ligne de commande un projet QxEntityEditor associ� � un + gestionnaire de code source Git, Perforce, CVS, etc. (plugin QxEESourceControlExport) : +
    +
    + +
    QxEntityEditor --no_gui --project="<path_to_your_qxee_project_file>" --plugin=QxEESourceControlExport --QxEESourceControlExport_path="<path_to_your_export_output_directory>"
    +
    +
    +

    + -- Exemple n�10 : importer en ligne de commande un projet QxEntityEditor associ� � un + gestionnaire de code source Git, Perforce, CVS, etc. (plugin QxEESourceControlImport) : +
    +
    + +
    QxEntityEditor --project="<path_to_your_qxee_project_file>" --plugin=QxEESourceControlImport --QxEESourceControlImport_path="<path_to_the_root_source_control_json_file>"
    +
    +
    +

    +
    + +
    +
    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/doc/qxorm_fr/model.html b/doc/qxorm_fr/model.html new file mode 100644 index 0000000..5c759d8 --- /dev/null +++ b/doc/qxorm_fr/model.html @@ -0,0 +1,177 @@ + + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic editor + (data model designer and source code generator) + + + + + + + + + + + + + + + + + + + + + +
    QxOrm + + + + Windows + Linux + Macintosh + C++
    +
    + + + + + + + + + + + + + + + + + + +
    AccueilT�l�chargementExemple rapide + Tutoriel (4) + + + + + + +
    + +
    +
    + Manuel (2) + + + + + + +
    + +
    +
    ForumNos clients
    +
    + + + + + + + + + + + + + + + + + +
    + QxOrm >> [model] + + + + + + + + + + + + + + +
    + Version courante :  + + QxOrm 1.5.0 - documentation en ligne de la biblioth�que QxOrm - GitHub +
    + + + QxEntityEditor 1.2.8 +
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    +
    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/doc/qxorm_fr/quick_sample.html b/doc/qxorm_fr/quick_sample.html new file mode 100644 index 0000000..2f2a2ef --- /dev/null +++ b/doc/qxorm_fr/quick_sample.html @@ -0,0 +1,460 @@ + + + + + + QxOrm : C++ Qt ORM Object Relational Mapping database library - QxEntityEditor : C++ Qt entities graphic editor + (data model designer and source code generator) + + + + + + + + + + + + + + + + + + + + + +
    QxOrm + + + + Windows + Linux + Macintosh + C++
    +
    + + + + + + + + + + + + + + + + + + +
    AccueilT�l�chargementExemple rapide + Tutoriel (4) + + + + + + +
    + +
    +
    + Manuel (2) + + + + + + +
    + +
    +
    ForumNos clients
    +
    + + + + + + + + + + + + + + + + + +
    + QxOrm >> Exemple rapide + + + + + + + + + + + + + + +
    + Version courante :  + + QxOrm 1.5.0 - documentation en ligne de la biblioth�que QxOrm - GitHub +
    + + + QxEntityEditor 1.2.8 +
    +
    Version fran�aise du siteWeb site english version
    + + + + + + + +
    + + + + + + + + + +
    Dans ce chapitre, nous allons traiter un exemple + rapide d'utilisation qui pr�sente les fonctionnalit�s de base de la + biblioth�que QxOrm.
    +
    + Remarque : la biblioth�que QxOrm utilise les conventions d'�criture de code C++ + suivantes : +
      +
    • toutes les classes, fonctions, variables, etc... sont d�finies sous le namespace qx +
    • +
    • les macro de QxOrm sont �crites sous la forme QX_... +
    • +
    • les classes abstraites (ou interfaces) sont pr�fix�es par Ix (par exemple + IxFactory est une interface pour la cr�ation d'instances) +
    • +
    • les autres classes sont pr�fix�es par Qx (par exemple QxDataMember) +
    • +
    • les collections d'objets ont pour suffixe X (par exemple QxDataMemberX est une + collection de QxDataMember) +
    • +
    • les fonctions pour communiquer avec les bases de donn�es se trouvent sous le namespace qx::dao (par exemple + qx::dao::fetch_by_id()) +
    • +
    • les fonctions pour la serialization des donn�es se trouvent sous le namespace qx::serialization + (par exemple qx::serialization::xml::to_file()) +
    • +
    • le moteur de reflection est accessible depuis la classe qx::QxClassX (par + exemple qx::QxClassX::invoke() pour invoquer une m�thode de classe) +
    • +
    • les classes de traits se trouvent sous le namespace qx::trait (par exemple + qx::trait::is_smart_ptr<T>) +
    • +
    +
    qt_ambassador
    + + QxOrm library has been accepted into the Qt Ambassador + Program + +
    + Autre Remarque : un exemple plus complet (avec notion d'h�ritage, de + relations entre tables, de collections, de libraries partag�es, de + memory leak, etc...) est pr�sent dans le dossier ./test/qxDllSample/ de la + distribution de QxOrm. Ce dossier ./test/qxDllSample/ contient 2 projets de + type dll et 1 projet de type ex�cutable : ./dll1/, + ./dll2/ et ./exe/.
    La solution compl�te peut �tre + compil�e avec Visual C++ 2008, 2010 ou 2012 sous Windows (ouvrir le fichier + ./test/qxDllSample/test.sln).
    + Elle peut �tre �galement compil�e avec GCC sous Linux et MinGW sous Windows en utilisant la + commande qmake.
    +
    + L'exemple qui suit pr�sente diff�rentes �tapes : + +
    + * + -----------------------------------------------------------------------------------------------------
    + * 1- fichier drug.h : d�finition d'une classe drug avec 3 + attributs : id, name et description

    + * + -----------------------------------------------------------------------------------------------------
    +

    + + + + + + + +
    +
    #ifndef _CLASS_DRUG_H_
    +#define _CLASS_DRUG_H_
    +
    +class drug
    +{
    +public:
    +   long id;
    +   QString name;
    +   QString description;
    +
    +   drug() : id(0) { ; }
    +   virtual ~drug() { ; }
    +};
    +
    +QX_REGISTER_HPP_MY_TEST_EXE(drug, qx::trait::no_base_class_defined, 1)
    +
    +/* This macro is necessary to register 'drug' class in QxOrm context */
    +/* param 1 : the current class to register => 'drug' */
    +/* param 2 : the base class, if no base class, use the qx trait => 'qx::trait::no_base_class_defined' */
    +/* param 3 : the class version used by serialization to provide 'ascendant compatibility' */
    +
    +#endif // _CLASS_DRUG_H_
    +
    +

    + * + ----------------------------------------------------------------------------------------------------
    + * 2- fichier drug.cpp : impl�mentation de la 'fonction de + param�trage' : void qx::register_class()
    + * + ---------------------------------------------------------------------------------------------------- +


    + + + + + + + +
    +
    #include "precompiled.h"   // Precompiled-header with '#include <QxOrm.h>' and '#include "export.h"'
    +#include "drug.h"          // Class definition 'drug'
    +#include <QxOrm_Impl.h>     // Automatic memory leak detection and boost serialization export macro
    +
    +QX_REGISTER_CPP_MY_TEST_EXE(drug)   // This macro is necessary to register 'drug' class in QxOrm context
    +
    +namespace qx {
    +template <> void register_class(QxClass<drug> & t)
    +{
    +  t.id(& drug::id, "id");               // Register 'drug::id' <=> primary key in your database
    +  t.data(& drug::name, "name", 1);      // Register 'drug::name' property with key 'name' and version '1'
    +  t.data(& drug::description, "desc");  // Register 'drug::description' property with key 'desc'
    +}}
    +
    +
    +
    + * + -----------------------------------------------------------------------------------------------
    + * 3- fichier main.cpp : pr�sentation des fonctionnalit�s de base + de QxOrm avec la classe drug
    + * + ----------------------------------------------------------------------------------------------- +


    + + + + + + + +
    +
    #include "precompiled.h"
    +#include "drug.h"
    +#include <QxOrm_Impl.h>
    +
    +int main(int argc, char * argv[])
    +{
    +   QApplication app(argc, argv); // Qt application
    +
    +   // Create 3 new drugs
    +   // It is possible to use 'std' and 'Qt' smart pointer : 'std::shared_ptr', 'QSharedPointer', etc...
    +   typedef std::shared_ptr<drug> drug_ptr;
    +   drug_ptr d1; d1.reset(new drug()); d1->name = "name1"; d1->description = "desc1";
    +   drug_ptr d2; d2.reset(new drug()); d2->name = "name2"; d2->description = "desc2";
    +   drug_ptr d3; d3.reset(new drug()); d3->name = "name3"; d3->description = "desc3";
    +
    +   // Insert drugs into container
    +   // It is possible to use a lot of containers from 'std', 'boost', 'Qt' and 'qx::QxCollection<Key, Value>'
    +   typedef std::vector<drug_ptr> type_lst_drug;
    +   type_lst_drug lst_drug;
    +   lst_drug.push_back(d1);
    +   lst_drug.push_back(d2);
    +   lst_drug.push_back(d3);
    +
    +   // Init parameters to communicate with a database
    +   qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE");
    +   qx::QxSqlDatabase::getSingleton()->setDatabaseName("./test_qxorm.db");
    +   qx::QxSqlDatabase::getSingleton()->setHostName("localhost");
    +   qx::QxSqlDatabase::getSingleton()->setUserName("root");
    +   qx::QxSqlDatabase::getSingleton()->setPassword("");
    +
    +   // Create table 'drug' into database to store drugs
    +   QSqlError daoError = qx::dao::create_table<drug>();
    +
    +   // Insert drugs from container to database
    +   // 'id' property of 'd1', 'd2' and 'd3' are auto-updated
    +   daoError = qx::dao::insert(lst_drug);
    +
    +   // Modify and update the second drug into database
    +   d2->name = "name2 modified";
    +   d2->description = "desc2 modified";
    +   daoError = qx::dao::update(d2);
    +
    +   // Delete the first drug from database
    +   daoError = qx::dao::delete_by_id(d1);
    +
    +   // Count drugs into database
    +   long lDrugCount = qx::dao::count<drug>();
    +
    +   // Fetch drug with id '3' into a new variable
    +   drug_ptr d_tmp; d_tmp.reset(new drug());
    +   d_tmp->id = 3;
    +   daoError = qx::dao::fetch_by_id(d_tmp);
    +
    +   // Export drugs from container to a file under xml format (serialization)
    +   qx::serialization::xml::to_file(lst_drug, "./export_drugs.xml");
    +
    +   // Import drugs from xml file into a new container
    +   type_lst_drug lst_drug_tmp;
    +   qx::serialization::xml::from_file(lst_drug_tmp, "./export_drugs.xml");
    +
    +   // Clone a drug
    +   drug_ptr d_clone = qx::clone(* d1);
    +
    +   // Create a new drug by class name (factory)
    +   qx::any d_any = qx::create("drug");
    +
    +   // Insert drugs container into 'qx::cache'
    +   qx::cache::set("drugs", lst_drug);
    +
    +   // Remove all elements from 'qx::cache'
    +   qx::cache::clear();
    +
    +   // Create a dummy memory leak
    +   drug * pDummy = new drug();
    +
    +   return 0;
    +}
    +
    +
    +

    + * + -------------------------------------------------------------------------
    + * 4- ex�cution du programme de test et affichage des traces g�n�r�es
    + * + -------------------------------------------------------------------------
    +

    +

    +

    + [QxOrm] qx::QxSqlDatabase : create new database + connection in thread '3616' with key + '{d315250c-b5c9-46e0-9402-f800368a6673}'
    + [QxOrm] sql query (78 ms) : CREATE TABLE drug (id INTEGER NOT NULL + PRIMARY KEY AUTOINCREMENT, name TEXT, desc TEXT)
    + [QxOrm] sql query (63 ms) : INSERT INTO drug (name, desc) VALUES + (:name, :desc)
    + [QxOrm] sql query (62 ms) : UPDATE drug SET id = :id, name = :name, + desc = :desc WHERE id = :id_bis
    + [QxOrm] sql query (63 ms) : DELETE FROM drug WHERE id = :id
    + [QxOrm] sql query (0 ms) : SELECT COUNT(*) FROM drug
    + [QxOrm] sql query (0 ms) : SELECT drug.id AS drug_id_0, drug.name AS + drug_name_0, drug.desc AS drug_desc_0 FROM drug WHERE drug_id_0 = + :id
    + [QxOrm] Leaked object at 0xf52ad8 (size 16, src\main.cpp:74)
    + [QxOrm] **** 1 memory leaks found ****

    +
    +

    + * + ------------------------------------------------------------------------------
    + * 5- contenu du fichier ./export_drugs.xml cr�� par le programme + de test
    + * + ------------------------------------------------------------------------------
    +

    +
    + quick_sample.export_drugs.xml +
    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/doc/qxorm_fr/resource/ES.png b/doc/qxorm_fr/resource/ES.png new file mode 100644 index 0000000000000000000000000000000000000000..805a1b66b448e51af64ffc7214adba1eae37184d GIT binary patch literal 605 zcmV-j0;2tiP)X00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy8FWQhbW?9;ba!ELWdKlNX>N2bPDNB8b~7$DE;K%k%ys|(0k%m* zK~z{r?Uy@C15p%&4>n0-B^K$d1HsnPA7Eo)r$0f#(n=v>Wn&>$DYOU(VM|jckiwK9 z2uYj5B3+PRJI-u27!t$YJiMD?7ltkN!FT8ExjV-!JyHi!2bOXGd@2dK!Da|YIuxCk6=oI#{i;#QQAq1R#(jCLMHau<25-?BFCI_zecfhDwE9TwoW=QGSCV zU{WQXUU8>$g?hanYB=0mx;e)0UCcqxK|vLW!h+*Im;%m5xzxc`1MWf&@~53d+;P^Hf|Qe*4Fu~$Gh^ouQ+I9m%m%Oa00000NkvXXu0mjfskrzI literal 0 HcmV?d00001 diff --git a/doc/qxorm_fr/resource/FR.png b/doc/qxorm_fr/resource/FR.png new file mode 100644 index 0000000000000000000000000000000000000000..35e3aaaf1dc70a96e1f7c794ccb7ef16678f1547 GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<^!3HGb?#A2&QY`6?zK#qG*KS<#k1zuAB}-f* zN`mv#O3D+9QW+dm@{>{(JaZG%Q-e|yQz{EjrrIztF#349IEGZ*O8WEvzy09^HU`Ec zM+{CdIxz57u`{t;SWvD2WeRYcIl8$WV?1ejfai3F9-DxJPQ?`AM1_fF#zsP6Oq>#l z3SYUq>X~K#@GCU1MT@Lv+NdM&nbA<&g8|5&&8*uPA>HBHID=P(0jRb4Ok-BSFT*p9 z42&i>oB}wc_R1J+X9U`(pT@wHalu*usAn%TBe~2D)_UpbkJ^lm0_`ra4B)v{u!TKI zSEJs6!GYmn(F1#he~d@wH~f&5z5AIj>pA}lX$b}*q>ImyNc?ulAo30&l%8&ky)v1TvZMThNwSvVA8_)?m_t-7&bM%>Q7@>Sb~6%H`` YW3q^@`SEH6Fz^{XUHx3vIVCg!0FGdgR{#J2 literal 0 HcmV?d00001 diff --git a/doc/qxorm_fr/resource/GB.png b/doc/qxorm_fr/resource/GB.png new file mode 100644 index 0000000000000000000000000000000000000000..3a189083842a36757587898f32f8f61eea08875f GIT binary patch literal 942 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<^!3HGb?#A2&QY`6?zK#qG*KS<#k1zuAB}-f* zN`mv#O3D+9QW+dm@{>{(JaZG%Q-e|yQz{EjrrIztFgtjW{1a`(GAw`M-C1{om`CnLU$|3=SxD`0AeEnGmU9?3mX4hT$e_>0VZW zY{rcm0-KwA7&sK>@UcivoBQ|w|JRql|Gytr^!tB=$NT@ko5lZ|oBsXJ&%-!Lmg5rh zC2k|;6tNRN6K*P4D`Xwu(_wiOutK@g(T#ycAdbChL(`%E|E@}U-mafe)otiwefBT|;B^-uOf70`Gc8rjFJOZco-JuiBX!7^J?K za-u}##s@1UUe59?SQ^>DkaTFdoUFHpfkDol69+ylNoPH*ZYJ0!y>BTGe}2vJOm#HN z85UCU=x=)dDsSg&4j+>__Yyeh3TfU%a^bp5xm;jHS%X z4h#>A9_X()q#j@=KUaoh^Jn2JygWet+Ps^bWmUPBTE2MV5V2-2+eL@%e_23=u!J_Q uaX7qZiRh&yj<9hEL01BQfB=E7E@iJfb@OxZun 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 literal 0 HcmV?d00001 diff --git a/doc/qxorm_fr/resource/gui_qxClientServer.jpg b/doc/qxorm_fr/resource/gui_qxClientServer.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5f10aa19c25899319bf723276eec306aaf54a184 GIT binary patch literal 192022 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%0Aiud11eRv>i>|wVq=aMj-i0Q( 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

    n8TqnHboQ(qO{L|7eH8oMx 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 z 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 literal 0 HcmV?d00001 diff --git a/doc/qxorm_fr/resource/gui_qxClient_01.jpg b/doc/qxorm_fr/resource/gui_qxClient_01.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c505283a8edddff5196710dda59c89c5995047ad GIT binary patch literal 67652 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 literal 0 HcmV?d00001 diff --git a/doc/qxorm_fr/resource/gui_qxClient_02.jpg b/doc/qxorm_fr/resource/gui_qxClient_02.jpg new file mode 100644 index 0000000000000000000000000000000000000000..90e964b88c1405a7dd6cb2c6103bbfdd9fbafb30 GIT binary patch literal 51014 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%0AE7;M2>ExO#HGp0vDB$S@4DOS8P+_bO3A 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 W5hs_C>Eer1ZCD?R6wasqH2Gi0u4K6Y literal 0 HcmV?d00001 diff --git a/doc/qxorm_fr/resource/gui_qxServer.jpg b/doc/qxorm_fr/resource/gui_qxServer.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e8a0549d0e055712158636966a3a0b512113cfc4 GIT binary patch literal 60288 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 M!GZH%i2a!PKXOTh*#H0l literal 0 HcmV?d00001 diff --git a/doc/qxorm_fr/resource/jquery.min.js b/doc/qxorm_fr/resource/jquery.min.js new file mode 100644 index 0000000..0f60b7b --- /dev/null +++ b/doc/qxorm_fr/resource/jquery.min.js @@ -0,0 +1,5 @@ +/*! jQuery v1.11.3 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.3",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b="length"in a&&a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1; + +return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="
    a",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h]","i"),ha=/^\s+/,ia=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ja=/<([\w:]+)/,ka=/\s*$/g,ra={option:[1,""],legend:[1,"

    ","
    "],area:[1,"",""],param:[1,"",""],thead:[1,"","
    "],tr:[2,"","
    "],col:[2,"","
    "],td:[3,"","
    "],_default:k.htmlSerialize?[0,"",""]:[1,"X
    ","
    "]},sa=da(y),ta=sa.appendChild(y.createElement("div"));ra.optgroup=ra.option,ra.tbody=ra.tfoot=ra.colgroup=ra.caption=ra.thead,ra.th=ra.td;function ua(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ua(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function va(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wa(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xa(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function ya(a){var b=pa.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function za(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Aa(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Ba(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xa(b).text=a.text,ya(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!ga.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(ta.innerHTML=a.outerHTML,ta.removeChild(f=ta.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ua(f),h=ua(a),g=0;null!=(e=h[g]);++g)d[g]&&Ba(e,d[g]);if(b)if(c)for(h=h||ua(a),d=d||ua(f),g=0;null!=(e=h[g]);g++)Aa(e,d[g]);else Aa(a,f);return d=ua(f,"script"),d.length>0&&za(d,!i&&ua(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=da(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(la.test(f)){h=h||o.appendChild(b.createElement("div")),i=(ja.exec(f)||["",""])[1].toLowerCase(),l=ra[i]||ra._default,h.innerHTML=l[1]+f.replace(ia,"<$1>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&ha.test(f)&&p.push(b.createTextNode(ha.exec(f)[0])),!k.tbody){f="table"!==i||ka.test(f)?""!==l[1]||ka.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ua(p,"input"),va),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ua(o.appendChild(f),"script"),g&&za(h),c)){e=0;while(f=h[e++])oa.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ua(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&za(ua(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ua(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fa,""):void 0;if(!("string"!=typeof a||ma.test(a)||!k.htmlSerialize&&ga.test(a)||!k.leadingWhitespace&&ha.test(a)||ra[(ja.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ia,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ua(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ua(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&na.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ua(i,"script"),xa),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ua(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,ya),j=0;f>j;j++)d=g[j],oa.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qa,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Ca,Da={};function Ea(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fa(a){var b=y,c=Da[a];return c||(c=Ea(a,b),"none"!==c&&c||(Ca=(Ca||m("
    +
    + Cette vid�o pr�sente les �tapes suivantes : +
      +
    • T�l�charger et installer la biblioth�que QxOrm (10s) ;
    • +
    • T�l�charger et installer l'application QxEntityEditor (56s) ;
    • +
    • Cr�er un projet QxEntityEditor (1m 46s) ;
    • +
    • Exporter les entit�s vers un projet C++/Qt (8m 26s) ;
    • +
    • Exporter les entit�s vers un script DDL SQL de base de donn�es (10m 22s) ;
    • +
    • Cr�er une application client/serveur pour transf�rer son mod�le de donn�es sur le r�seau (12m + 31s) ;
    • +
    • Exporter un projet QxEntityEditor vers un fichier XML ou JSON (17m 13s) ;
    • +
    • Ex�cuter QxEntityEditor en ligne de commande (sans IHM) (18m 07s).
    • +
    +
    +
    +
    + Voici une autre vid�o de l'application + QxEntityEditor pour montrer comment importer une structure de base de donn�es existante (projet MySQL + Workbench) :
    +
    +
    +
    + Cette vid�o pr�sente les �tapes suivantes : +
      +
    • Projet MySQL Workbench - exemple de base de donn�es Sakila (10s) ;
    • +
    • Cr�ation d'un DSN pour se connecter � MySQL par ODBC (46s) ;
    • +
    • Import de la structure de base de donn�es dans un projet QxEntityEditor (1m 15s) ;
    • +
    • Export du projet QxEntityEditor vers un projet C++ Qt (3m 10s) ;
    • +
    • Compilation des classes g�n�r�es du projet C++ Qt (4m 22s).
    • +
    +
    + + + +
    +
    +
    + + + + + + + + + + + +
    + QxOrm + + � 2011-202XDL Teamty - ic-east.com + +
    + + + + +
    +
    + + + \ No newline at end of file diff --git a/include/QxCollection/IxCollection.h b/include/QxCollection/IxCollection.h new file mode 100644 index 0000000..b3c895d --- /dev/null +++ b/include/QxCollection/IxCollection.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_COLLECTION_H_ +#define _IX_COLLECTION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxCollection.h + * \author XDL Team + * \ingroup QxCollection + * \brief Common interface for all QxOrm containers qx::QxCollection + */ + +#include + +#include + +namespace qx +{ + + /*! + * \ingroup QxCollection + * \brief qx::IxCollection : common interface for all QxOrm containers qx::QxCollection + */ + class QX_DLL_EXPORT IxCollection + { + + public: + IxCollection() { ; } + virtual ~IxCollection() = 0; + + virtual long _count() const = 0; + virtual void _clear() = 0; + virtual bool _remove(long index) = 0; + virtual qx::any _at(long index) const = 0; + + template + T _get(long index) const + { + return qx::any_cast_dynamic::get(_at(index)); + } + }; + + typedef std::shared_ptr IxCollection_ptr; + +} // namespace qx + +QX_REGISTER_CLASS_NAME(IxCollection) + +#endif // _IX_COLLECTION_H_ diff --git a/include/QxCollection/QxCollection.h b/include/QxCollection/QxCollection.h new file mode 100644 index 0000000..8a59087 --- /dev/null +++ b/include/QxCollection/QxCollection.h @@ -0,0 +1,285 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_COLLECTION_H_ +#define _QX_COLLECTION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxCollection.h + * \author XDL Team + * \ingroup QxCollection + * \brief QxOrm thread-safe container (keep insertion order + quick access by index + quick access by key) + */ + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) +#pragma warning(disable : 4503) +#endif // _MSC_VER + +#include + +#include +#include + +#include + +#include +#include + +namespace qx +{ + + /*! + * \ingroup QxCollection + * \brief qx::QxCollection : QxOrm thread-safe container (keep insertion order + quick access by index + quick access by key) + * + * Based on boost::multi_index_container, this collection has advantages of std::vector (keep insertion order + quick access by index) + * and boost::unordered_map or QHash (quick access by key : hash-map). + * + * Note : qx::QxCollection is compatible with the foreach macro provided by Qt library and the BOOST_FOREACH macro provided by boost library. + * However, each element returned by these 2 macros corresponds to an object of type std::pair. + * To obtain a more natural and more readable result, it is advised to use the _foreach macro : this macro uses BOOST_FOREACH for all the containers except for qx::QxCollection. + * In this case, the returned element corresponds to the Value type (cf. following sample). + * The macro _foreach is compatible with all containers (stl, Qt, boost...) since it uses the macro BOOST_FOREACH. + * + * Additional note : qx::QxCollection is particularly suited to receive data resulting from a database. + * Indeed, these data can be sorted (by using ORDER BY in a SQL request for example), it is thus important to preserve the insertion order of the elements in the list. + * Furthermore, each data resulting from a database has a unique id. It is thus important to be able to access quickly to an element based on this single identifier (hash-map). + * + * Quick sample using qx::QxCollection container : + * \code + // definition of drug class with 3 properties : 'code', 'name' and 'description' + class drug { public: QString code; QString name; QString desc; }; + + // typedef a smart-pointer of drug class + typedef std::shared_ptr drug_ptr; + + // collection of drugs indexed by 'code' property (QString type) + qx::QxCollection lstDrugs; + + // create 3 new drugs + drug_ptr d1; d1.reset(new drug()); d1->code = "code1"; d1->name = "name1"; d1->desc = "desc1"; + drug_ptr d2; d2.reset(new drug()); d2->code = "code2"; d2->name = "name2"; d2->desc = "desc2"; + drug_ptr d3; d3.reset(new drug()); d3->code = "code3"; d3->name = "name3"; d3->desc = "desc3"; + + // insert 3 drugs into the collection + lstDrugs.insert(d1->code, d1); + lstDrugs.insert(d2->code, d2); + lstDrugs.insert(d3->code, d3); + + // iterate over drugs container using QxOrm '_foreach' keyword + _foreach(drug_ptr p, lstDrugs) + { qDebug() << qPrintable(p->name) << " " << qPrintable(p->desc); } + + // iterate over drugs container using classic C++ 'for' keyword + for (long l = 0; l < lstDrugs.count(); ++l) + { + drug_ptr p = lstDrugs.getByIndex(l); + QString code = lstDrugs.getKeyByIndex(l); + qDebug() << qPrintable(p->name) << " " << qPrintable(p->desc); + } + + // iterate over drugs container using 'qx::QxCollectionIterator' Java-style iterator + qx::QxCollectionIterator itr(lstDrugs); + while (itr.next()) + { + QString code = itr.key(); + qDebug() << qPrintable(itr.value()->name) << " " << qPrintable(itr.value()->desc); + } + + // sort drugs container ascending by key and sort descending by value + lstDrugs.sortByKey(true); + lstDrugs.sortByValue(false); + + // access to a drug into the collection by its 'code' property + drug_ptr p = lstDrugs.getByKey("code2"); + + // access to a drug into the collection by index (position in the list) + drug_ptr p = lstDrugs.getByIndex(2); + + // test if a drug exists into the collection and if the collection is empty + bool bExist = lstDrugs.exist("code3"); + bool bEmpty = lstDrugs.empty(); + + // remove the second drug from the collection + lstDrugs.removeByIndex(2); + + // remove a drug from the collection using its 'code' property + lstDrugs.removeByKey("code3"); + + // clear the collection : remove all items from the list + lstDrugs.clear(); + * \endcode + */ + template + class QxCollection : public IxCollection + { + + public: + typedef QPair type_pair_key_value; + + protected: + typedef QList type_list_pair_key_value; + typedef QHash type_hash_position; + + public: + typedef typename type_list_pair_key_value::iterator iterator; + typedef typename type_list_pair_key_value::const_iterator const_iterator; + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + typedef typename type_list_pair_key_value::reverse_iterator reverse_iterator; + typedef typename type_list_pair_key_value::const_reverse_iterator const_reverse_iterator; +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + + typedef const Key &const_reference_key; + typedef const Value &const_reference_value; + + protected: + mutable QMutex m_mutex; //!< Mutex => qx::QxCollection is thread-safe + type_list_pair_key_value m_list; //!< Container to keep insertion order + type_hash_position m_hash; //!< Container for fast search by key + bool m_batch; //!< Batch mode to sync internal containers + + public: + QxCollection(); //!< Construct an empty list + QxCollection(const QxCollection &other); //!< Construct a copy of 'other' + virtual ~QxCollection(); //!< Destroy the list + + QxCollection &operator=(const QxCollection &other); //!< Assign 'other' to this list and return a reference to this list + bool operator==(const QxCollection &other) const; //!< Return 'true' if 'other' is equal to this list, otherwise return 'false' (same values in the same order) + bool operator!=(const QxCollection &other) const; //!< Return 'true' if 'other' is not equal to this list, otherwise return 'false' + + iterator begin(); //!< Return an STL-style iterator pointing to the first item in the list + iterator end(); //!< Return an STL-style iterator pointing to the imaginary item after the last item in the list + const_iterator begin() const; //!< Return a const STL-style iterator pointing to the first item in the list + const_iterator end() const; //!< Return a const STL-style iterator pointing to the imaginary item after the last item in the list + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + reverse_iterator rbegin(); //!< Return a reverse STL-style iterator pointing to the first item in the list + reverse_iterator rend(); //!< Return a reverse STL-style iterator pointing to the imaginary item after the last item in the list + const_reverse_iterator rbegin() const; //!< Return a const reverse STL-style iterator pointing to the first item in the list + const_reverse_iterator rend() const; //!< Return a const reverse STL-style iterator pointing to the imaginary item after the last item in the list +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + + void reserve(long size); //!< Request that the capacity of the allocated storage space for the items of the container be at least enough to hold 'size' elements + void reverse(); //!< Reverse all items in the list + void clear(); //!< Remove all items from the list + long count() const; //!< Return the number of items in the list (same as 'size()') + long size() const; //!< Return the number of items in the list (same as 'count()') + bool contains(const Key &key) const; //!< Return 'true' if the list contains an occurrence of 'key', otherwise return 'false' (same as 'exist()') + bool exist(const Key &key) const; //!< Return 'true' if the list contains an occurrence of 'key', otherwise return 'false' (same as 'contains()') + bool empty() const; //!< Return 'true' if the list contains no items; otherwise return 'false' + + bool push_back(const Key &key, const Value &value); //!< Add element 'value' at the end of the list indexed by 'key' + bool push_front(const Key &key, const Value &value); //!< Insert 'value' at the beginning of the list indexed by 'key' + bool insert(const Key &key, const Value &value); //!< Add element 'value' at the end of the list indexed by 'key' + bool insert(long index, const Key &key, const Value &value); //!< Insert element 'value' at position 'index' in the list indexed by 'key' + bool insert(const QxCollection &other); //!< Add all items of 'other' at the end of the list + bool insert(long index, const QxCollection &other); //!< Insert all items of 'other' at the end of the list + bool replace(long index, const Key &key, const Value &value); //!< Replace the item at index position 'index' with element 'value' indexed by 'key' + bool swap(long index1, long index2); //!< Exchange the item at index position 'index1' with the item at index position 'index2' + bool move(long indexFrom, long indexTo); //!< Move the item at index position 'indexFrom' to index position 'indexTo' + + bool removeByKey(const Key &key); //!< Remove the item indexed by 'key' in the list + bool removeByIndex(long index); //!< Remove the item at index position 'index' + bool removeByIndex(long first, long last); //!< Remove all items from index position 'first' to index position 'last' + bool removeFirst(); //!< Remove the first item in the list + bool removeLast(); //!< Remove the last item in the list + + const_reference_value getByKey(const Key &key) const; //!< Return the item associated with the 'key' + const_reference_value getByIndex(long index) const; //!< Return the item at index position 'index' + const_reference_value getFirst() const; //!< Return the first element in the list + const_reference_value getLast() const; //!< Return the last element in the list + const_reference_key getKeyByIndex(long index) const; //!< Return the key associated with the element at index position 'index' + + void sortByKey(bool bAscending = true); //!< Sort all items in the list using associated keys to compare + void sortByValue(bool bAscending = true); //!< Sort all items in the list + + template + void sort(Compare comp) + { + { + QMutexLocker locker(&m_mutex); + std::sort(m_list.begin(), m_list.end(), comp); + } + updateHashPosition(); + } + + protected: + void cloneCollection(QxCollection *pClone, const QxCollection &pRef); + bool isSameCollection(const QxCollection *p1, const QxCollection &p2) const; + void updateHashPosition(long from = 0, long to = -1, bool check = false); + + template + struct compareKeyValue + { + static bool compareByKeyAscending(const type_pair_key_value &v1, const type_pair_key_value &v2) { return (v1.first < v2.first); } + static bool compareByKeyDescending(const type_pair_key_value &v1, const type_pair_key_value &v2) { return (v1.first > v2.first); } + static bool compareByValueAscending(const type_pair_key_value &v1, const type_pair_key_value &v2) { return (v1.second < v2.second); } + static bool compareByValueDescending(const type_pair_key_value &v1, const type_pair_key_value &v2) { return (v1.second > v2.second); } + }; + + template + struct compareKeyValue + { + static bool compareByKeyAscending(const type_pair_key_value &v1, const type_pair_key_value &v2) { return ((v1.first && v2.first) ? ((*v1.first) < (*v2.first)) : false); } + static bool compareByKeyDescending(const type_pair_key_value &v1, const type_pair_key_value &v2) { return ((v1.first && v2.first) ? ((*v1.first) > (*v2.first)) : true); } + static bool compareByValueAscending(const type_pair_key_value &v1, const type_pair_key_value &v2) { return ((v1.second && v2.second) ? ((*v1.second) < (*v2.second)) : false); } + static bool compareByValueDescending(const type_pair_key_value &v1, const type_pair_key_value &v2) { return ((v1.second && v2.second) ? ((*v1.second) > (*v2.second)) : true); } + }; + + public: + virtual long _count() const { return this->count(); } + virtual void _clear() { this->clear(); } + virtual bool _remove(long index) { return this->removeByIndex(index); } + virtual qx::any _at(long index) const + { + Value val = this->getByIndex(index); + return qx::any(val); + } + }; + +} // namespace qx + +#include "../../inl/QxCollection/QxCollection.inl" + +QX_REGISTER_CLASS_NAME_TEMPLATE_2(qx::QxCollection) + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +#endif // _QX_COLLECTION_H_ diff --git a/include/QxCollection/QxCollectionIterator.h b/include/QxCollection/QxCollectionIterator.h new file mode 100644 index 0000000..56dd4de --- /dev/null +++ b/include/QxCollection/QxCollectionIterator.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_COLLECTION_ITERATOR_H_ +#define _QX_COLLECTION_ITERATOR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxCollectionIterator.h + * \author XDL Team + * \ingroup QxCollection + * \brief Java-style iterator to iterate over a qx::QxCollection container + */ + +#include + +namespace qx +{ + + /*! + * \ingroup QxCollection + * \brief qx::QxCollectionIterator : Java-style iterator to iterate over a qx::QxCollection container + * + * Quick sample using qx::QxCollectionIterator Java-style iterator : + * \code + // iterate over a drugs container using 'qx::QxCollectionIterator' Java-style iterator + qx::QxCollectionIterator itr(lstDrugs); + while (itr.next()) + { + QString code = itr.key(); + qDebug() << qPrintable(itr.value()->name) << " " << qPrintable(itr.value()->desc); + } + * \endcode + */ + template + class QxCollectionIterator + { + + private: + const QxCollection *m_pCollection; //!< Collection to iterate over + long m_lCurrIndex; //!< Current index (position) in the collection + + public: + QxCollectionIterator(const QxCollection &col); //!< Construct an iterator for traversing the collection. The iterator is set to be at the front of the list (before the first item) + ~QxCollectionIterator(); //!< Destroy the iterator + + inline const Key &key() const; //!< Return the 'key' at current position + inline const Value &value() const; //!< Return the 'value' at current position + + inline void toFirst(); //!< Move the iterator to the front of the container (before the first item) + inline void toLast(); //!< Move the iterator to the back of the container (after the last item) + + inline bool next(); //!< Advance the iterator by one position. Return 'true' if there is at least one item ahead of the iterator, i.e. the iterator is not at the back of the container; otherwise return 'false' + inline bool previous(); //!< Move the iterator back by one position. Return 'true' if there is at least one item behind the iterator, i.e. the iterator is not at the front of the container; otherwise return 'false' + + private: + QxCollectionIterator(const QxCollectionIterator &other) { Q_UNUSED(other); } + QxCollectionIterator &operator=(const QxCollectionIterator &other) + { + Q_UNUSED(other); + return (*this); + } + }; + +} // namespace qx + +#include "../../inl/QxCollection/QxCollectionIterator.inl" + +#endif // _QX_COLLECTION_ITERATOR_H_ diff --git a/include/QxCollection/QxForeach.h b/include/QxCollection/QxForeach.h new file mode 100644 index 0000000..2fb0e0a --- /dev/null +++ b/include/QxCollection/QxForeach.h @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST +#ifndef _QX_FOREACH_H_ +#define _QX_FOREACH_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxForeach.h + * \author XDL Team + * \ingroup QxCollection + * \brief foreach-style (based on BOOST_FOREACH macro) to iterate over all stl, boost and Qt containers + qx::QxCollection QxOrm library container + * + * Quick sample using QX_FOREACH (or _foreach) macro : + * \code +// iterate over drugs container using QxOrm '_foreach' keyword +_foreach(drug_ptr p, lstDrugs) +{ qDebug() << qPrintable(p->name) << " " << qPrintable(p->desc); } + * \endcode + * + * \note QxOrm library provides also other macros to iterator over all containers : _foreach_reverse, _foreach_if, _foreach_reverse_if + */ + +#include + +#ifndef BOOST_FOREACH_ID +#define BOOST_FOREACH_ID(x) x +#endif + +#include + +namespace qx +{ + + namespace foreach + { + + template + struct qx_deref_boost_or_qx + { + typedef typename boost::foreach_detail_::foreach_reference::type type; + }; + + template + struct qx_deref_boost_or_qx + { + typedef typename T::type_pair_key_value::second_type type; + }; + + template + struct qx_deref_deduce + { + static inline typename qx::foreach::qx_deref_boost_or_qx::type + deref(boost::foreach_detail_::auto_any_t cur, boost::foreach_detail_::type2type *ptmp) + { + return boost::foreach_detail_::deref(cur, ptmp); + } + + static inline typename qx::foreach::qx_deref_boost_or_qx::type + rderef(boost::foreach_detail_::auto_any_t cur, boost::foreach_detail_::type2type *ptmp) + { + return boost::foreach_detail_::rderef(cur, ptmp); + } + }; + + template + struct qx_deref_deduce + { + static inline typename qx::foreach::qx_deref_boost_or_qx::type + deref(boost::foreach_detail_::auto_any_t cur, boost::foreach_detail_::type2type *ptmp) + { + return boost::foreach_detail_::deref(cur, ptmp).second; + } + + static inline typename qx::foreach::qx_deref_boost_or_qx::type + rderef(boost::foreach_detail_::auto_any_t cur, boost::foreach_detail_::type2type *ptmp) + { + return boost::foreach_detail_::rderef(cur, ptmp).second; + } + }; + + struct qx_deref + { + template + static inline typename qx::foreach::qx_deref_boost_or_qx::value>::type + deref(boost::foreach_detail_::auto_any_t cur, boost::foreach_detail_::type2type *ptmp) + { + return qx::foreach::qx_deref_deduce::value>::deref(cur, ptmp); + } + + template + static inline typename qx::foreach::qx_deref_boost_or_qx::value>::type + deref_reverse(boost::foreach_detail_::auto_any_t cur, boost::foreach_detail_::type2type *ptmp) + { + return qx::foreach::qx_deref_deduce::value>::rderef(cur, ptmp); + } + }; + + } // namespace foreach + +} // namespace qx + +#define QX_FOREACH_DEREF(COL) \ + qx::foreach::qx_deref::deref(BOOST_FOREACH_ID(_foreach_cur), BOOST_FOREACH_TYPEOF(COL)) + +#define QX_FOREACH_DEREF_REVERSE(COL) \ + qx::foreach::qx_deref::deref_reverse(BOOST_FOREACH_ID(_foreach_cur), BOOST_FOREACH_TYPEOF(COL)) + +#define QX_FOREACH(VAR, COL) \ + BOOST_FOREACH_PREAMBLE() \ + if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_col) = BOOST_FOREACH_CONTAIN(COL)) \ + { \ + } \ + else if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_cur) = BOOST_FOREACH_BEGIN(COL)) \ + { \ + } \ + else if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_end) = BOOST_FOREACH_END(COL)) \ + { \ + } \ + else \ + for (bool BOOST_FOREACH_ID(_foreach_continue) = true; \ + BOOST_FOREACH_ID(_foreach_continue) && !BOOST_FOREACH_DONE(COL); \ + BOOST_FOREACH_ID(_foreach_continue) ? BOOST_FOREACH_NEXT(COL) : (void)0) \ + if (boost::foreach_detail_::set_false(BOOST_FOREACH_ID(_foreach_continue))) \ + { \ + } \ + else \ + for (VAR = QX_FOREACH_DEREF(COL); !BOOST_FOREACH_ID(_foreach_continue); BOOST_FOREACH_ID(_foreach_continue) = true) + +#define QX_FOREACH_REVERSE(VAR, COL) \ + BOOST_FOREACH_PREAMBLE() \ + if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_col) = BOOST_FOREACH_CONTAIN(COL)) \ + { \ + } \ + else if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_cur) = BOOST_FOREACH_RBEGIN(COL)) \ + { \ + } \ + else if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_end) = BOOST_FOREACH_REND(COL)) \ + { \ + } \ + else \ + for (bool BOOST_FOREACH_ID(_foreach_continue) = true; \ + BOOST_FOREACH_ID(_foreach_continue) && !BOOST_FOREACH_RDONE(COL); \ + BOOST_FOREACH_ID(_foreach_continue) ? BOOST_FOREACH_RNEXT(COL) : (void)0) \ + if (boost::foreach_detail_::set_false(BOOST_FOREACH_ID(_foreach_continue))) \ + { \ + } \ + else \ + for (VAR = QX_FOREACH_DEREF_REVERSE(COL); !BOOST_FOREACH_ID(_foreach_continue); BOOST_FOREACH_ID(_foreach_continue) = true) + +#ifdef _foreach +#undef _foreach +#endif // _foreach + +#ifdef _foreach_reverse +#undef _foreach_reverse +#endif // _foreach_reverse + +#ifdef _foreach_if +#undef _foreach_if +#endif // _foreach_if + +#ifdef _foreach_reverse_if +#undef _foreach_reverse_if +#endif // _foreach_reverse_if + +#define _foreach QX_FOREACH +#define _foreach_reverse QX_FOREACH_REVERSE + +#define _foreach_if(VAR, COL, COND) _foreach(VAR, COL) if (COND) +#define _foreach_reverse_if(VAR, COL, COND) _foreach_reverse(VAR, COL) if (COND) + +#endif // _QX_FOREACH_H_ +#endif // _QX_ENABLE_BOOST diff --git a/include/QxCommon/QxAny.h b/include/QxCommon/QxAny.h new file mode 100644 index 0000000..048b9f0 --- /dev/null +++ b/include/QxCommon/QxAny.h @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_ANY_H_ +#define _QX_ANY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxAny.h + * \author XDL Team + * \ingroup QxCommon + * \brief qx::any : basic implementation of boost::any (written by Kevlin Henney) when boost dependency is not available + */ + +#ifndef _QX_NO_RTTI +#include +#define QX_TYPE_ID(T) typeid(T) +#else // _QX_NO_RTTI +#include +#include +#define QX_TYPE_ID(T) std::string(qx::trait::get_class_name::get()) +#endif // _QX_NO_RTTI + +#ifndef Q_OS_WIN +#if (__GNUC__ >= 4) +#define QX_ANY_FORCE_HIDDEN_VISIBILITY __attribute__((visibility("hidden"))) // To avoid a GCC warning : 'qx::any::holder' declared with greater visibility than the type of its field 'qx::any::holder::held' [-Wattributes] +#endif // (__GNUC__ >= 4) +#endif // Q_OS_WIN + +#ifndef QX_ANY_FORCE_HIDDEN_VISIBILITY +#define QX_ANY_FORCE_HIDDEN_VISIBILITY /* Nothing */ +#endif // QX_ANY_FORCE_HIDDEN_VISIBILITY + +namespace qx +{ + + class any; + template + ValueType *any_cast(any *); + template + ValueType *unsafe_any_cast(any *); + + class any + { + + template + friend ValueType *qx::any_cast(any *); + template + friend ValueType *qx::unsafe_any_cast(any *); + + public: +#ifndef _QX_NO_RTTI + typedef const std::type_info &type_check; +#else // _QX_NO_RTTI + typedef std::string type_check; +#endif // _QX_NO_RTTI + + any() : content(NULL) { ; } + any(const any &other) : content(other.content ? other.content->clone() : NULL) { ; } + ~any() + { + if (content) + { + delete content; + } + } + + template + any(const ValueType &value) : content(new holder::type>::type>(value)) { ; } + + any &swap(any &other) + { + std::swap(content, other.content); + return (*this); + } + + template + any &operator=(const ValueType &other) + { + any(other).swap(*this); + return (*this); + } + + any &operator=(any other) + { + any(other).swap(*this); + return (*this); + } + bool empty() const { return (!content); } + void clear() { any().swap(*this); } + type_check type() const { return (content ? content->type() : QX_TYPE_ID(void)); } + + private: + struct placeholder + { + virtual ~placeholder() { ; } + virtual type_check type() const = 0; + virtual placeholder *clone() const = 0; + }; + + template + struct QX_ANY_FORCE_HIDDEN_VISIBILITY holder : public placeholder + { + holder(const ValueType &value) : held(value) { ; } + virtual type_check type() const { return QX_TYPE_ID(ValueType); } + virtual placeholder *clone() const { return new holder(held); } + ValueType held; + + private: + holder &operator=(const holder &); + }; + + placeholder *content; + }; + + inline void swap(any &lhs, any &other) { lhs.swap(other); } + + struct bad_any_cast : public std::exception + { + virtual const char *what() const throw() { return "qx::bad_any_cast : failed conversion using qx::any_cast"; } + }; + + template + ValueType *any_cast(any *operand) + { + return ((operand && (operand->type() == QX_TYPE_ID(ValueType))) ? (&static_cast::type> *>(operand->content)->held) : NULL); + } + + template + const ValueType *any_cast(const any *operand) + { + return any_cast(const_cast(operand)); + } + + template + ValueType any_cast(any &operand) + { + typedef typename std::remove_reference::type nonref; + nonref *result = any_cast(&operand); + if (!result) + { + throw qx::bad_any_cast(); + } + return static_cast(*result); + } + + template + ValueType any_cast(const any &operand) + { + typedef typename std::remove_reference::type nonref; + return any_cast(const_cast(operand)); + } + + template + ValueType *unsafe_any_cast(any *operand) + { + return (&static_cast *>(operand->content)->held); + } + + template + const ValueType *unsafe_any_cast(const any *operand) + { + return unsafe_any_cast(const_cast(operand)); + } + +} // namespace qx + +#endif // _QX_ANY_H_ diff --git a/include/QxCommon/QxAnyCastDynamic.h b/include/QxCommon/QxAnyCastDynamic.h new file mode 100644 index 0000000..5015d03 --- /dev/null +++ b/include/QxCommon/QxAnyCastDynamic.h @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_ANY_CAST_DYNAMIC_H_ +#define _QX_ANY_CAST_DYNAMIC_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxAnyCastDynamic.h + * \author XDL Team + * \ingroup QxCommon + * \brief qx::any_cast_dynamic::get() : provides a tool to use qx::any_cast and polymorphism + */ + +#include + +#include + +#include + +namespace qx +{ + + template + struct any_cast_dynamic + { + static T get(const qx::any &a) { return qx::any_cast(a); } + }; + + template + struct any_cast_dynamic + { + static T *get(const qx::any &a) + { + if (a.empty()) + { + return NULL; + } + qx::any *b = const_cast(&a); + T **t = qx::unsafe_any_cast(b); + if (!t) + { + return NULL; + } + return (*t); + } + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct any_cast_dynamic> + { + static boost::shared_ptr get(const qx::any &a) + { + if (a.empty()) + { + return boost::shared_ptr(); + } + qx::any *b = const_cast(&a); + boost::shared_ptr *t = qx::unsafe_any_cast>(b); + if (!t) + { + return boost::shared_ptr(); + } + return (*t); + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct any_cast_dynamic> + { + static QSharedPointer get(const qx::any &a) + { + if (a.empty()) + { + return QSharedPointer(); + } + qx::any *b = const_cast(&a); + QSharedPointer *t = qx::unsafe_any_cast>(b); + if (!t) + { + return QSharedPointer(); + } + return (*t); + } + }; + + template + struct any_cast_dynamic> + { + static qx::dao::ptr get(const qx::any &a) + { + if (a.empty()) + { + return qx::dao::ptr(); + } + qx::any *b = const_cast(&a); + qx::dao::ptr *t = qx::unsafe_any_cast>(b); + if (!t) + { + return qx::dao::ptr(); + } + return (*t); + } + }; + + template + struct any_cast_dynamic> + { + static std::shared_ptr get(const qx::any &a) + { + if (a.empty()) + { + return std::shared_ptr(); + } + qx::any *b = const_cast(&a); + std::shared_ptr *t = qx::unsafe_any_cast>(b); + if (!t) + { + return std::shared_ptr(); + } + return (*t); + } + }; + +} // namespace qx + +#endif // _QX_ANY_CAST_DYNAMIC_H_ diff --git a/include/QxCommon/QxBool.h b/include/QxCommon/QxBool.h new file mode 100644 index 0000000..a9be4e6 --- /dev/null +++ b/include/QxCommon/QxBool.h @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_BOOL_H_ +#define _QX_BOOL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxBool.h + * \author XDL Team + * \ingroup QxCommon + * \brief qx_bool : QxOrm library boolean type with code and description message when an error occured + */ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#include +#include +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#include + +#include + +namespace qx +{ + class QxBool; +} // namespace qx + +QX_DLL_EXPORT QDataStream &operator<<(QDataStream &stream, const qx::QxBool &t) QX_USED; +QX_DLL_EXPORT QDataStream &operator>>(QDataStream &stream, qx::QxBool &t) QX_USED; + +namespace qx +{ + + /*! + * \ingroup QxCommon + * \brief qx_bool : boolean type with code and description message when an error occured + */ + class QxBool + { + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + friend class boost::serialization::access; +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + friend QX_DLL_EXPORT QDataStream & ::operator<<(QDataStream &stream, const qx::QxBool &t); + friend QX_DLL_EXPORT QDataStream & ::operator>>(QDataStream &stream, qx::QxBool &t); + + private: + bool m_bValue; //!< Data boolean value + long m_lCode; //!< Error code when value is false + QString m_sDesc; //!< Error description when value is false + + public: + QxBool() : m_bValue(false), m_lCode(0) { ; } + QxBool(bool b) : m_bValue(b), m_lCode(0) { qAssert(checkInitialized(b)); } + QxBool(long lCode, const QString &sDesc) : m_bValue(false), m_lCode(lCode), m_sDesc(sDesc) { ; } + QxBool(bool bValue, long lCode, const QString &sDesc) : m_bValue(bValue), m_lCode(lCode), m_sDesc(sDesc) { qAssert(checkInitialized(bValue)); } + QxBool(const QxBool &other) : m_bValue(other.getValue()), m_lCode(other.getCode()), m_sDesc(other.getDesc()) { ; } + ~QxBool() { ; } + + inline bool getValue() const { return m_bValue; } + inline long getCode() const { return m_lCode; } + inline QString getDesc() const { return m_sDesc; } + + inline void setValue(bool bValue) + { + m_bValue = bValue; + qAssert(checkInitialized(bValue)); + } + inline void setCode(long lCode) { m_lCode = lCode; } + inline void setDesc(const QString &sDesc) { m_sDesc = sDesc; } + + inline QxBool &operator=(const QxBool &other) + { + m_bValue = other.getValue(); + m_lCode = other.getCode(); + m_sDesc = other.getDesc(); + return (*this); + } + inline QxBool &operator=(const bool b) + { + m_bValue = b; + qAssert(checkInitialized(b)); + return (*this); + } + + inline operator bool() const { return (m_bValue != false); } + inline bool operator!() const { return (m_bValue == false); } + + inline bool operator==(const QxBool &other) const { return ((m_bValue == other.getValue()) && (m_lCode == other.getCode()) && (m_sDesc == other.getDesc())); } + inline bool operator==(const bool b) const + { + qAssert(checkInitialized(b)); + return (m_bValue == b); + } + inline bool operator!=(const QxBool &other) const { return ((m_bValue != other.getValue()) || (m_lCode != other.getCode()) || (m_sDesc != other.getDesc())); } + inline bool operator!=(const bool b) const + { + qAssert(checkInitialized(b)); + return (m_bValue != b); + } + inline bool operator&&(const QxBool &other) const { return (m_bValue && other.getValue()); } + inline bool operator&&(const bool b) const + { + qAssert(checkInitialized(b)); + return (m_bValue && b); + } + inline bool operator||(const QxBool &other) const { return (m_bValue || other.getValue()); } + inline bool operator||(const bool b) const + { + qAssert(checkInitialized(b)); + return (m_bValue || b); + } + + QString toString() const { return (QString(m_bValue ? "1" : "0") + "|" + QString::number(static_cast(m_lCode)) + "|" + m_sDesc); } + + void fromString(const QString &s) + { + if (s.trimmed().isEmpty()) + { + (*this) = QxBool(); + return; + } + bool bValue = s.startsWith("1"); + int iPos = s.indexOf("|", 2); + if (iPos == -1) + { + (*this) = QxBool(bValue); + return; + } + long lCode = s.mid(2, (iPos - 2)).toLong(); + QString sDesc = s.right(s.size() - (iPos + 1)); + (*this) = QxBool(bValue, lCode, sDesc); + } + + private: +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + template + void serialize(Archive &ar, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("value", m_bValue); + ar &boost::serialization::make_nvp("code", m_lCode); + ar &boost::serialization::make_nvp("desc", m_sDesc); + } +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + inline bool checkInitialized(const bool b) const { return ((static_cast(b) == 0) || (static_cast(b) == 1)); } + }; + +} // namespace qx + +typedef qx::QxBool qx_bool; +QX_REGISTER_CLASS_NAME(qx_bool) + +#endif // _QX_BOOL_H_ diff --git a/include/QxCommon/QxCache.h b/include/QxCommon/QxCache.h new file mode 100644 index 0000000..778ed36 --- /dev/null +++ b/include/QxCommon/QxCache.h @@ -0,0 +1,264 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_CACHE_H_ +#define _QX_CACHE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxCache.h + * \author XDL Team + * \ingroup QxCache + * \brief qx::cache : based on singleton pattern, provide basic thread-safe cache feature to backup and restore any kind of objects (for example, object fetched from database) + */ + +#include +#include + +#include + +#include + +namespace qx +{ + namespace cache + { + namespace detail + { + + class QX_DLL_EXPORT QxCache : public qx::QxSingleton + { + + friend class qx::QxSingleton; + + protected: + typedef std::tuple type_qx_cache; + typedef qx::QxCollection type_qx_lst_cache; + + type_qx_lst_cache m_cache; //!< List of objects in cache under qx::any format + QMutex m_oMutexCache; //!< Mutex => 'QxCache' is thread-safe + long m_lMaxCost; //!< Max cost before deleting object in cache + long m_lCurrCost; //!< Current cost in cache + + public: + QxCache(); + virtual ~QxCache(); + + long getCurrCost() const; + long getMaxCost() const; + void setMaxCost(long l); + + long count() const; + long size() const; + bool isEmpty() const; + bool exist(const QString &sKey) const; + bool contains(const QString &sKey) const; + qx::any at(const QString &sKey); + long insertionCost(const QString &sKey); + QDateTime insertionDateTime(const QString &sKey); + void clear(); + + bool insert(const QString &sKey, const qx::any &anyObj, long lCost = 1, const QDateTime &dt = QDateTime()); + bool remove(const QString &sKey); + + private: + void updateCost(); + }; + + } // namespace detail + } // namespace cache +} // namespace qx + +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::cache::detail::QxCache) + +namespace qx +{ + namespace cache + { + + /*! + * \ingroup QxCache + * \brief Set the maximum allowed total cost of the cache to l. If the current total cost is greater than l, some objects are deleted immediately + */ + inline void max_cost(long l) + { + qx::cache::detail::QxCache::getSingleton()->setMaxCost(l); + } + + /*! + * \ingroup QxCache + * \brief Return the maximum allowed total cost of the cache + */ + inline long max_cost() + { + return qx::cache::detail::QxCache::getSingleton()->getMaxCost(); + } + + /*! + * \ingroup QxCache + * \brief Return the current cost used by the cache + */ + inline long current_cost() + { + return qx::cache::detail::QxCache::getSingleton()->getCurrCost(); + } + + /*! + * \ingroup QxCache + * \brief Return the number of objects in the cache + */ + inline long count() + { + return qx::cache::detail::QxCache::getSingleton()->count(); + } + + /*! + * \ingroup QxCache + * \brief Return true if the cache contains no object; otherwise return false + */ + inline bool is_empty() + { + return qx::cache::detail::QxCache::getSingleton()->isEmpty(); + } + + /*! + * \ingroup QxCache + * \brief Delete all the objects in the cache + */ + inline void clear() + { + qx::cache::detail::QxCache::getSingleton()->clear(); + } + + /*! + * \ingroup QxCache + * \brief Return true if the cache contains an object associated with key sKey; otherwise return false + */ + inline bool exist(const QString &sKey) + { + return qx::cache::detail::QxCache::getSingleton()->exist(sKey); + } + + /*! + * \ingroup QxCache + * \brief Delete the object associated with key sKey. Return true if the object was found in the cache; otherwise return false + */ + inline bool remove(const QString &sKey) + { + return qx::cache::detail::QxCache::getSingleton()->remove(sKey); + } + + /*! + * \ingroup QxCache + * \brief Insert object t into the cache with key sKey, associated cost lCost and insertion date-time dt. Any object with the same key already in the cache will be removed + */ + template + inline bool set(const QString &sKey, T &t, long lCost = 1, const QDateTime &dt = QDateTime()) + { + qx::any obj(t); + return qx::cache::detail::QxCache::getSingleton()->insert(sKey, obj, lCost, dt); + } + + /*! + * \ingroup QxCache + * \brief Return the object of type T associated with key sKey, or return default instance of T() if the key does not exist in the cache + */ + template + inline T get(const QString &sKey) + { + qx::any obj = qx::cache::detail::QxCache::getSingleton()->at(sKey); + if (obj.empty()) + { + return T(); + } + try + { + return qx::any_cast(obj); + } + catch (const qx::bad_any_cast &err) + { + Q_UNUSED(err); + return T(); + } + catch (...) + { + return T(); + } + } + + /*! + * \ingroup QxCache + * \brief Return true if object t can be fetched with associated key sKey and insertion date-time dt; otherwise return false with an error description + */ + template + inline qx_bool get(const QString &sKey, T &t, QDateTime &dt) + { + dt = QDateTime(); + if (!qx::cache::exist(sKey)) + { + return qx_bool(false, 0, "[QxOrm] qx::cache : key doesn't exist in cache"); + } + qx::any obj = qx::cache::detail::QxCache::getSingleton()->at(sKey); + dt = qx::cache::detail::QxCache::getSingleton()->insertionDateTime(sKey); + try + { + t = qx::any_cast(obj); + return qx_bool(true); + } + catch (const qx::bad_any_cast &err) + { + Q_UNUSED(err); + return qx_bool(false, 0, "[QxOrm] qx::cache : bad any cast exception"); + } + catch (...) + { + return qx_bool(false, 0, "[QxOrm] qx::cache : unknown cast exception"); + } + } + + /*! + * \ingroup QxCache + * \brief Return true if object t can be fetched with associated key sKey; otherwise return false with an error description + */ + template + inline qx_bool get(const QString &sKey, T &t) + { + QDateTime dt; + return qx::cache::get(sKey, t, dt); + } + + } // namespace cache +} // namespace qx + +#endif // _QX_CACHE_H_ diff --git a/include/QxCommon/QxConfig.h b/include/QxCommon/QxConfig.h new file mode 100644 index 0000000..8484a1b --- /dev/null +++ b/include/QxCommon/QxConfig.h @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_CONFIG_H_ +#define _QX_CONFIG_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxConfig.h + * \author XDL Team + * \ingroup QxCommon + * \brief List of parameters to compile and build QxOrm library + */ + +#define QX_VERSION 0x010500 +#define QX_VERSION_STR "1.5.0" + +#ifndef _QX_MODE_DEBUG +#ifndef _QX_MODE_RELEASE +#ifdef QT_NO_DEBUG +#define _QX_MODE_RELEASE +#else // QT_NO_DEBUG +#define _QX_MODE_DEBUG +#endif // QT_NO_DEBUG +#endif // _QX_MODE_RELEASE +#endif // _QX_MODE_DEBUG + +#ifndef _QX_ENABLE_BOOST_SERIALIZATION_BINARY +#ifndef _QX_ENABLE_BOOST_SERIALIZATION_XML +#ifndef _QX_ENABLE_BOOST_SERIALIZATION_POLYMORPHIC +#ifndef _QX_ENABLE_BOOST_SERIALIZATION_TEXT +#ifndef _QX_ENABLE_BOOST_SERIALIZATION_PORTABLE_BINARY +#ifndef _QX_ENABLE_BOOST_SERIALIZATION_WIDE_BINARY +#ifndef _QX_ENABLE_BOOST_SERIALIZATION_WIDE_TEXT +#ifndef _QX_ENABLE_BOOST_SERIALIZATION_WIDE_XML +#define _QX_ENABLE_BOOST_SERIALIZATION_BINARY +#define _QX_ENABLE_BOOST_SERIALIZATION_XML +#endif // _QX_ENABLE_BOOST_SERIALIZATION_WIDE_XML +#endif // _QX_ENABLE_BOOST_SERIALIZATION_WIDE_TEXT +#endif // _QX_ENABLE_BOOST_SERIALIZATION_WIDE_BINARY +#endif // _QX_ENABLE_BOOST_SERIALIZATION_PORTABLE_BINARY +#endif // _QX_ENABLE_BOOST_SERIALIZATION_TEXT +#endif // _QX_ENABLE_BOOST_SERIALIZATION_POLYMORPHIC +#endif // _QX_ENABLE_BOOST_SERIALIZATION_XML +#endif // _QX_ENABLE_BOOST_SERIALIZATION_BINARY + +#ifndef _QX_ENABLE_BOOST_SERIALIZATION +#ifdef _QX_ENABLE_BOOST_SERIALIZATION_BINARY +#undef _QX_ENABLE_BOOST_SERIALIZATION_BINARY +#endif // _QX_ENABLE_BOOST_SERIALIZATION_BINARY +#ifdef _QX_ENABLE_BOOST_SERIALIZATION_XML +#undef _QX_ENABLE_BOOST_SERIALIZATION_XML +#endif // _QX_ENABLE_BOOST_SERIALIZATION_XML +#ifdef _QX_ENABLE_BOOST_SERIALIZATION_POLYMORPHIC +#undef _QX_ENABLE_BOOST_SERIALIZATION_POLYMORPHIC +#endif // _QX_ENABLE_BOOST_SERIALIZATION_POLYMORPHIC +#ifdef _QX_ENABLE_BOOST_SERIALIZATION_TEXT +#undef _QX_ENABLE_BOOST_SERIALIZATION_TEXT +#endif // _QX_ENABLE_BOOST_SERIALIZATION_TEXT +#ifdef _QX_ENABLE_BOOST_SERIALIZATION_PORTABLE_BINARY +#undef _QX_ENABLE_BOOST_SERIALIZATION_PORTABLE_BINARY +#endif // _QX_ENABLE_BOOST_SERIALIZATION_PORTABLE_BINARY +#ifdef _QX_ENABLE_BOOST_SERIALIZATION_WIDE_BINARY +#undef _QX_ENABLE_BOOST_SERIALIZATION_WIDE_BINARY +#endif // _QX_ENABLE_BOOST_SERIALIZATION_WIDE_BINARY +#ifdef _QX_ENABLE_BOOST_SERIALIZATION_WIDE_TEXT +#undef _QX_ENABLE_BOOST_SERIALIZATION_WIDE_TEXT +#endif // _QX_ENABLE_BOOST_SERIALIZATION_WIDE_TEXT +#ifdef _QX_ENABLE_BOOST_SERIALIZATION_WIDE_XML +#undef _QX_ENABLE_BOOST_SERIALIZATION_WIDE_XML +#endif // _QX_ENABLE_BOOST_SERIALIZATION_WIDE_XML +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION_POLYMORPHIC +#define _QX_SERIALIZE_POLYMORPHIC 1 +#else // _QX_ENABLE_BOOST_SERIALIZATION_POLYMORPHIC +#define _QX_SERIALIZE_POLYMORPHIC 0 +#endif // _QX_ENABLE_BOOST_SERIALIZATION_POLYMORPHIC + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION_BINARY +#define _QX_SERIALIZE_BINARY (!_QX_SERIALIZE_POLYMORPHIC && 1) +#else // _QX_ENABLE_BOOST_SERIALIZATION_BINARY +#define _QX_SERIALIZE_BINARY (!_QX_SERIALIZE_POLYMORPHIC && 0) +#endif // _QX_ENABLE_BOOST_SERIALIZATION_BINARY + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION_TEXT +#define _QX_SERIALIZE_TEXT (!_QX_SERIALIZE_POLYMORPHIC && 1) +#else // _QX_ENABLE_BOOST_SERIALIZATION_TEXT +#define _QX_SERIALIZE_TEXT (!_QX_SERIALIZE_POLYMORPHIC && 0) +#endif // _QX_ENABLE_BOOST_SERIALIZATION_TEXT + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION_XML +#define _QX_SERIALIZE_XML (!_QX_SERIALIZE_POLYMORPHIC && 1) +#else // _QX_ENABLE_BOOST_SERIALIZATION_XML +#define _QX_SERIALIZE_XML (!_QX_SERIALIZE_POLYMORPHIC && 0) +#endif // _QX_ENABLE_BOOST_SERIALIZATION_XML + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION_PORTABLE_BINARY +#define _QX_SERIALIZE_PORTABLE_BINARY (!_QX_SERIALIZE_POLYMORPHIC && 1) +#else // _QX_ENABLE_BOOST_SERIALIZATION_PORTABLE_BINARY +#define _QX_SERIALIZE_PORTABLE_BINARY (!_QX_SERIALIZE_POLYMORPHIC && 0) +#endif // _QX_ENABLE_BOOST_SERIALIZATION_PORTABLE_BINARY + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION_WIDE_BINARY +#define _QX_SERIALIZE_WIDE_BINARY (!_QX_SERIALIZE_POLYMORPHIC && 1) +#else // _QX_ENABLE_BOOST_SERIALIZATION_WIDE_BINARY +#define _QX_SERIALIZE_WIDE_BINARY (!_QX_SERIALIZE_POLYMORPHIC && 0) +#endif // _QX_ENABLE_BOOST_SERIALIZATION_WIDE_BINARY + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION_WIDE_TEXT +#define _QX_SERIALIZE_WIDE_TEXT (!_QX_SERIALIZE_POLYMORPHIC && 1) +#else // _QX_ENABLE_BOOST_SERIALIZATION_WIDE_TEXT +#define _QX_SERIALIZE_WIDE_TEXT (!_QX_SERIALIZE_POLYMORPHIC && 0) +#endif // _QX_ENABLE_BOOST_SERIALIZATION_WIDE_TEXT + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION_WIDE_XML +#define _QX_SERIALIZE_WIDE_XML (!_QX_SERIALIZE_POLYMORPHIC && 1) +#else // _QX_ENABLE_BOOST_SERIALIZATION_WIDE_XML +#define _QX_SERIALIZE_WIDE_XML (!_QX_SERIALIZE_POLYMORPHIC && 0) +#endif // _QX_ENABLE_BOOST_SERIALIZATION_WIDE_XML + +#define _QX_AUTO_REGISTER_REPOSITORY 0 +#define _QX_USE_MEM_LEAK_DETECTION 0 +#define _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON 1 +#define _QX_USE_BOOST_SERIALIZE_REGISTER_HELPER 0 +#define _QX_USE_MODIFY_BOOST_SERIALIZATION_EXPORT_HPP 0 +#define _QX_WRITE_BOOST_CLASS_EXPORT_IN_HPP_FILE 0 +#define _QX_WRITE_BOOST_CLASS_EXPORT_IN_CPP_FILE 1 +#define _QX_INCLUDE_BOOST_SERIALIZE_EXPORT_HPP_INTO_QX_MEM_LEAK_HPP 1 +#define _QX_INCLUDE_BOOST_SERIALIZE_ARCHIVE_IMPL_IPP 0 +#define _QX_MEM_LEAK_ONLY_KNOWN_SRC_FILE 1 +#define _QX_SUPPORT_BOOST_SERIALIZE_SHARED_PTR_132 0 +#define _QX_USE_QX_CONVERT_EXPORT 0 +#define _QX_USE_GCC_EXPORT_ALL_SYMBOLS 0 +#define _QX_USE_GCC_VISIBILITY 0 +#define _QX_USE_ASSERT 1 +#define _QX_SUPPORT_COVARIANT_RETURN_TYPE 1 +#define _QX_USE_QX_SINGLETON_X 1 + +#ifdef _MSC_VER +/* -- Link error with VC++ 9.0 => Qt uses "-Zc:wchar_t-" option to compile and boost serialization library is compiled without this option -- */ +#define _QX_USE_SERIALIZE_POLYMORPHIC_PATCH (_QX_SERIALIZE_POLYMORPHIC && 1) +#else // _MSC_VER +#define _QX_USE_SERIALIZE_POLYMORPHIC_PATCH 0 +#endif // _MSC_VER + +#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) +#ifndef _QX_NO_JSON +#define _QX_NO_JSON +#endif // _QX_NO_JSON +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) + +#endif // _QX_CONFIG_H_ diff --git a/include/QxCommon/QxException.h b/include/QxCommon/QxException.h new file mode 100644 index 0000000..e7b257b --- /dev/null +++ b/include/QxCommon/QxException.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_EXCEPTION_H_ +#define _QX_EXCEPTION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxException.h + * \author XDL Team + * \ingroup QxCommon + * \brief Exception with error code and error description + */ + +#include +#include + +#include + +namespace qx +{ + + /*! + * \ingroup QxCommon + * \brief qx::exception : exception with error code and error description + */ + class exception : public std::exception + { + + private: + long m_code; //!< Error code + QString m_desc; //!< Error description + QString m_what; //!< Formatted error : code + "^" + description + + public: + exception(const QString &desc) : std::exception(), m_code(0), m_desc(desc) { updateWhat(); } + exception(long code, const QString &desc) : std::exception(), m_code(code), m_desc(desc) { updateWhat(); } + virtual ~exception() throw() { ; } + + virtual const char *what() const throw() { return qPrintable(m_what); } + + long getCode() const { return m_code; } + QString getDescription() const { return m_desc; } + qx_bool toQxBool() const { return qx_bool(m_code, m_desc); } + + private: + void updateWhat() { m_what = (QString::number(m_code) + QString("^") + m_desc); } + }; + +} // namespace qx + +#endif // _QX_EXCEPTION_H_ diff --git a/include/QxCommon/QxExceptionCode.h b/include/QxCommon/QxExceptionCode.h new file mode 100644 index 0000000..091a7e3 --- /dev/null +++ b/include/QxCommon/QxExceptionCode.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_EXCEPTION_CODE_H_ +#define _QX_EXCEPTION_CODE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxExceptionCode.h + * \author XDL Team + * \ingroup QxCommon + * \brief Some errors codes defined by QxOrm library + */ + +#include + +#define QX_ERROR_UNKNOWN 1 +#define QX_ERROR_SERVICE_NOT_SPECIFIED 2 +#define QX_ERROR_SERVICE_INVALID 3 +#define QX_ERROR_SERVER_NOT_FOUND 4 +#define QX_ERROR_SERVICE_WRITE_ERROR 5 +#define QX_ERROR_SERVICE_READ_ERROR 6 +#define QX_ERROR_SERVICE_SSL_ENCRYPTED 7 + +#define QX_EXCEPTION_UNKNOWN qx::exception(QX_ERROR_UNKNOWN, "unknown error") +#define QX_EXCEPTION_SERVICE_NOT_SPECIFIED qx::exception(QX_ERROR_SERVICE_NOT_SPECIFIED, "[QxOrm] empty service name") +#define QX_EXCEPTION_SERVICE_INVALID qx::exception(QX_ERROR_SERVICE_INVALID, "[QxOrm] invalid service") +#define QX_EXCEPTION_SERVER_NOT_FOUND qx::exception(QX_ERROR_SERVER_NOT_FOUND, "[QxOrm] unable to connect to server") +#define QX_EXCEPTION_SERVICE_WRITE_ERROR qx::exception(QX_ERROR_SERVICE_WRITE_ERROR, "[QxOrm] unable to write request to socket") +#define QX_EXCEPTION_SERVICE_READ_ERROR qx::exception(QX_ERROR_SERVICE_READ_ERROR, "[QxOrm] unable to read reply from socket") +#define QX_EXCEPTION_SERVICE_SSL_ENCRYPTED qx::exception(QX_ERROR_SERVICE_SSL_ENCRYPTED, "[QxOrm] SSL socket encrypted error") + +#endif // _QX_EXCEPTION_CODE_H_ diff --git a/include/QxCommon/QxHashValue.h b/include/QxCommon/QxHashValue.h new file mode 100644 index 0000000..df86314 --- /dev/null +++ b/include/QxCommon/QxHashValue.h @@ -0,0 +1,493 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_HASH_VALUE_H_ +#define _QX_HASH_VALUE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxHashValue.h + * \author XDL Team + * \ingroup QxCommon + * \brief Specialize hash_value function for some Qt and boost types (used for example by qx::QxCollection container) + */ + +#include +#include +#include +#include + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#define qx_hash_result uint +#else // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#define qx_hash_result std::size_t +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +inline std::size_t hash_value(const QString &s) { return qHash(s); } +inline std::size_t hash_value(const QVariant &v) { return qHash(v.toString()); } +inline std::size_t hash_value(const QDate &d) { return qHash(d.toJulianDay()); } +inline std::size_t hash_value(const QTime &t) { return qHash(t.toString()); } +inline std::size_t hash_value(const QDateTime &dt) { return qHash(dt.toString()); } + +inline qx_hash_result qHash(const QVariant &v) { return static_cast(hash_value(v)); } + +#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) +inline qx_hash_result qHash(const QDate &d) { return static_cast(hash_value(d)); } +inline qx_hash_result qHash(const QTime &t) { return static_cast(hash_value(t)); } +inline qx_hash_result qHash(const QDateTime &dt) { return static_cast(hash_value(dt)); } +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) + +namespace qx +{ + template + inline void hash_combine(std::size_t &seed, const T &t) + { + seed ^= qHash(t) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + } +} // namespace qx + +template +inline std::size_t hash_value(const QPair &p) +{ + std::size_t seed = 0; + qx::hash_combine(seed, p.first); + qx::hash_combine(seed, p.second); + return seed; +} + +#ifdef _QX_ENABLE_BOOST + +namespace boost +{ + namespace tuples + { + + template + inline std::size_t hash_value(const boost::tuple &tu) + { + std::size_t seed = 0; + qx::hash_combine(seed, boost::get<0>(tu)); + qx::hash_combine(seed, boost::get<1>(tu)); + return seed; + } + + template + inline std::size_t hash_value(const boost::tuple &tu) + { + std::size_t seed = 0; + qx::hash_combine(seed, boost::get<0>(tu)); + qx::hash_combine(seed, boost::get<1>(tu)); + qx::hash_combine(seed, boost::get<2>(tu)); + return seed; + } + + template + inline std::size_t hash_value(const boost::tuple &tu) + { + std::size_t seed = 0; + qx::hash_combine(seed, boost::get<0>(tu)); + qx::hash_combine(seed, boost::get<1>(tu)); + qx::hash_combine(seed, boost::get<2>(tu)); + qx::hash_combine(seed, boost::get<3>(tu)); + return seed; + } + + template + inline std::size_t hash_value(const boost::tuple &tu) + { + std::size_t seed = 0; + qx::hash_combine(seed, boost::get<0>(tu)); + qx::hash_combine(seed, boost::get<1>(tu)); + qx::hash_combine(seed, boost::get<2>(tu)); + qx::hash_combine(seed, boost::get<3>(tu)); + qx::hash_combine(seed, boost::get<4>(tu)); + return seed; + } + + template + inline std::size_t hash_value(const boost::tuple &tu) + { + std::size_t seed = 0; + qx::hash_combine(seed, boost::get<0>(tu)); + qx::hash_combine(seed, boost::get<1>(tu)); + qx::hash_combine(seed, boost::get<2>(tu)); + qx::hash_combine(seed, boost::get<3>(tu)); + qx::hash_combine(seed, boost::get<4>(tu)); + qx::hash_combine(seed, boost::get<5>(tu)); + return seed; + } + + template + inline std::size_t hash_value(const boost::tuple &tu) + { + std::size_t seed = 0; + qx::hash_combine(seed, boost::get<0>(tu)); + qx::hash_combine(seed, boost::get<1>(tu)); + qx::hash_combine(seed, boost::get<2>(tu)); + qx::hash_combine(seed, boost::get<3>(tu)); + qx::hash_combine(seed, boost::get<4>(tu)); + qx::hash_combine(seed, boost::get<5>(tu)); + qx::hash_combine(seed, boost::get<6>(tu)); + return seed; + } + + template + inline std::size_t hash_value(const boost::tuple &tu) + { + std::size_t seed = 0; + qx::hash_combine(seed, boost::get<0>(tu)); + qx::hash_combine(seed, boost::get<1>(tu)); + qx::hash_combine(seed, boost::get<2>(tu)); + qx::hash_combine(seed, boost::get<3>(tu)); + qx::hash_combine(seed, boost::get<4>(tu)); + qx::hash_combine(seed, boost::get<5>(tu)); + qx::hash_combine(seed, boost::get<6>(tu)); + qx::hash_combine(seed, boost::get<7>(tu)); + return seed; + } + + template + inline std::size_t hash_value(const boost::tuple &tu) + { + std::size_t seed = 0; + qx::hash_combine(seed, boost::get<0>(tu)); + qx::hash_combine(seed, boost::get<1>(tu)); + qx::hash_combine(seed, boost::get<2>(tu)); + qx::hash_combine(seed, boost::get<3>(tu)); + qx::hash_combine(seed, boost::get<4>(tu)); + qx::hash_combine(seed, boost::get<5>(tu)); + qx::hash_combine(seed, boost::get<6>(tu)); + qx::hash_combine(seed, boost::get<7>(tu)); + qx::hash_combine(seed, boost::get<8>(tu)); + return seed; + } + + template + inline std::size_t hash_value(const boost::tuple &tu) + { + std::size_t seed = 0; + qx::hash_combine(seed, boost::get<0>(tu)); + qx::hash_combine(seed, boost::get<1>(tu)); + qx::hash_combine(seed, boost::get<2>(tu)); + qx::hash_combine(seed, boost::get<3>(tu)); + qx::hash_combine(seed, boost::get<4>(tu)); + qx::hash_combine(seed, boost::get<5>(tu)); + qx::hash_combine(seed, boost::get<6>(tu)); + qx::hash_combine(seed, boost::get<7>(tu)); + qx::hash_combine(seed, boost::get<8>(tu)); + qx::hash_combine(seed, boost::get<9>(tu)); + return seed; + } + + template + inline qx_hash_result qHash(const boost::tuple &tu) + { + return static_cast(hash_value(tu)); + } + + template + inline qx_hash_result qHash(const boost::tuple &tu) + { + return static_cast(hash_value(tu)); + } + + template + inline qx_hash_result qHash(const boost::tuple &tu) + { + return static_cast(hash_value(tu)); + } + + template + inline qx_hash_result qHash(const boost::tuple &tu) + { + return static_cast(hash_value(tu)); + } + + template + inline qx_hash_result qHash(const boost::tuple &tu) + { + return static_cast(hash_value(tu)); + } + + template + inline qx_hash_result qHash(const boost::tuple &tu) + { + return static_cast(hash_value(tu)); + } + + template + inline qx_hash_result qHash(const boost::tuple &tu) + { + return static_cast(hash_value(tu)); + } + + template + inline qx_hash_result qHash(const boost::tuple &tu) + { + return static_cast(hash_value(tu)); + } + + template + inline qx_hash_result qHash(const boost::tuple &tu) + { + return static_cast(hash_value(tu)); + } + + } // namespace tuples +} // namespace boost + +#endif // _QX_ENABLE_BOOST + +// Compilation option '_QX_HASH_NO_STD_NAMESPACE' +// Try to avoid compilation error, something like : error: no matching function for call to 'qHash(const std::tuple<...>&)' +// This is due to C++ ADL to resolve specialized functions : qHash(T) should be implemented in the same namespace as T +// For 'std' classes, it should be NOT allowed : the behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std +// More details here : https://www.kdab.com/how-to-declare-a-qhash-overload/ +// And here : https://stackoverflow.com/questions/47460098/using-standard-library-types-as-keys-in-qhash-or-qset +#ifndef _QX_HASH_NO_STD_NAMESPACE +namespace std +{ +#endif // _QX_HASH_NO_STD_NAMESPACE + +#ifndef QT_NO_STL + inline qx_hash_result qHash(const std::string &s) + { + QString tmp = QString::fromStdString(s); + return qHash(tmp); + } + inline qx_hash_result qHash(const std::wstring &s) + { + QString tmp = QString::fromStdWString(s); + return qHash(tmp); + } +#else // QT_NO_STL +inline qx_hash_result qHash(const std::string &s) +{ + QString tmp = QString::fromLatin1(s.data(), int(s.size())); + return qHash(tmp); +} +inline qx_hash_result qHash(const std::wstring &s) +{ + qAssert(false); /* Need STL compatibility ! */ + return 0; +} +#endif // QT_NO_STL + + template + inline std::size_t hash_value(const std::tuple &tu) + { + std::size_t seed = 0; + qx::hash_combine(seed, std::get<0>(tu)); + qx::hash_combine(seed, std::get<1>(tu)); + return seed; + } + + template + inline std::size_t hash_value(const std::tuple &tu) + { + std::size_t seed = 0; + qx::hash_combine(seed, std::get<0>(tu)); + qx::hash_combine(seed, std::get<1>(tu)); + qx::hash_combine(seed, std::get<2>(tu)); + return seed; + } + + template + inline std::size_t hash_value(const std::tuple &tu) + { + std::size_t seed = 0; + qx::hash_combine(seed, std::get<0>(tu)); + qx::hash_combine(seed, std::get<1>(tu)); + qx::hash_combine(seed, std::get<2>(tu)); + qx::hash_combine(seed, std::get<3>(tu)); + return seed; + } + + template + inline std::size_t hash_value(const std::tuple &tu) + { + std::size_t seed = 0; + qx::hash_combine(seed, std::get<0>(tu)); + qx::hash_combine(seed, std::get<1>(tu)); + qx::hash_combine(seed, std::get<2>(tu)); + qx::hash_combine(seed, std::get<3>(tu)); + qx::hash_combine(seed, std::get<4>(tu)); + return seed; + } + + template + inline std::size_t hash_value(const std::tuple &tu) + { + std::size_t seed = 0; + qx::hash_combine(seed, std::get<0>(tu)); + qx::hash_combine(seed, std::get<1>(tu)); + qx::hash_combine(seed, std::get<2>(tu)); + qx::hash_combine(seed, std::get<3>(tu)); + qx::hash_combine(seed, std::get<4>(tu)); + qx::hash_combine(seed, std::get<5>(tu)); + return seed; + } + + template + inline std::size_t hash_value(const std::tuple &tu) + { + std::size_t seed = 0; + qx::hash_combine(seed, std::get<0>(tu)); + qx::hash_combine(seed, std::get<1>(tu)); + qx::hash_combine(seed, std::get<2>(tu)); + qx::hash_combine(seed, std::get<3>(tu)); + qx::hash_combine(seed, std::get<4>(tu)); + qx::hash_combine(seed, std::get<5>(tu)); + qx::hash_combine(seed, std::get<6>(tu)); + return seed; + } + + template + inline std::size_t hash_value(const std::tuple &tu) + { + std::size_t seed = 0; + qx::hash_combine(seed, std::get<0>(tu)); + qx::hash_combine(seed, std::get<1>(tu)); + qx::hash_combine(seed, std::get<2>(tu)); + qx::hash_combine(seed, std::get<3>(tu)); + qx::hash_combine(seed, std::get<4>(tu)); + qx::hash_combine(seed, std::get<5>(tu)); + qx::hash_combine(seed, std::get<6>(tu)); + qx::hash_combine(seed, std::get<7>(tu)); + return seed; + } + + template + inline std::size_t hash_value(const std::tuple &tu) + { + std::size_t seed = 0; + qx::hash_combine(seed, std::get<0>(tu)); + qx::hash_combine(seed, std::get<1>(tu)); + qx::hash_combine(seed, std::get<2>(tu)); + qx::hash_combine(seed, std::get<3>(tu)); + qx::hash_combine(seed, std::get<4>(tu)); + qx::hash_combine(seed, std::get<5>(tu)); + qx::hash_combine(seed, std::get<6>(tu)); + qx::hash_combine(seed, std::get<7>(tu)); + qx::hash_combine(seed, std::get<8>(tu)); + return seed; + } + + template + inline std::size_t hash_value(const std::tuple &tu) + { + std::size_t seed = 0; + qx::hash_combine(seed, std::get<0>(tu)); + qx::hash_combine(seed, std::get<1>(tu)); + qx::hash_combine(seed, std::get<2>(tu)); + qx::hash_combine(seed, std::get<3>(tu)); + qx::hash_combine(seed, std::get<4>(tu)); + qx::hash_combine(seed, std::get<5>(tu)); + qx::hash_combine(seed, std::get<6>(tu)); + qx::hash_combine(seed, std::get<7>(tu)); + qx::hash_combine(seed, std::get<8>(tu)); + qx::hash_combine(seed, std::get<9>(tu)); + return seed; + } + +#if ((QT_VERSION < QT_VERSION_CHECK(5, 7, 0)) || ((QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && (QT_VERSION < QT_VERSION_CHECK(6, 2, 0)))) + template + inline qx_hash_result qHash(const std::pair &p) + { + std::size_t seed = 0; + qx::hash_combine(seed, p.first); + qx::hash_combine(seed, p.second); + return static_cast(seed); + } +#endif // ((QT_VERSION < QT_VERSION_CHECK(5, 7, 0)) || ((QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && (QT_VERSION < QT_VERSION_CHECK(6, 2, 0)))) + + template + inline qx_hash_result qHash(const std::tuple &tu) + { + return static_cast(hash_value(tu)); + } + + template + inline qx_hash_result qHash(const std::tuple &tu) + { + return static_cast(hash_value(tu)); + } + + template + inline qx_hash_result qHash(const std::tuple &tu) + { + return static_cast(hash_value(tu)); + } + + template + inline qx_hash_result qHash(const std::tuple &tu) + { + return static_cast(hash_value(tu)); + } + + template + inline qx_hash_result qHash(const std::tuple &tu) + { + return static_cast(hash_value(tu)); + } + + template + inline qx_hash_result qHash(const std::tuple &tu) + { + return static_cast(hash_value(tu)); + } + + template + inline qx_hash_result qHash(const std::tuple &tu) + { + return static_cast(hash_value(tu)); + } + + template + inline qx_hash_result qHash(const std::tuple &tu) + { + return static_cast(hash_value(tu)); + } + + template + inline qx_hash_result qHash(const std::tuple &tu) + { + return static_cast(hash_value(tu)); + } + +#ifndef _QX_HASH_NO_STD_NAMESPACE +} // namespace std +#endif // _QX_HASH_NO_STD_NAMESPACE + +#endif // _QX_HASH_VALUE_H_ diff --git a/include/QxCommon/QxMacro.h b/include/QxCommon/QxMacro.h new file mode 100644 index 0000000..90878ef --- /dev/null +++ b/include/QxCommon/QxMacro.h @@ -0,0 +1,298 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_MACRO_H_ +#define _QX_MACRO_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxMacro.h + * \author XDL Team + * \ingroup QxCommon + * \brief List of common macros used by QxOrm library + */ + +#include + +#ifndef qAssert +#if _QX_USE_ASSERT +#define qAssert Q_ASSERT +#else // _QX_USE_ASSERT +#define qAssert(x) /* Nothing */ +#endif // _QX_USE_ASSERT +#endif // qAssert + +#ifndef qAssertMsg +#if _QX_USE_ASSERT +#define qAssertMsg(test, where, what) Q_ASSERT_X(test, where, what) +#else // _QX_USE_ASSERT +#define qAssertMsg(test, where, what) /* Nothing */ +#endif // _QX_USE_ASSERT +#endif // qAssertMsg + +#ifndef QX_PRAGMA +#ifdef __GNUC__ +#define QX_PRAGMA(x) _Pragma(#x) +#endif // __GNUC__ +#ifdef _MSC_VER +#define QX_PRAGMA(x) __pragma(x) +#endif // _MSC_VER +#ifndef QX_PRAGMA +#define QX_PRAGMA(x) /* Nothing */ +#endif // QX_PRAGMA +#endif // QX_PRAGMA + +#ifndef QX_DLL_EXPORT_HELPER +#ifdef Q_OS_WIN +#define QX_DLL_EXPORT_HELPER __declspec(dllexport) +#elif (_QX_USE_GCC_VISIBILITY && (__GNUC__ >= 4)) +#define QX_DLL_EXPORT_HELPER __attribute__((visibility("default"))) +#else +#define QX_DLL_EXPORT_HELPER /* Nothing */ +#endif // Q_OS_WIN +#endif // QX_DLL_EXPORT_HELPER + +#ifndef QX_DLL_IMPORT_HELPER +#ifdef Q_OS_WIN +#define QX_DLL_IMPORT_HELPER __declspec(dllimport) +#elif (_QX_USE_GCC_VISIBILITY && (__GNUC__ >= 4)) +#define QX_DLL_IMPORT_HELPER __attribute__((visibility("default"))) +#else +#define QX_DLL_IMPORT_HELPER /* Nothing */ +#endif // Q_OS_WIN +#endif // QX_DLL_IMPORT_HELPER + +#ifdef __GNUC__ +#if _QX_USE_GCC_EXPORT_ALL_SYMBOLS +#undef QX_DLL_EXPORT_HELPER +#undef QX_DLL_IMPORT_HELPER +#define QX_DLL_EXPORT_HELPER /* Nothing */ +#define QX_DLL_IMPORT_HELPER /* Nothing */ +#endif // _QX_USE_GCC_EXPORT_ALL_SYMBOLS +#endif // __GNUC__ + +#ifdef _QX_STATIC_BUILD +#undef QX_DLL_EXPORT_HELPER +#undef QX_DLL_IMPORT_HELPER +#define QX_DLL_EXPORT_HELPER /* Nothing */ +#define QX_DLL_IMPORT_HELPER /* Nothing */ +#endif // _QX_STATIC_BUILD + +#ifndef QX_DLL_INTERNAL_HELPER +#if (_QX_USE_GCC_VISIBILITY && (__GNUC__ >= 4)) +#define QX_DLL_INTERNAL_HELPER __attribute__((visibility("hidden"))) +#else +#define QX_DLL_INTERNAL_HELPER /* Nothing */ +#endif // (_QX_USE_GCC_VISIBILITY && (__GNUC__ >= 4)) +#endif // QX_DLL_INTERNAL_HELPER + +#ifndef QX_DLL_EXPORT_TEMPLATE_HELPER +#ifdef _MSC_VER +#define QX_DLL_EXPORT_TEMPLATE_HELPER QX_DLL_EXPORT_HELPER +#else // _MSC_VER +#define QX_DLL_EXPORT_TEMPLATE_HELPER /* Nothing */ +#endif // _MSC_VER +#endif // QX_DLL_EXPORT_TEMPLATE_HELPER + +#ifndef QX_DLL_IMPORT_TEMPLATE_HELPER +#ifdef _MSC_VER +#define QX_DLL_IMPORT_TEMPLATE_HELPER QX_DLL_IMPORT_HELPER +#else // _MSC_VER +#define QX_DLL_IMPORT_TEMPLATE_HELPER /* Nothing */ +#endif // _MSC_VER +#endif // QX_DLL_IMPORT_TEMPLATE_HELPER + +#ifndef QX_PRAGMA_VISIBILITY_BEGIN +#ifndef Q_OS_WIN +#if (_QX_USE_GCC_VISIBILITY && (__GNUC__ >= 4)) +#define QX_PRAGMA_VISIBILITY_BEGIN QX_PRAGMA(GCC visibility push(default)) +#endif // (_QX_USE_GCC_VISIBILITY && (__GNUC__ >= 4)) +#endif // Q_OS_WIN +#ifndef QX_PRAGMA_VISIBILITY_BEGIN +#define QX_PRAGMA_VISIBILITY_BEGIN /* Nothing */ +#endif // QX_PRAGMA_VISIBILITY_BEGIN +#endif // QX_PRAGMA_VISIBILITY_BEGIN + +#ifndef QX_PRAGMA_VISIBILITY_END +#ifndef Q_OS_WIN +#if (_QX_USE_GCC_VISIBILITY && (__GNUC__ >= 4)) +#define QX_PRAGMA_VISIBILITY_END QX_PRAGMA(GCC visibility pop) +#endif // (_QX_USE_GCC_VISIBILITY && (__GNUC__ >= 4)) +#endif // Q_OS_WIN +#ifndef QX_PRAGMA_VISIBILITY_END +#define QX_PRAGMA_VISIBILITY_END /* Nothing */ +#endif // QX_PRAGMA_VISIBILITY_END +#endif // QX_PRAGMA_VISIBILITY_END + +#define QX_DLL_EXPORT_TEMPLATE_HPP(CL, T) \ + QX_PRAGMA_VISIBILITY_BEGIN extern template CL QX_DLL_IMPORT_TEMPLATE_HELPER T; \ + QX_PRAGMA_VISIBILITY_END +#define QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(CL, T, P1) \ + QX_PRAGMA_VISIBILITY_BEGIN extern template CL QX_DLL_IMPORT_TEMPLATE_HELPER T; \ + QX_PRAGMA_VISIBILITY_END +#define QX_DLL_EXPORT_TEMPLATE_T_U_P1_HPP(CL, T, U, P1) \ + QX_PRAGMA_VISIBILITY_BEGIN extern template CL QX_DLL_IMPORT_TEMPLATE_HELPER T>; \ + QX_PRAGMA_VISIBILITY_END +#define QX_DLL_EXPORT_TEMPLATE_T_P1_P2_HPP(CL, T, P1, P2) \ + QX_PRAGMA_VISIBILITY_BEGIN extern template CL QX_DLL_IMPORT_TEMPLATE_HELPER T; \ + QX_PRAGMA_VISIBILITY_END +#define QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_HPP(CL, T, U, P1, P2) \ + QX_PRAGMA_VISIBILITY_BEGIN extern template CL QX_DLL_IMPORT_TEMPLATE_HELPER T>; \ + QX_PRAGMA_VISIBILITY_END + +#define QX_DLL_EXPORT_TEMPLATE_CPP(CL, T) \ + QX_PRAGMA_VISIBILITY_BEGIN template CL QX_DLL_EXPORT_TEMPLATE_HELPER T; \ + QX_PRAGMA_VISIBILITY_END +#define QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(CL, T, P1) \ + QX_PRAGMA_VISIBILITY_BEGIN template CL QX_DLL_EXPORT_TEMPLATE_HELPER T; \ + QX_PRAGMA_VISIBILITY_END +#define QX_DLL_EXPORT_TEMPLATE_T_U_P1_CPP(CL, T, U, P1) \ + QX_PRAGMA_VISIBILITY_BEGIN template CL QX_DLL_EXPORT_TEMPLATE_HELPER T>; \ + QX_PRAGMA_VISIBILITY_END +#define QX_DLL_EXPORT_TEMPLATE_T_P1_P2_CPP(CL, T, P1, P2) \ + QX_PRAGMA_VISIBILITY_BEGIN template CL QX_DLL_EXPORT_TEMPLATE_HELPER T; \ + QX_PRAGMA_VISIBILITY_END +#define QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_CPP(CL, T, U, P1, P2) \ + QX_PRAGMA_VISIBILITY_BEGIN template CL QX_DLL_EXPORT_TEMPLATE_HELPER T>; \ + QX_PRAGMA_VISIBILITY_END + +#define QX_TEMPLATE_T(T) T<> +#define QX_TEMPLATE_T_P1(T, P1) T +#define QX_TEMPLATE_T_P1_P2(T, P1, P2) T +#define QX_TEMPLATE_T_P1_P2_P3(T, P1, P2, P3) T +#define QX_TEMPLATE_T_U_P1(T, U, P1) T> +#define QX_TEMPLATE_T_U_P1_P2(T, U, P1, P2) T> +#define QX_TEMPLATE_T_U_P1_P2_P3(T, U, P1, P2, P3) T> + +#ifndef QX_DLL_EXPORT +#ifdef _QX_BUILDING_QX_ORM +#define QX_DLL_EXPORT QX_DLL_EXPORT_HELPER +#else // _QX_BUILDING_QX_ORM +#define QX_DLL_EXPORT QX_DLL_IMPORT_HELPER +#endif // _QX_BUILDING_QX_ORM +#endif // QX_DLL_EXPORT + +#ifndef QX_DLL_EXPORT_QX_SINGLETON_HPP +#ifdef _QX_BUILDING_QX_ORM +#define QX_DLL_EXPORT_QX_SINGLETON_HPP(x) /* Nothing */ +#else // _QX_BUILDING_QX_ORM +#define QX_DLL_EXPORT_QX_SINGLETON_HPP(x) QX_DLL_EXPORT_TEMPLATE_HPP(class, qx::QxSingleton) +#endif // _QX_BUILDING_QX_ORM +#endif // QX_DLL_EXPORT_QX_SINGLETON_HPP + +#ifndef QX_DLL_EXPORT_QX_SINGLETON_CPP +#ifdef _QX_BUILDING_QX_ORM +#define QX_DLL_EXPORT_QX_SINGLETON_CPP(x) QX_DLL_EXPORT_TEMPLATE_CPP(class, qx::QxSingleton) +#else // _QX_BUILDING_QX_ORM +#define QX_DLL_EXPORT_QX_SINGLETON_CPP(x) /* Nothing */ +#endif // _QX_BUILDING_QX_ORM +#endif // QX_DLL_EXPORT_QX_SINGLETON_CPP + +#ifndef QX_DLL_EXPORT_INLINE_FCT +#ifdef _MSC_VER +#define QX_DLL_EXPORT_INLINE_FCT QX_DLL_EXPORT +#else // _MSC_VER +#define QX_DLL_EXPORT_INLINE_FCT /* Nothing */ +#endif // _MSC_VER +#endif // QX_DLL_EXPORT_INLINE_FCT + +#ifdef __GNUC__ +#define QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE inline +#elif (defined(_MSC_VER) && (_MSC_VER >= 1920)) // MSVC 2019 +#define QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE inline +#else +#define QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE /* Nothing */ +#endif // __GNUC__ + +#ifdef _MSC_VER +#define QX_STRNCPY strncpy_s +#define QX_VSPRINTF vsprintf_s +#else // _MSC_VER +#define QX_STRNCPY strncpy +#define QX_VSPRINTF vsprintf +#endif // _MSC_VER + +#ifdef _QX_MODE_RELEASE +#ifndef NDEBUG +#define NDEBUG +#endif // NDEBUG +#endif // _QX_MODE_RELEASE + +// From file (written by Robert Ramey) +#if !defined(_WIN32) && !defined(_WIN64) +#if defined(__GNUC__) && (__GNUC__ >= 3) +#define QX_USED __attribute__((__used__)) +#elif defined(__IBMCPP__) && (__IBMCPP__ >= 1110) +#define QX_USED __attribute__((__used__)) +#elif defined(__INTEL_COMPILER) +#define QX_USED __attribute__((__used__)) +#endif +#endif // ! defined(_WIN32) && ! defined(_WIN64) + +#ifndef QX_USED +#define QX_USED /* Nothing */ +#endif // QX_USED + +#ifdef QT_NO_OPENSSL +#ifndef QT_NO_SSL +#define QT_NO_SSL /* Nothing */ +#endif // QT_NO_SSL +#endif // QT_NO_OPENSSL + +#ifndef Q_DECL_OVERRIDE +#define Q_DECL_OVERRIDE /* Nothing */ +#endif // Q_DECL_OVERRIDE + +#ifndef Q_DECL_FINAL +#define Q_DECL_FINAL /* Nothing */ +#endif // Q_DECL_FINAL + +#ifndef Q_DECL_HIDDEN +#define Q_DECL_HIDDEN /* Nothing */ +#endif // Q_DECL_HIDDEN + +#ifndef Q_DECL_NOEXCEPT +#define Q_DECL_NOEXCEPT /* Nothing */ +#endif // Q_DECL_NOEXCEPT + +#ifndef Q_NULLPTR +#define Q_NULLPTR NULL +#endif // Q_NULLPTR + +// From 'QtCore' directory, 'qtversionchecks.h' file +#ifndef QT_VERSION_CHECK +#define QT_VERSION_CHECK(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +#endif // QT_VERSION_CHECK + +#endif // _QX_MACRO_H_ diff --git a/include/QxCommon/QxMainPage.h b/include/QxCommon/QxMainPage.h new file mode 100644 index 0000000..da3e06b --- /dev/null +++ b/include/QxCommon/QxMainPage.h @@ -0,0 +1,367 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +/* -- Main page for Doxygen documentation -- */ + +/*! + * \mainpage QxOrm - C++ Object Relational Mapping library + * + * QxOrm is a C++ library designed to provide Object Relational Mapping (ORM) feature to C++/Qt developers (like Hibernate in Java, or NHibernate in .Net).
    + * QxOrm engine is based on a simple and non intrusive mapping function per class to provide : + * + * - Persistence (based on QtSql Qt module) ; + * - Serialization (JSON, XML and binary, based on Qt and boost serialization engines) ; + * - Reflection or Introspection (invoke dynamically class methods and access to properties) ; + * - HTTP web server : standalone multi-threaded HTTP 1.1 web server (support SSL/TLS, persistent connections, cookies, sessions, chunked responses, URL dispatcher/routing) ; + * - JSON API : interoperability with other technology than C++/Qt (REST web services, QML applications, scripting language). + * + * QxOrm is developed by XDL Team, a software development engineer since 2003.
    + * QxOrm library has been accepted into the Qt Ambassador Program.
    + * QxOrm library is available on GitHub. + * + * For more information about QxOrm library (quick sample, tutorial and forum), please visit : https://www.qxorm.com/.
    + * A manual (user guide) to learn how to work with QxOrm library is available on QxOrm website. + *

    + * + * \section quick_sample Quick sample using QxOrm library + * + * 1- drug.h file : drug class definition with 3 properties : id, name and description + * \code +#ifndef _CLASS_DRUG_H_ +#define _CLASS_DRUG_H_ + +class drug +{ +public: + long id; + QString name; + QString description; + + drug() : id(0) { ; } + virtual ~drug() { ; } +}; + +QX_REGISTER_HPP_MY_TEST_EXE(drug, qx::trait::no_base_class_defined, 1) + +// This macro is necessary to register 'drug' class in QxOrm context +// param 1 : the current class to register => 'drug' +// param 2 : the base class, if no base class, use the qx trait => 'qx::trait::no_base_class_defined' +// param 3 : the class version used by serialization to provide 'ascendant compatibility' + +#endif // _CLASS_DRUG_H_ + * \endcode + * + *
    + * 2- drug.cpp file : setting function implementation - void qx::register_class() + * \code +#include "precompiled.h" // Precompiled-header with '#include ' and '#include "export.h"' +#include "drug.h" // Class definition 'drug' +#include // Automatic memory leak detection and boost serialization export macro + +QX_REGISTER_CPP_MY_TEST_EXE(drug) // This macro is necessary to register 'drug' class in QxOrm context + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& drug::id, "id"); // Register 'drug::id' <=> primary key in your database + t.data(& drug::name, "name", 1); // Register 'drug::name' property with key 'name' and version '1' + t.data(& drug::description, "desc"); // Register 'drug::description' property with key 'desc' +}} + * \endcode + * + *
    + * 3- main.cpp file : basic functionalities of QxOrm library with drug class + * \code +#include "precompiled.h" +#include "drug.h" +#include + +int main(int argc, char * argv[]) +{ + QApplication app(argc, argv); // Qt application + + // Create 3 new drugs + // It is possible to use 'boost' and 'Qt' smart pointer : 'boost::shared_ptr', 'QSharedPointer', etc... + typedef std::shared_ptr drug_ptr; + drug_ptr d1; d1.reset(new drug()); d1->name = "name1"; d1->description = "desc1"; + drug_ptr d2; d2.reset(new drug()); d2->name = "name2"; d2->description = "desc2"; + drug_ptr d3; d3.reset(new drug()); d3->name = "name3"; d3->description = "desc3"; + + // Insert drugs into container + // It is possible to use a lot of containers from 'std', 'boost', 'Qt' and 'qx::QxCollection' + typedef std::vector type_lst_drug; + type_lst_drug lst_drug; + lst_drug.push_back(d1); + lst_drug.push_back(d2); + lst_drug.push_back(d3); + + // Init parameters to communicate with a database + qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE"); + qx::QxSqlDatabase::getSingleton()->setDatabaseName("./test_qxorm.db"); + qx::QxSqlDatabase::getSingleton()->setHostName("localhost"); + qx::QxSqlDatabase::getSingleton()->setUserName("root"); + qx::QxSqlDatabase::getSingleton()->setPassword(""); + + // Create table 'drug' into database to store drugs + QSqlError daoError = qx::dao::create_table(); + + // Insert drugs from container to database + // 'id' property of 'd1', 'd2' and 'd3' are auto-updated + daoError = qx::dao::insert(lst_drug); + + // Modify and update the second drug into database + d2->name = "name2 modified"; + d2->description = "desc2 modified"; + daoError = qx::dao::update(d2); + + // Delete the first drug from database + daoError = qx::dao::delete_by_id(d1); + + // Count drugs into database + long lDrugCount = qx::dao::count(); + + // Fetch drug with id '3' into a new variable + drug_ptr d_tmp; d_tmp.reset(new drug()); + d_tmp->id = 3; + daoError = qx::dao::fetch_by_id(d_tmp); + + // Export drugs from container to a file under XML format (serialization) + qx::serialization::xml::to_file(lst_drug, "./export_drugs.xml"); + + // Import drugs from XML file into a new container + type_lst_drug lst_drug_tmp; + qx::serialization::xml::from_file(lst_drug_tmp, "./export_drugs.xml"); + + // Clone a drug + drug_ptr d_clone = qx::clone(* d1); + + // Create a new drug by class name (factory) + qx::any d_any = qx::create("drug"); + + // Insert drugs container into 'qx::cache' + qx::cache::set("drugs", lst_drug); + + // Remove all elements from 'qx::cache' + qx::cache::clear(); + + // Create a dummy memory leak + drug * pDummy = new drug(); + + return 0; +} + * \endcode + * + *
    + * 4- execute program and trace output debug + * \code +[QxOrm] qx::QxSqlDatabase : create new database connection in thread '3616' with key '{d315250c-b5c9-46e0-9402-f800368a6673}' +[QxOrm] sql query (78 ms) : CREATE TABLE drug (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT, desc TEXT) +[QxOrm] sql query (63 ms) : INSERT INTO drug (name, desc) VALUES (:name, :desc) +[QxOrm] sql query (62 ms) : UPDATE drug SET id = :id, name = :name, desc = :desc WHERE id = :id_bis +[QxOrm] sql query (63 ms) : DELETE FROM drug WHERE id = :id +[QxOrm] sql query (0 ms) : SELECT COUNT(*) FROM drug +[QxOrm] sql query (0 ms) : SELECT drug.id AS drug_id_0, drug.name AS drug_name_0, drug.desc AS drug_desc_0 FROM drug WHERE drug_id_0 = :id +[QxOrm] Leaked object at 0xf52ad8 (size 16, src\main.cpp:74) +[QxOrm] **** 1 memory leaks found **** + * \endcode + * + *
    + * 5- export_drugs.xml file created by the program + * \code + + 3 + 1 + + + 1 + name1 + desc1 + + + + + 2 + name2 modified + desc2 modified + + + + + 3 + name3 + desc3 + + + + * \endcode + */ + +/*! + * \brief Root namespace for all QxOrm library features + */ +namespace qx +{ + + /*! + * \ingroup QxCache + * \brief Provide basic thread-safe cache feature to backup and restore any kind of objects (for example, object fetched from database) + */ + namespace cache + { + + /*! + * \ingroup QxCache + * \brief Internal helper tools for qx::cache namespace + */ + namespace detail + { + } // namespace detail + + } // namespace cache + + /*! + * \ingroup QxDao + * \brief Database communication used by persistence engine (ORM - Object Relational Mapping) + */ + namespace dao + { + + /*! + * \ingroup QxDao + * \brief Internal helper tools for qx::dao namespace + */ + namespace detail + { + } // namespace detail + + } // namespace dao + + /*! + * \ingroup QxFunction + * \brief Register function into QxOrm context used by introspection engine + */ + namespace function + { + + /*! + * \ingroup QxFunction + * \brief Internal helper tools for qx::function namespace + */ + namespace detail + { + } // namespace detail + + } // namespace function + + /*! + * \ingroup QxMemLeak + * \brief QxOrm library memory leak detection (by Wu Yongwei) + */ + namespace memory + { + } // namespace memory + + /*! + * \ingroup QxSerialize + * \brief QxOrm library serialization engine based on boost::serialization library + */ + namespace serialization + { + + /*! + * \ingroup QxSerialize + * \brief Internal helper tools for qx::serialization namespace + */ + namespace detail + { + } // namespace detail + + /*! + * \ingroup QxSerialize + * \brief QxOrm library serialization engine for wide archive + */ + namespace wide + { + } // namespace wide + + } // namespace serialization + + /*! + * \ingroup QxService + * \brief QxOrm library services engine to provide easy and powerful way to create C++ application server (to transfer data over network) + */ + namespace service + { + } // namespace service + + /*! + * \ingroup QxTraits + * \brief QxOrm library traits (template metaprogramming) not available in boost::type_traits library + */ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief Internal helper tools for qx::trait namespace + */ + namespace detail + { + } // namespace detail + + } // namespace trait + + /*! + * \ingroup QxCollection + * \brief Foreach-style (based on BOOST_FOREACH macro) to iterate over all stl, boost and Qt containers + qx::QxCollection QxOrm library container + */ + namespace foreach + { + } // namespace foreach + + /*! + * \ingroup QxCommon + * \brief Provide global functions to convert any kind of objects to/from QString and QVariant format + */ + namespace cvt + { + + /*! + * \ingroup QxCommon + * \brief Internal helper tools for qx::cvt namespace + */ + namespace detail + { + } // namespace detail + + } // namespace cvt + +} // namespace qx diff --git a/include/QxCommon/QxPropertyBag.h b/include/QxCommon/QxPropertyBag.h new file mode 100644 index 0000000..44dafc5 --- /dev/null +++ b/include/QxCommon/QxPropertyBag.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_PROPERTY_BAG_H_ +#define _QX_PROPERTY_BAG_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxPropertyBag.h + * \author XDL Team + * \ingroup QxCommon + * \brief Used by introspection engine (IxClass, IxDataMember, IxFunction, etc.) to add meta-data (property bag) + */ + +#include +#include +#include + +namespace qx +{ + + /*! + * \ingroup QxCommon + * \brief qx::QxPropertyBag : used by introspection engine (IxClass, IxDataMember, IxFunction, etc.) to add meta-data (property bag) + */ + class QxPropertyBag + { + + protected: + typedef QHash type_hash_prop_bag; + typedef std::shared_ptr type_hash_prop_bag_ptr; + + type_hash_prop_bag_ptr m_lstPropertyBag; //!< List of all properties in the bag (meta-data) + + public: + QxPropertyBag() { ; } + virtual ~QxPropertyBag() { ; } + + void setPropertyBag(const QString &key, const QVariant &value) + { + initPropertyBag(); + m_lstPropertyBag->insert(key, value); + } + QVariant getPropertyBag(const QString &key) const { return ((m_lstPropertyBag && m_lstPropertyBag->contains(key)) ? m_lstPropertyBag->value(key) : QVariant()); } + void removePropertyBag(const QString &key) + { + if (m_lstPropertyBag) + { + m_lstPropertyBag->remove(key); + } + } + void clearPropertyBag() + { + if (m_lstPropertyBag) + { + m_lstPropertyBag->clear(); + } + } + long countPropertyBag() const { return (m_lstPropertyBag ? m_lstPropertyBag->count() : 0); } + QList getAllPropertyBagKeys() const { return (m_lstPropertyBag ? m_lstPropertyBag->keys() : QList()); } + + private: + inline void initPropertyBag() + { + if (!m_lstPropertyBag) + { + m_lstPropertyBag = std::make_shared(); + } + } + }; + +} // namespace qx + +#endif // _QX_PROPERTY_BAG_H_ diff --git a/include/QxCommon/QxSimpleCrypt.h b/include/QxCommon/QxSimpleCrypt.h new file mode 100644 index 0000000..f4fc42b --- /dev/null +++ b/include/QxCommon/QxSimpleCrypt.h @@ -0,0 +1,250 @@ +/* +Copyright (c) 2011, Andre Somers +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 of the Rathenau Instituut, Andre Somers nor the + names of its contributors 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 ANDRE SOMERS 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 _QX_SIMPLECRYPT_H_ +#define _QX_SIMPLECRYPT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSimpleCrypt.h + * \author Andre Somers + * \ingroup QxCommon + * \brief qx::QxSimpleCrypt : simple encryption and decryption of strings and byte arrays + */ + +#include +#include +#include +#include + +namespace qx { + +/** + @ingroup QxCommon + @short Simple encryption and decryption of strings and byte arrays + + This class provides a simple implementation of encryption and decryption + of strings and byte arrays : http://qt-project.org/wiki/Simple_encryption + + @warning The encryption provided by this class is NOT strong encryption. It may + help to shield things from curious eyes, but it will NOT stand up to someone + determined to break the encryption. Don't say you were not warned. + + The class uses a 64 bit key. Simply create an instance of the class, set the key, + and use the encryptToString() method to calculate an encrypted version of the input string. + To decrypt that string again, use an instance of QxSimpleCrypt initialized with + the same key, and call the decryptToString() method with the encrypted string. If the key + matches, the decrypted version of the string will be returned again. + + If you do not provide a key, or if something else is wrong, the encryption and + decryption function will return an empty string or will return a string containing nonsense. + lastError() will return a value indicating if the method was succesful, and if not, why not. + + QxSimpleCrypt is prepared for the case that the encryption and decryption + algorithm is changed in a later version, by prepending a version identifier to the cypertext. + */ +class QX_DLL_EXPORT QxSimpleCrypt +{ + +public: + + /** + CompressionMode describes if compression will be applied to the data to be + encrypted. + */ + enum CompressionMode { + CompressionAuto, /*!< Only apply compression if that results in a shorter plaintext. */ + CompressionAlways, /*!< Always apply compression. Note that for short inputs, a compression may result in longer data */ + CompressionNever /*!< Never apply compression. */ + }; + + /** + IntegrityProtectionMode describes measures taken to make it possible to detect problems with the data + or wrong decryption keys. + + Measures involve adding a checksum or a cryptograhpic hash to the data to be encrypted. This + increases the length of the resulting cypertext, but makes it possible to check if the plaintext + appears to be valid after decryption. + */ + enum IntegrityProtectionMode { + ProtectionNone, /*!< The integerity of the encrypted data is not protected. It is not really possible to detect a wrong key, for instance. */ + ProtectionChecksum,/*!< A simple checksum is used to verify that the data is in order. If not, an empty string is returned. */ + ProtectionHash /*!< A cryptographic hash is used to verify the integrity of the data. This method produces a much stronger, but longer check */ + }; + + /** + Error describes the type of error that occured. + */ + enum Error { + ErrorNoError, /*!< No error occurred. */ + ErrorNoKeySet, /*!< No key was set. You can not encrypt or decrypt without a valid key. */ + ErrorUnknownVersion, /*!< The version of this data is unknown, or the data is otherwise not valid. */ + ErrorIntegrityFailed, /*!< The integrity check of the data failed. Perhaps the wrong key was used. */ + }; + + /** + Constructor. + + Constructs a QxSimpleCrypt instance without a valid key set on it. + */ + QxSimpleCrypt(); + /** + Constructor. + + Constructs a QxSimpleCrypt instance and initializes it with the given @arg key. + */ + explicit QxSimpleCrypt(quint64 key); + + /** + (Re-) initializes the key with the given @arg key. + */ + void setKey(quint64 key); + /** + Returns true if QxSimpleCrypt has been initialized with a key. + */ + bool hasKey() const {return !m_keyParts.isEmpty();} + + /** + Sets the compression mode to use when encrypting data. The default mode is Auto. + + Note that decryption is not influenced by this mode, as the decryption recognizes + what mode was used when encrypting. + */ + void setCompressionMode(CompressionMode mode) {m_compressionMode = mode;} + /** + Returns the CompressionMode that is currently in use. + */ + CompressionMode compressionMode() const {return m_compressionMode;} + + /** + Sets the integrity mode to use when encrypting data. The default mode is Checksum. + + Note that decryption is not influenced by this mode, as the decryption recognizes + what mode was used when encrypting. + */ + void setIntegrityProtectionMode(IntegrityProtectionMode mode) {m_protectionMode = mode;} + /** + Returns the IntegrityProtectionMode that is currently in use. + */ + IntegrityProtectionMode integrityProtectionMode() const {return m_protectionMode;} + + /** + Returns the last error that occurred. + */ + Error lastError() const {return m_lastError;} + + /** + Encrypts the @arg plaintext string with the key the class was initialized with, and returns + a cyphertext the result. The result is a base64 encoded version of the binary array that is the + actual result of the string, so it can be stored easily in a text format. + */ + QString encryptToString(const QString& plaintext) ; + /** + Encrypts the @arg plaintext QByteArray with the key the class was initialized with, and returns + a cyphertext the result. The result is a base64 encoded version of the binary array that is the + actual result of the encryption, so it can be stored easily in a text format. + */ + QString encryptToString(QByteArray plaintext) ; + /** + Encrypts the @arg plaintext string with the key the class was initialized with, and returns + a binary cyphertext in a QByteArray the result. + + This method returns a byte array, that is useable for storing a binary format. If you need + a string you can store in a text file, use encryptToString() instead. + */ + QByteArray encryptToByteArray(const QString& plaintext) ; + /** + Encrypts the @arg plaintext QByteArray with the key the class was initialized with, and returns + a binary cyphertext in a QByteArray the result. + + This method returns a byte array, that is useable for storing a binary format. If you need + a string you can store in a text file, use encryptToString() instead. + */ + QByteArray encryptToByteArray(QByteArray plaintext) ; + + /** + Decrypts a cyphertext string encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QString decryptToString(const QString& cyphertext) ; + /** + Decrypts a cyphertext string encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QByteArray decryptToByteArray(const QString& cyphertext) ; + /** + Decrypts a cyphertext binary encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QString decryptToString(QByteArray cypher) ; + /** + Decrypts a cyphertext binary encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QByteArray decryptToByteArray(QByteArray cypher) ; + + // enum to describe options that have been used for the encryption. Currently only one, but + // that only leaves room for future extensions like adding a cryptographic hash... + enum CryptoFlag{CryptoFlagNone = 0, + CryptoFlagCompression = 0x01, + CryptoFlagChecksum = 0x02, + CryptoFlagHash = 0x04 + }; + Q_DECLARE_FLAGS(CryptoFlags, CryptoFlag); + +private: + + void splitKey(); + + quint64 m_key; + QVector m_keyParts; + CompressionMode m_compressionMode; + IntegrityProtectionMode m_protectionMode; + Error m_lastError; + +}; + +} // namespace qx + +Q_DECLARE_OPERATORS_FOR_FLAGS(qx::QxSimpleCrypt::CryptoFlags) + +#endif // _QX_SIMPLECRYPT_H_ diff --git a/include/QxConvert/QxConvert.h b/include/QxConvert/QxConvert.h new file mode 100644 index 0000000..e8850c1 --- /dev/null +++ b/include/QxConvert/QxConvert.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_CONVERT_H_ +#define _QX_CONVERT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxConvert.h + * \author XDL Team + * \ingroup QxConvert + * \brief qx::cvt : namespace to provide global functions to convert any kind of objects to/from QString and QVariant format + */ + +#ifndef _QX_NO_JSON +#include +#include +#include +#include +#endif // _QX_NO_JSON + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToString; + template + struct QxConvert_FromString; + template + struct QxConvert_ToVariant; + template + struct QxConvert_FromVariant; + template + struct QxConvert_WithIndex_ToString; + template + struct QxConvert_WithIndex_FromString; + template + struct QxConvert_WithIndex_ToVariant; + template + struct QxConvert_WithIndex_FromVariant; + +#ifndef _QX_NO_JSON + template + struct QxConvert_ToJson; + template + struct QxConvert_FromJson; +#endif // _QX_NO_JSON + + } // namespace detail + + struct context + { + enum ctx_type + { + e_no_context, + e_database, + e_serialize_registered + }; + }; + + template + inline QString to_string(const T &t, const QString &format = QString(), int index = -1, qx::cvt::context::ctx_type ctx = qx::cvt::context::e_no_context) { return qx::cvt::detail::QxConvert_WithIndex_ToString::toString(t, format, index, ctx); } + template + inline qx_bool from_string(const QString &s, T &t, const QString &format = QString(), int index = -1, qx::cvt::context::ctx_type ctx = qx::cvt::context::e_no_context) { return qx::cvt::detail::QxConvert_WithIndex_FromString::fromString(t, s, format, index, ctx); } + template + inline QVariant to_variant(const T &t, const QString &format = QString(), int index = -1, qx::cvt::context::ctx_type ctx = qx::cvt::context::e_no_context) { return qx::cvt::detail::QxConvert_WithIndex_ToVariant::toVariant(t, format, index, ctx); } + template + inline qx_bool from_variant(const QVariant &v, T &t, const QString &format = QString(), int index = -1, qx::cvt::context::ctx_type ctx = qx::cvt::context::e_no_context) { return qx::cvt::detail::QxConvert_WithIndex_FromVariant::fromVariant(t, v, format, index, ctx); } + +#ifndef _QX_NO_JSON + template + inline QJsonValue to_json(const T &t, const QString &format = QString()) { return qx::cvt::detail::QxConvert_ToJson::toJson(t, format); } + template + inline qx_bool from_json(const QJsonValue &j, T &t, const QString &format = QString()) { return qx::cvt::detail::QxConvert_FromJson::fromJson(j, t, format); } +#endif // _QX_NO_JSON + + } // namespace cvt +} // namespace qx + +#endif // _QX_CONVERT_H_ diff --git a/include/QxConvert/QxConvert_Export.h b/include/QxConvert/QxConvert_Export.h new file mode 100644 index 0000000..301d7d1 --- /dev/null +++ b/include/QxConvert/QxConvert_Export.h @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_CONVERT_EXPORT_H_ +#define _QX_CONVERT_EXPORT_H_ +#if _QX_USE_QX_CONVERT_EXPORT + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include + +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, qx::trait::no_type) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QString) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QDate) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QTime) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QDateTime) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QByteArray) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QVariant) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, qx_bool) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, bool) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, char) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, short) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, int) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, long) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, long long) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, float) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, double) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, unsigned short) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, unsigned int) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, unsigned long) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, unsigned long long) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, std::string) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, std::wstring) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QBrush) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QColor) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QFont) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QObject) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QPoint) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QRect) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QRegion) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QSize) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QStringList) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QUrl) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QImage) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QPicture) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QPixmap) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QUuid) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, qx::QxDateNeutral) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, qx::QxTimeNeutral) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, qx::QxDateTimeNeutral) + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QRegExp) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToString, QMatrix) +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, qx::trait::no_type) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QString) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QDate) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QTime) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QDateTime) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QByteArray) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QVariant) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, qx_bool) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, bool) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, char) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, short) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, int) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, long) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, long long) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, float) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, double) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, unsigned short) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, unsigned int) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, unsigned long) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, unsigned long long) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, std::string) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, std::wstring) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QBrush) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QColor) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QFont) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QObject) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QPoint) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QRect) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QRegion) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QSize) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QStringList) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QUrl) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QImage) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QPicture) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QPixmap) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QUuid) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, qx::QxDateNeutral) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, qx::QxTimeNeutral) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, qx::QxDateTimeNeutral) + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QRegExp) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromString, QMatrix) +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, qx::trait::no_type) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QString) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QDate) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QTime) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QDateTime) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QByteArray) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QVariant) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, qx_bool) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, bool) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, char) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, short) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, int) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, long) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, long long) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, float) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, double) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, unsigned short) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, unsigned int) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, unsigned long) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, unsigned long long) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, std::string) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, std::wstring) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QBrush) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QColor) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QFont) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QObject) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QPoint) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QRect) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QRegion) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QSize) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QStringList) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QUrl) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QImage) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QPicture) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QPixmap) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QUuid) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, qx::QxDateNeutral) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, qx::QxTimeNeutral) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, qx::QxDateTimeNeutral) + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QRegExp) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_ToVariant, QMatrix) +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, qx::trait::no_type) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QString) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QDate) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QTime) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QDateTime) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QByteArray) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QVariant) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, qx_bool) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, bool) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, char) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, short) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, int) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, long) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, long long) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, float) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, double) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, unsigned short) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, unsigned int) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, unsigned long) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, unsigned long long) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, std::string) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, std::wstring) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QBrush) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QColor) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QFont) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QObject) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QPoint) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QRect) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QRegion) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QSize) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QStringList) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QUrl) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QImage) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QPicture) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QPixmap) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QUuid) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, qx::QxDateNeutral) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, qx::QxTimeNeutral) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, qx::QxDateTimeNeutral) + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QRegExp) +QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, qx::cvt::detail::QxConvert_FromVariant, QMatrix) +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +#endif // _QX_USE_QX_CONVERT_EXPORT +#endif // _QX_CONVERT_EXPORT_H_ diff --git a/include/QxConvert/QxConvert_Impl.h b/include/QxConvert/QxConvert_Impl.h new file mode 100644 index 0000000..c759d0b --- /dev/null +++ b/include/QxConvert/QxConvert_Impl.h @@ -0,0 +1,906 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_CONVERT_IMPL_H_ +#define _QX_CONVERT_IMPL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define QX_STR_CVT_QDATE_FORMAT "yyyyMMdd" +#define QX_STR_CVT_QTIME_FORMAT "hhmmsszzz" +#define QX_STR_CVT_QDATETIME_FORMAT "yyyyMMddhhmmsszzz" + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#if _QX_SERIALIZE_POLYMORPHIC +#define QX_CVT_DEFAULT_ARCHIVE qx::serialization::polymorphic_xml +#elif _QX_SERIALIZE_XML +#define QX_CVT_DEFAULT_ARCHIVE qx::serialization::xml +#elif _QX_SERIALIZE_TEXT +#define QX_CVT_DEFAULT_ARCHIVE qx::serialization::text +#elif _QX_SERIALIZE_BINARY +#define QX_CVT_DEFAULT_ARCHIVE qx::serialization::binary +#endif // _QX_SERIALIZE_XML +#else // _QX_ENABLE_BOOST_SERIALIZATION +#define QX_CVT_DEFAULT_ARCHIVE qx::serialization::qt +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#define QX_CVT_USING_ARCHIVE_IMPL(className) \ + namespace qx \ + { \ + namespace cvt \ + { \ + namespace detail \ + { \ + template <> \ + struct QxConvert_ToString \ + { \ + static inline QString toString(const className &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) \ + { \ + Q_UNUSED(format); \ + Q_UNUSED(index); \ + Q_UNUSED(ctx); \ + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); \ + } \ + }; \ + template <> \ + struct QxConvert_FromString \ + { \ + static inline qx_bool fromString(const QString &s, className &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) \ + { \ + Q_UNUSED(format); \ + Q_UNUSED(index); \ + Q_UNUSED(ctx); \ + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); \ + } \ + }; \ + template <> \ + struct QxConvert_ToVariant \ + { \ + static inline QVariant toVariant(const className &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) \ + { \ + Q_UNUSED(format); \ + Q_UNUSED(index); \ + Q_UNUSED(ctx); \ + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); \ + } \ + }; \ + template <> \ + struct QxConvert_FromVariant \ + { \ + static inline qx_bool fromVariant(const QVariant &v, className &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) \ + { \ + Q_UNUSED(format); \ + Q_UNUSED(index); \ + Q_UNUSED(ctx); \ + QString s = v.toString(); \ + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); \ + } \ + }; \ + } \ + } \ + } // namespace qx::cvt::detail + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)) +#define QX_JSON_DATE_TIME_FORMAT Qt::ISODateWithMs +#define QX_JSON_DATE_TIME_FORMAT_SIZE 23 // yyyy-MM-ddTHH:mm:ss.zzz +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)) +#define QX_JSON_DATE_TIME_FORMAT Qt::ISODate +#define QX_JSON_DATE_TIME_FORMAT_SIZE 19 // yyyy-MM-ddTHH:mm:ss +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)) + +namespace qx +{ + namespace cvt + { + namespace detail + { + namespace helper + { + + struct QxConvertHelper_Generic + { + }; + struct QxConvertHelper_Ptr + { + }; + struct QxConvertHelper_Registered + { + }; + struct QxConvertHelper_Persistable + { + }; + struct QxConvertHelper_Container + { + }; + struct QxConvertHelper_Enum + { + }; + + inline bool checkConvertQVariantToString(const QVariant &v) { return ((v.type() == QVariant::List) || (v.type() == QVariant::Map) || (v.type() == QVariant::Hash) || (v.type() == QVariant::StringList)); } + + } // namespace helper + + template + struct QxConvertHelper_ToString + { + static inline QString toString(const T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssertMsg(false, "qx::cvt::detail::QxConvertHelper_ToString", "template must be specialized"); + Q_UNUSED(t); + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QString(); + } + }; + + template + struct QxConvertHelper_FromString + { + static inline qx_bool fromString(const QString &s, T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssertMsg(false, "qx::cvt::detail::QxConvertHelper_FromString", "template must be specialized"); + Q_UNUSED(s); + Q_UNUSED(t); + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return qx_bool(); + } + }; + + template + struct QxConvertHelper_ToVariant + { + static inline QVariant toVariant(const T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssertMsg(false, "qx::cvt::detail::QxConvertHelper_ToVariant", "template must be specialized"); + Q_UNUSED(t); + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(); + } + }; + + template + struct QxConvertHelper_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssertMsg(false, "qx::cvt::detail::QxConvertHelper_FromVariant", "template must be specialized"); + Q_UNUSED(v); + Q_UNUSED(t); + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return qx_bool(); + } + }; + +#ifndef _QX_NO_JSON + + template + struct QxConvertHelper_ToJson + { + static inline QJsonValue toJson(const T &t, const QString &format) + { + qAssertMsg(false, "qx::cvt::detail::QxConvertHelper_ToJson", "template must be specialized"); + Q_UNUSED(t); + Q_UNUSED(format); + return QJsonValue(); + } + }; + + template + struct QxConvertHelper_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, T &t, const QString &format) + { + qAssertMsg(false, "qx::cvt::detail::QxConvertHelper_FromJson", "template must be specialized"); + Q_UNUSED(j); + Q_UNUSED(t); + Q_UNUSED(format); + return qx_bool(); + } + }; + +#endif // _QX_NO_JSON + + template + struct QxConvertHelper_ToString + { + + enum + { + qx_need_to_specialize_template_convert_to_string_from_string = 0 + }; + + static inline QString toString(const T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(t); + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + static_assert(qx_need_to_specialize_template_convert_to_string_from_string, "qx_need_to_specialize_template_convert_to_string_from_string"); // If a compilation error occurred here : you have to specialize template 'qx::cvt::detail::QxConvert_ToString< MyType >' + return QString(); + } + }; + + template + struct QxConvertHelper_FromString + { + + enum + { + qx_need_to_specialize_template_convert_to_string_from_string = 0 + }; + + static inline qx_bool fromString(const QString &s, T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(s); + Q_UNUSED(t); + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + static_assert(qx_need_to_specialize_template_convert_to_string_from_string, "qx_need_to_specialize_template_convert_to_string_from_string"); // If a compilation error occurred here : you have to specialize template 'qx::cvt::detail::QxConvert_FromString< MyType >' + return qx_bool(true); + } + }; + + template + struct QxConvertHelper_ToVariant + { + + static inline QVariant toVariant(const T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + return cvtQVariant::value, 0>::toVariant(t, format, index, ctx); + } + + private: + template + struct cvtQVariant + { + static inline QVariant toVariant(const T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) { return qx::cvt::to_string(t, format, index, ctx); }; + }; + + template + struct cvtQVariant + { + static inline QVariant toVariant(const T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(t); + }; + }; + }; + + template + struct QxConvertHelper_FromVariant + { + + static inline qx_bool fromVariant(const QVariant &v, T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + return cvtQVariant::value, 0>::fromVariant(v, t, format, index, ctx); + } + + private: + template + struct cvtQVariant + { + static inline qx_bool fromVariant(const QVariant &v, T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) { return qx::cvt::from_string(v.toString(), t, format, index, ctx); }; + }; + + template + struct cvtQVariant + { + static inline qx_bool fromVariant(const QVariant &v, T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = v.value(); + return qx_bool(true); + }; + }; + }; + +#ifndef _QX_NO_JSON + + template + struct QxConvertHelper_ToJson + { + static inline QJsonValue toJson(const T &t, const QString &format) + { + return QJsonValue::fromVariant(qx::cvt::detail::QxConvertHelper_ToVariant::toVariant(t, format, -1, qx::cvt::context::e_no_context)); + } + }; + + template + struct QxConvertHelper_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, T &t, const QString &format) + { + QVariant v = j.toVariant(); + return qx::cvt::detail::QxConvertHelper_FromVariant::fromVariant(v, t, format, -1, qx::cvt::context::e_no_context); + } + }; + +#endif // _QX_NO_JSON + + template + struct QxConvertHelper_ToString + { + + enum + { + qx_need_to_specialize_template_convert_to_string_from_string = 0 + }; + + static inline QString toString(const T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(t); + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); +#ifndef _QX_NO_JSON + return t.toJson(format); +#else // _QX_NO_JSON + static_assert(qx_need_to_specialize_template_convert_to_string_from_string, "qx_need_to_specialize_template_convert_to_string_from_string"); // If a compilation error occurred here : you have to specialize template 'qx::cvt::detail::QxConvert_ToString< MyType >' + return QString(); +#endif // _QX_NO_JSON + } + }; + + template + struct QxConvertHelper_FromString + { + + enum + { + qx_need_to_specialize_template_convert_to_string_from_string = 0 + }; + + static inline qx_bool fromString(const QString &s, T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(s); + Q_UNUSED(t); + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); +#ifndef _QX_NO_JSON + return t.fromJson(s, format); +#else // _QX_NO_JSON + static_assert(qx_need_to_specialize_template_convert_to_string_from_string, "qx_need_to_specialize_template_convert_to_string_from_string"); // If a compilation error occurred here : you have to specialize template 'qx::cvt::detail::QxConvert_FromString< MyType >' + return qx_bool(true); +#endif // _QX_NO_JSON + } + }; + + template + struct QxConvertHelper_ToVariant + { + + enum + { + qx_need_to_specialize_template_convert_to_variant_from_variant = 0 + }; + + static inline QVariant toVariant(const T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(t); + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); +#ifndef _QX_NO_JSON + return QVariant(t.toJson(format)); +#else // _QX_NO_JSON + static_assert(qx_need_to_specialize_template_convert_to_variant_from_variant, "qx_need_to_specialize_template_convert_to_variant_from_variant"); // If a compilation error occurred here : you have to specialize template 'qx::cvt::detail::QxConvert_ToVariant< MyType >' + return QVariant(); +#endif // _QX_NO_JSON + } + }; + + template + struct QxConvertHelper_FromVariant + { + + enum + { + qx_need_to_specialize_template_convert_to_variant_from_variant = 0 + }; + + static inline qx_bool fromVariant(const QVariant &v, T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(v); + Q_UNUSED(t); + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); +#ifndef _QX_NO_JSON + return t.fromJson(v.toString(), format); +#else // _QX_NO_JSON + static_assert(qx_need_to_specialize_template_convert_to_variant_from_variant, "qx_need_to_specialize_template_convert_to_variant_from_variant"); // If a compilation error occurred here : you have to specialize template 'qx::cvt::detail::QxConvert_FromVariant< MyType >' + return qx_bool(true); +#endif // _QX_NO_JSON + } + }; + +#ifndef _QX_NO_JSON + + template + struct QxConvertHelper_ToJson + { + static inline QJsonValue toJson(const T &t, const QString &format) + { + const qx::IxPersistable *p = static_cast(&t); + return qx::cvt::to_json((*p), format); + } + }; + + template + struct QxConvertHelper_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, T &t, const QString &format) + { + qx::IxPersistable *p = static_cast(&t); + return qx::cvt::from_json(j, (*p), format); + } + }; + +#endif // _QX_NO_JSON + + template + struct QxConvertHelper_ToString + { + static inline QString toString(const T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + return (t ? qx::cvt::to_string((*t), format, index, ctx) : ""); + } + }; + + template + struct QxConvertHelper_FromString + { + static inline qx_bool fromString(const QString &s, T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (!t) + { + qx::trait::construct_ptr::get(t); + }; + return (t ? qx::cvt::from_string(s, (*t), format, index, ctx) : qx_bool(false)); + } + }; + + template + struct QxConvertHelper_ToVariant + { + static inline QVariant toVariant(const T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + return (t ? qx::cvt::to_variant((*t), format, index, ctx) : QVariant()); + } + }; + + template + struct QxConvertHelper_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (!t && !v.isNull()) + { + qx::trait::construct_ptr::get(t); + } + else if (v.isNull()) + { + qx::trait::construct_ptr::get(t, true); + }; + return (t ? qx::cvt::from_variant(v, (*t), format, index, ctx) : qx_bool(false)); + } + }; + +#ifndef _QX_NO_JSON + + template + struct QxConvertHelper_ToJson + { + static inline QJsonValue toJson(const T &t, const QString &format) + { + return (t ? qx::cvt::to_json((*t), format) : QJsonValue()); + } + }; + + template + struct QxConvertHelper_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, T &t, const QString &format) + { + if (!t && !j.isNull()) + { + qx::trait::construct_ptr::get(t); + } + else if (j.isNull()) + { + qx::trait::construct_ptr::get(t, true); + }; + return (t ? qx::cvt::from_json(j, (*t), format) : qx_bool(false)); + } + }; + +#endif // _QX_NO_JSON + + template + struct QxConvertHelper_ToString + { + static inline QString toString(const T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (ctx != qx::cvt::context::e_serialize_registered) + { + qx::IxDataMember *pId = qx::QxClass::getSingleton()->getDataMemberX()->getId_WithDaoStrategy(); + return (pId ? pId->toVariant((&t), format, index, ctx).toString() : QString()); + } + return qx::serialization::qt::to_string(t); + } + }; + + template + struct QxConvertHelper_FromString + { + static inline qx_bool fromString(const QString &s, T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (ctx != qx::cvt::context::e_serialize_registered) + { + qx::IxDataMember *pId = qx::QxClass::getSingleton()->getDataMemberX()->getId_WithDaoStrategy(); + QVariant tmp(s); + return (pId ? pId->fromVariant((&t), tmp, format, index, ctx) : qx_bool(false)); + } + return qx::serialization::qt::from_string(t, s); + } + }; + + template + struct QxConvertHelper_ToVariant + { + static inline QVariant toVariant(const T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (ctx != qx::cvt::context::e_serialize_registered) + { + qx::IxDataMember *pId = qx::QxClass::getSingleton()->getDataMemberX()->getId_WithDaoStrategy(); + return (pId ? pId->toVariant((&t), format, index, ctx) : QVariant()); + } + return qx::serialization::qt::to_byte_array(t); + } + }; + + template + struct QxConvertHelper_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (ctx != qx::cvt::context::e_serialize_registered) + { + qx::IxDataMember *pId = qx::QxClass::getSingleton()->getDataMemberX()->getId_WithDaoStrategy(); + return (pId ? pId->fromVariant((&t), v, format, index, ctx) : qx_bool(false)); + } + return qx::serialization::qt::from_byte_array(t, v.toByteArray()); + } + }; + +#ifndef _QX_NO_JSON + + template + struct QxConvertHelper_ToJson + { + static inline QJsonValue toJson(const T &t, const QString &format) + { + return qx::cvt::detail::QxSerializeJsonRegistered::save(t, format); + } + }; + + template + struct QxConvertHelper_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, T &t, const QString &format) + { + return qx::cvt::detail::QxSerializeJsonRegistered::load(j, t, format); + } + }; + +#endif // _QX_NO_JSON + + template + struct QxConvertHelper_ToString + { + static inline QString toString(const T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvertHelper_FromString + { + static inline qx_bool fromString(const QString &s, T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvertHelper_ToVariant + { + static inline QVariant toVariant(const T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvertHelper_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, v.toString()); + } + }; + +#ifndef _QX_NO_JSON + + template + struct QxConvertHelper_ToJson + { + static inline QJsonValue toJson(const T &t, const QString &format) + { + return QJsonValue::fromVariant(qx::cvt::detail::QxConvertHelper_ToVariant::toVariant(t, format, -1, qx::cvt::context::e_no_context)); + } + }; + + template + struct QxConvertHelper_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, T &t, const QString &format) + { + QVariant v = j.toVariant(); + return qx::cvt::detail::QxConvertHelper_FromVariant::fromVariant(v, t, format, -1, qx::cvt::context::e_no_context); + } + }; + +#endif // _QX_NO_JSON + + template + struct QxConvertHelper_ToString + { + static inline QString toString(const T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QString::number(static_cast(t)); + } + }; + + template + struct QxConvertHelper_FromString + { + static inline qx_bool fromString(const QString &s, T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = static_cast(static_cast(s.toLongLong(&bOk))); + return bOk; + } + }; + + template + struct QxConvertHelper_ToVariant + { + static inline QVariant toVariant(const T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(static_cast(t)); + } + }; + + template + struct QxConvertHelper_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = static_cast(static_cast(v.toLongLong(&bOk))); + return bOk; + } + }; + +#ifndef _QX_NO_JSON + + template + struct QxConvertHelper_ToJson + { + static inline QJsonValue toJson(const T &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(static_cast(t)); + } + }; + + template + struct QxConvertHelper_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, T &t, const QString &format) + { + Q_UNUSED(format); + t = static_cast(static_cast(qRound(j.toDouble()))); + return qx_bool(true); + } + }; + +#endif // _QX_NO_JSON + + template + struct QxConvertHelper + { + + private: + typedef typename std::conditional::value, qx::cvt::detail::helper::QxConvertHelper_Persistable, qx::cvt::detail::helper::QxConvertHelper_Generic>::type type_str_cvt_helper_0; + typedef typename std::conditional::value, qx::cvt::detail::helper::QxConvertHelper_Ptr, type_str_cvt_helper_0>::type type_str_cvt_helper_1; + typedef typename std::conditional::value, qx::cvt::detail::helper::QxConvertHelper_Ptr, type_str_cvt_helper_1>::type type_str_cvt_helper_2; + typedef typename std::conditional::value, qx::cvt::detail::helper::QxConvertHelper_Container, type_str_cvt_helper_2>::type type_str_cvt_helper_3; + typedef typename std::conditional::value, qx::cvt::detail::helper::QxConvertHelper_Registered, type_str_cvt_helper_3>::type type_str_cvt_helper_4; + typedef typename std::conditional::value, qx::cvt::detail::helper::QxConvertHelper_Enum, type_str_cvt_helper_4>::type type_str_cvt_helper_5; + + public: + typedef typename QxConvertHelper::type_str_cvt_helper_5 type; + }; + + template + struct QxConvert_ToString + { + static inline QString toString(const T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + return qx::cvt::detail::QxConvertHelper_ToString::type>::toString(t, format, index, ctx); + } + }; + + template + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + return qx::cvt::detail::QxConvertHelper_FromString::type>::fromString(s, t, format, index, ctx); + } + }; + + template + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + return qx::cvt::detail::QxConvertHelper_ToVariant::type>::toVariant(t, format, index, ctx); + } + }; + + template + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, T &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + return qx::cvt::detail::QxConvertHelper_FromVariant::type>::fromVariant(v, t, format, index, ctx); + } + }; + +#ifndef _QX_NO_JSON + + template + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const T &t, const QString &format) + { + return qx::cvt::detail::QxConvertHelper_ToJson::type>::toJson(t, format); + } + }; + + template + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, T &t, const QString &format) + { + return qx::cvt::detail::QxConvertHelper_FromJson::type>::fromJson(j, t, format); + } + }; + +#endif // _QX_NO_JSON + + } // namespace detail + } // namespace cvt +} // namespace qx + +#include "../../inl/QxConvert/QxConvert_WithIndex.inl" +#include "../../inl/QxConvert/QxConvert_ToString.inl" +#include "../../inl/QxConvert/QxConvert_FromString.inl" +#include "../../inl/QxConvert/QxConvert_ToVariant.inl" +#include "../../inl/QxConvert/QxConvert_FromVariant.inl" +#include "../../inl/QxConvert/QxConvert_ToJson.inl" +#include "../../inl/QxConvert/QxConvert_FromJson.inl" +#include "../../inl/QxConvert/QxConvert_Qt.inl" + +#endif // _QX_CONVERT_IMPL_H_ diff --git a/include/QxDao/IxDao_Helper.h b/include/QxDao/IxDao_Helper.h new file mode 100644 index 0000000..034d7e0 --- /dev/null +++ b/include/QxDao/IxDao_Helper.h @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_DAO_HELPER_H_ +#define _IX_DAO_HELPER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxDao_Helper.h + * \author XDL Team + * \ingroup QxDao + * \brief Helper class to communicate with database + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +namespace qx +{ + template + QxInvalidValueX validate(T &t, const QString &group); + class QxSession; +} // namespace qx + +namespace qx +{ + namespace dao + { + namespace detail + { + + struct IxDao_Timer; + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::IxDao_Helper : helper class to communicate with database + */ + class QX_DLL_EXPORT IxDao_Helper + { + + friend struct IxDao_Timer; + + public: + enum timer_type + { + timer_none, + timer_total, + timer_db_exec, + timer_db_next, + timer_db_prepare, + timer_cpp_build_hierarchy, + timer_cpp_build_instance, + timer_cpp_read_instance, + timer_build_sql, + timer_db_open, + timer_db_transaction + }; + + private: + struct IxDao_HelperImpl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + + protected: + IxDao_Helper(qx::IxSqlQueryBuilder *pBuilder, const qx::QxSqlQuery *pQuery = NULL); + virtual ~IxDao_Helper(); + + public: + bool isValid() const; + bool hasFeature(QSqlDriver::DriverFeature ft) const; + + QSqlDatabase &database(); + const QSqlDatabase &database() const; + QSqlQuery &query(); + const QSqlQuery &query() const; + QSqlError &error(); + const QSqlError &error() const; + qx::QxSqlQuery &qxQuery(); + const qx::QxSqlQuery &qxQuery() const; + qx::IxSqlQueryBuilder &builder(); + const qx::IxSqlQueryBuilder &builder() const; + qx::IxDataMemberX *getDataMemberX() const; + long getDataCount() const; + qx::IxDataMember *getDataId() const; + qx::IxDataMember *nextData(long &l) const; + QString sql() const; + qx::QxSqlRelationLinked *getSqlRelationLinked() const; + qx::QxSession *getSession() const; + QString getIgnoreSoftDeleteHash() const; + bool getCartesianProduct() const; + QStringList getSqlColumns() const; + void setSqlColumns(const QStringList &lst); + bool getUseExecBatch() const; + void setUseExecBatch(bool b); + qx::QxCollection &getListExecBatch(); + IxSqlGenerator *getSqlGenerator() const; + void addInvalidValues(const qx::QxInvalidValueX &lst); + bool getAddAutoIncrementIdToUpdateQuery() const; + QStringList &itemsAsJson(); + bool isReadOnly() const; + bool isMongoDB() const; + bool isDistinct() const; + QVariant getIdFromQuery(int iNameIndex = -1) const; + + QSqlError errFailed(bool bPrepare = false); + QSqlError errEmpty(); + QSqlError errNoData(); + QSqlError errInvalidId(); + QSqlError errInvalidRelation(); + QSqlError errReadOnly(); + + bool transaction(); + bool nextRecord(); + void quiet(); + bool exec(bool bForceEmptyExec = false); + bool prepare(QString &sql); + + QSqlError updateError(const QSqlError &error); + bool updateSqlRelationX(const QStringList &relation); + void addQuery(bool bResolve); + void dumpRecord() const; + void resolveQuery(); + + template + inline bool isValidPrimaryKey(const U &u) + { + return (getDataId() && qx::trait::is_valid_primary_key(getDataId()->toVariant((&u), -1, qx::cvt::context::e_database))); + } + + template + inline void updateLastInsertId(U &u) + { + if (getDataId() && getDataId()->getAutoIncrement() && this->hasFeature(QSqlDriver::LastInsertId)) + { + getDataId()->fromVariant((&u), query().lastInsertId(), -1, qx::cvt::context::e_database); + } + } + + template + inline bool validateInstance(U &u) + { + qx::QxInvalidValueX invalidValues = qx::validate(u, ""); + this->addInvalidValues(invalidValues); + return (invalidValues.count() <= 0); + } + + protected: + void dumpBoundValues() const; + QSqlError updateError(const QString &sError); + void timerStart(IxDao_Helper::timer_type timer); + qint64 timerElapsed(IxDao_Helper::timer_type timer); + void init(QSqlDatabase *pDatabase, const QString &sContext); + void terminate(); + }; + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::IxDao_Timer : scoped timer to measure database elapsed times (using C++ RAII) + */ + struct IxDao_Timer + { + + IxDao_Helper *m_pDaoHelper; //!< Pointer to dao helper class + IxDao_Helper::timer_type m_eTimerType; //!< Timer type (database or C++ action) + + IxDao_Timer(IxDao_Helper *pDaoHelper, IxDao_Helper::timer_type timer) : m_pDaoHelper(pDaoHelper), m_eTimerType(timer) + { + if (m_pDaoHelper) + { + m_pDaoHelper->timerStart(m_eTimerType); + } + } + ~IxDao_Timer() + { + if (m_pDaoHelper) + { + m_pDaoHelper->timerElapsed(m_eTimerType); + } + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _IX_DAO_HELPER_H_ diff --git a/include/QxDao/IxPersistable.h b/include/QxDao/IxPersistable.h new file mode 100644 index 0000000..34edef4 --- /dev/null +++ b/include/QxDao/IxPersistable.h @@ -0,0 +1,713 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_PERSISTABLE_H_ +#define _IX_PERSISTABLE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxPersistable.h + * \author XDL Team + * \ingroup QxDao + * \brief Common interface (abstract class) for persistents classes using QX_PERSISTABLE_HPP() and QX_PERSISTABLE_CPP() macros + */ + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +namespace qx +{ + + class IxPersistableCollection; + + /*! + * \ingroup QxDao + * \brief qx::IxPersistable : common interface (abstract class) for persistents classes using QX_PERSISTABLE_HPP() and QX_PERSISTABLE_CPP() macros + * + * To use this common interface for persistents classes :
    + * 1- inherit your persistent class from qx::IxPersistable ;
    + * 2- into your class definition (myClass.h for example), add QX_PERSISTABLE_HPP(myClass) macro ;
    + * 3- into your class implementation (myClass.cpp for example), add QX_PERSISTABLE_CPP(myClass) macro. + * + * Note : for a list of objects (qxFetchAll() method or qxFetchByQuery() method), use qx::QxCollection> type. + * Or just use qx::QxPersistableCollectionHelper::type to retrieve your persistent collection type. + * A convenient way to fetch a list of objects is to use following static methods qxFetchAll() and qxFetchByQuery() : + * \code + std::shared_ptr lst = qx::IxPersistable::qxFetchAll("myClass"); + for (long l = 0; l < lst->_count(); l++) + { qx::IxPersistable_ptr ptr = lst->_get(l); etc... } + * \endcode + */ + class QX_DLL_EXPORT IxPersistable + { + + public: + IxPersistable(); + virtual ~IxPersistable(); + + /*! + * \brief Return the number of lines in the table (database) mapped to the current C++ class (registered into QxOrm context) and filtered by a user SQL query + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) + * + * qx::IxPersistable * p = ...;
    + * p->qxCount(...) execute following SQL query :
    + * SELECT COUNT(*) FROM my_table + XXX_JOINS_XXX + WHERE my_query... + */ + virtual long qxCount(const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL, const QStringList &relation = QStringList()) = 0; + + /*! + * \brief Return the number of lines in the table (database) mapped to the current C++ class (registered into QxOrm context) and filtered by a user SQL query + * \param lCount Output parameter with the number of lines in the table associated to the SQL query + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) + * + * qx::IxPersistable * p = ...;
    + * p->qxCount(...) execute following SQL query :
    + * SELECT COUNT(*) FROM my_table + XXX_JOINS_XXX + WHERE my_query... + */ + virtual QSqlError qxCount(long &lCount, const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL, const QStringList &relation = QStringList()) = 0; + + /*! + * \brief Fetch current instance (retrieve all its properties) mapped to a table in the database (current instance must have a valid id before to be fetched without error, or pass the id to the first parameter of this method) + * \param id Unique id to fetch properties from database (if empty, check id of current instance) + * \param columns List of database table columns (mapped to properties of C++ class) to fetch (if empty, all columns are fetched) + * \param relation List of relationships keys to fetch (eager fetch instead of default lazy fetch for a relation) : use "|" separator to put many relationships keys into this parameter + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::IxPersistable * p = ...;
    + * p->qxFetchById(...) execute following SQL query :
    + * SELECT * FROM my_table WHERE my_id = ? + */ + virtual QSqlError qxFetchById(const QVariant &id = QVariant(), const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) = 0; + + /*! + * \brief Fetch a list of objects (retrieve all elements and properties associated) of current type (container registered into QxOrm context) mapped to a table in the database + * \param list Container to fetch all items (retrieve all elements and properties associated); list is cleared before executing SQL query + * \param columns List of database table columns (mapped to properties of C++ class) to fetch (if empty, all columns are fetched) + * \param relation List of relationships keys to fetch (eager fetch instead of default lazy fetch for a relation) : use "|" separator to put many relationships keys into this parameter + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::IxPersistable * p = ...;
    + * std::shared_ptr lst = p->qxNewPersistableCollection();
    + * p->qxFetchAll(& lst, ...) execute following SQL query :
    + * SELECT * FROM my_table + */ + virtual QSqlError qxFetchAll(qx::IxPersistableCollection *list = NULL, const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) = 0; + + /*! + * \brief Fetch a list of objects (retrieve all elements and properties associated) of current type (container registered into QxOrm context) mapped to a table in the database and filtered by a user SQL query + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param list Container to fetch all items (retrieve all elements and properties associated); list is cleared before executing SQL query + * \param columns List of database table columns (mapped to properties of C++ class) to fetch (if empty, all columns are fetched) + * \param relation List of relationships keys to fetch (eager fetch instead of default lazy fetch for a relation) : use "|" separator to put many relationships keys into this parameter + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::IxPersistable * p = ...;
    + * std::shared_ptr lst = p->qxNewPersistableCollection();
    + * p->qxFetchByQuery(my_query, & lst, ...) execute following SQL query :
    + * SELECT * FROM my_table + WHERE my_query... + */ + virtual QSqlError qxFetchByQuery(const qx::QxSqlQuery &query, qx::IxPersistableCollection *list = NULL, const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) = 0; + + /*! + * \brief Insert current instance into database + * \param relation List of relationships keys to insert in others tables of database : use "|" separator to put many relationships keys into this parameter + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance inserting a list of instances to database (but doesn't fill the last inserted identifier in the C++ instances) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::IxPersistable * p = ...;
    + * p->qxInsert(...) execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + */ + virtual QSqlError qxInsert(const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) = 0; + + /*! + * \brief Update current instance into database (you can add a user SQL query to the default SQL query builded by QxOrm library) + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param columns List of database table columns (mapped to properties of C++ class) to update (if empty, all columns are updated) + * \param relation List of relationships keys to update in others tables of database : use "|" separator to put many relationships keys into this parameter + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance updating a list of instances in database + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::IxPersistable * p = ...;
    + * p->qxUpdate(...) execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + WHERE my_query... + */ + virtual QSqlError qxUpdate(const qx::QxSqlQuery &query = qx::QxSqlQuery(), const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) = 0; + + /*! + * \brief Insert (if no exist) or update (if already exist) current instance into database + * \param relation List of relationships keys to insert or update in others tables of database : use "|" separator to put many relationships keys into this parameter + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class + * \param eSaveRecursiveMode Parameter to call qx::dao::save_with_relation_recursive function + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::IxPersistable * p = ...;
    + * p->qxSave(...) execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + *
    or (if already exist into database) :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + */ + virtual QSqlError qxSave(const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, qx::dao::save_mode::e_save_mode eSaveRecursiveMode = qx::dao::save_mode::e_none) = 0; + + /*! + * \brief Delete current instance from database + * \param id Unique id to delete from database (if empty, check id of current instance) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance deleting a list of instances in database + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::IxPersistable * p = ...;
    + * p->qxDeleteById(...) execute following SQL query :
    + * DELETE FROM my_table WHERE my_id = ?
    + *
    + * If a soft delete behavior is defined for current class, p->qxDeleteById(...) execute following SQL query :
    + * UPDATE my_table SET is_deleted='1' WHERE my_id = ? + */ + virtual QSqlError qxDeleteById(const QVariant &id = QVariant(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) = 0; + + /*! + * \brief Delete all lines of a table (database) mapped to current C++ class (registered into QxOrm context) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::IxPersistable * p = ...;
    + * p->qxDeleteAll(...) execute following SQL query :
    + * DELETE FROM my_table
    + *
    + * If a soft delete behavior is defined for current class, p->qxDeleteAll(...) execute following SQL query :
    + * UPDATE my_table SET is_deleted='1' + */ + virtual QSqlError qxDeleteAll(QSqlDatabase *pDatabase = NULL) = 0; + + /*! + * \brief Delete all lines of a table (database) mapped to current C++ class (registered into QxOrm context) and filtered by a user SQL query + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::IxPersistable * p = ...;
    + * p->qxDeleteByQuery(...) execute following SQL query :
    + * DELETE FROM my_table + WHERE my_query...
    + *
    + * If a soft delete behavior is defined for current class, p->qxDeleteByQuery(...) execute following SQL query :
    + * UPDATE my_table SET is_deleted='1' + WHERE my_query... + */ + virtual QSqlError qxDeleteByQuery(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) = 0; + + /*! + * \brief Delete current instance from database + * \param id Unique id to delete from database (if empty, check id of current instance) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance destroying a list of instances in database + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::IxPersistable * p = ...;
    + * p->qxDestroyById(...) execute following SQL query :
    + * DELETE FROM my_table WHERE my_id = ?
    + */ + virtual QSqlError qxDestroyById(const QVariant &id = QVariant(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) = 0; + + /*! + * \brief Delete all lines of a table (database) mapped to current C++ class (registered into QxOrm context) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::IxPersistable * p = ...;
    + * p->qxDestroyAll(...) execute following SQL query :
    + * DELETE FROM my_table
    + */ + virtual QSqlError qxDestroyAll(QSqlDatabase *pDatabase = NULL) = 0; + + /*! + * \brief Delete all lines of a table (database) mapped to current C++ class (registered into QxOrm context) and filtered by a user SQL query + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::IxPersistable * p = ...;
    + * p->qxDestroyByQuery(...) execute following SQL query :
    + * DELETE FROM my_table + WHERE my_query...
    + */ + virtual QSqlError qxDestroyByQuery(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) = 0; + + /*! + * \brief Execute a custom SQL query or a stored procedure, all columns that can be mapped to the instance of type T will be fetched automatically + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxExecuteQuery(qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) = 0; + + /*! + * \brief Execute a custom SQL query or a stored procedure, all columns that can be mapped to the instance of type T will be fetched automatically + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param list Container to fetch all items (retrieve all elements and properties associated); list is cleared before executing SQL query + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxExecuteQuery(qx::QxSqlQuery &query, qx::IxPersistableCollection *list = NULL, QSqlDatabase *pDatabase = NULL) = 0; + + /*! + * \brief Search if current instance already exists into database + * \param id Unique id to check into database (if empty, check id of current instance) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class + * \return Return true if element already exists into database; otherwise return false; if an error occurred, qx_bool object contains a description of database error executing SQL query + * + * qx::IxPersistable * p = ...;
    + * p->qxExist(...) execute following SQL query :
    + * SELECT * FROM my_table WHERE my_id = ? + */ + virtual qx_bool qxExist(const QVariant &id = QVariant(), QSqlDatabase *pDatabase = NULL) = 0; + + /*! + * \brief Check if current instance is valid or not + * \param groups List of groups to check (defined into QxValidator module for current class) + * \return Return list of invalid values (if empty, current instance is valid) + * + * For more details about QxValidator module : https://www.qxorm.com/qxorm_en/faq.html#faq_250 + */ + virtual qx::QxInvalidValueX qxValidate(const QStringList &groups = QStringList()) = 0; + + /*! + * \brief Create a new collection smart-pointer to fetch a list of items of current class type + * \param bAsList If true, returns a list (array) of persistent instances instead of key/value hash-map + * \return Return a new collection smart-pointer to fetch a list of items of current class type + */ + virtual std::shared_ptr qxNewPersistableCollection(bool bAsList = false) const = 0; + + /*! + * \brief Access to introspection engine (or reflection engine) of QxOrm library + * \return Return a qx::IxClass pointer to access to introspection engine (or reflection engine) of QxOrm library, and get some informations about current class (properties, methods, etc...) + */ + virtual qx::IxClass *qxClass() const = 0; + +#ifndef _QX_NO_JSON + virtual QString toJson(const QString &format = QString()) const = 0; + virtual QJsonValue toJson_(const QString &format = QString()) const = 0; + virtual qx_bool fromJson(const QString &json, const QString &format = QString()) = 0; + virtual qx_bool fromJson_(const QJsonValue &json, const QString &format = QString()) = 0; +#endif // _QX_NO_JSON + + public: + static std::shared_ptr qxFetchAll(const QString &className, const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, bool bAsList = false); + static std::shared_ptr qxFetchByQuery(const QString &className, const qx::QxSqlQuery &query, const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, bool bAsList = false); + static std::shared_ptr qxExecuteQuery(const QString &className, qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL, bool bAsList = false); + }; + + typedef std::shared_ptr IxPersistable_ptr; + +} // namespace qx + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_ix_persistable::value : return true if T implements qx::IxPersistable interface, otherwise return false + */ + template + struct is_ix_persistable + { + enum + { + value = std::is_base_of::value + }; + }; + + } // namespace trait +} // namespace qx + +#ifndef _QX_NO_JSON +#define QX_PERSISTABLE_JSON_HPP(className) \ + virtual QString toJson(const QString &format = QString()) const; \ + virtual QJsonValue toJson_(const QString &format = QString()) const; \ + virtual qx_bool fromJson(const QString &json, const QString &format = QString()); \ + virtual qx_bool fromJson_(const QJsonValue &json, const QString &format = QString()); +#else // _QX_NO_JSON +#define QX_PERSISTABLE_JSON_HPP(className) /* Nothing */ +#endif // _QX_NO_JSON + +#define QX_PERSISTABLE_HPP(className) \ +public: \ + virtual long qxCount(const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL, const QStringList &relation = QStringList()); \ + virtual QSqlError qxCount(long &lCount, const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL, const QStringList &relation = QStringList()); \ + virtual QSqlError qxFetchById(const QVariant &id = QVariant(), const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL); \ + virtual QSqlError qxFetchAll(qx::IxPersistableCollection *list = NULL, const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL); \ + virtual QSqlError qxFetchByQuery(const qx::QxSqlQuery &query, qx::IxPersistableCollection *list = NULL, const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL); \ + virtual QSqlError qxInsert(const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false); \ + virtual QSqlError qxUpdate(const qx::QxSqlQuery &query = qx::QxSqlQuery(), const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false); \ + virtual QSqlError qxSave(const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, qx::dao::save_mode::e_save_mode eSaveRecursiveMode = qx::dao::save_mode::e_none); \ + virtual QSqlError qxDeleteById(const QVariant &id = QVariant(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false); \ + virtual QSqlError qxDeleteAll(QSqlDatabase *pDatabase = NULL); \ + virtual QSqlError qxDeleteByQuery(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL); \ + virtual QSqlError qxDestroyById(const QVariant &id = QVariant(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false); \ + virtual QSqlError qxDestroyAll(QSqlDatabase *pDatabase = NULL); \ + virtual QSqlError qxDestroyByQuery(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL); \ + virtual QSqlError qxExecuteQuery(qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL); \ + virtual QSqlError qxExecuteQuery(qx::QxSqlQuery &query, qx::IxPersistableCollection *list = NULL, QSqlDatabase *pDatabase = NULL); \ + virtual qx_bool qxExist(const QVariant &id = QVariant(), QSqlDatabase *pDatabase = NULL); \ + virtual qx::QxInvalidValueX qxValidate(const QStringList &groups = QStringList()); \ + virtual std::shared_ptr qxNewPersistableCollection(bool bAsList = false) const; \ + virtual qx::IxClass *qxClass() const; \ + QX_PERSISTABLE_JSON_HPP(className) + +#ifdef _QX_NO_RTTI +#define QX_PERSISTABLE_CAST_COLLECTION(className) \ + if (!list) \ + { \ + return QSqlError(); \ + } \ + qx::QxPersistableCollectionHelper::type_coll *list_typed = static_cast::type *>(list); +#else // _QX_NO_RTTI +#define QX_PERSISTABLE_CAST_COLLECTION(className) \ + if (!list) \ + { \ + return QSqlError(); \ + } \ + qx::QxPersistableCollectionHelper::type_coll *list_typed = dynamic_cast::type *>(list); +#endif // _QX_NO_RTTI + +#ifndef _QX_NO_JSON +#define QX_PERSISTABLE_JSON_CPP(className) \ + QString className::toJson(const QString &format) const { return qx::serialization::json::to_string((*this), 1, format); } \ + QJsonValue className::toJson_(const QString &format) const { return qx::cvt::to_json((*this), format); } \ + qx_bool className::fromJson(const QString &json, const QString &format) { return qx::serialization::json::from_string((*this), json, 1, format); } \ + qx_bool className::fromJson_(const QJsonValue &json, const QString &format) { return qx::cvt::from_json(json, (*this), format); } +#else // _QX_NO_JSON +#define QX_PERSISTABLE_JSON_CPP(className) /* Nothing */ +#endif // _QX_NO_JSON + +#define QX_PERSISTABLE_CPP(className) \ + \ + long className::qxCount(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase, const QStringList &relation) \ + { \ + if (relation.count() == 0) \ + { \ + return qx::dao::count(query, pDatabase); \ + } \ + else \ + { \ + long lCount(0); \ + qx::dao::count_with_relation(lCount, relation, query, pDatabase); \ + return lCount; \ + } \ + } \ + \ + QSqlError className::qxCount(long &lCount, const qx::QxSqlQuery &query, QSqlDatabase *pDatabase, const QStringList &relation) \ + { \ + if (relation.count() == 0) \ + { \ + return qx::dao::count(lCount, query, pDatabase); \ + } \ + else \ + { \ + return qx::dao::count_with_relation(lCount, relation, query, pDatabase); \ + } \ + } \ + \ + QSqlError className::qxFetchById(const QVariant &id, const QStringList &columns, const QStringList &relation, QSqlDatabase *pDatabase) \ + { \ + if (id.isValid()) \ + { \ + qx::IxDataMemberX *pDataMemberX = qx::QxClass::getSingleton()->getDataMemberX(); \ + qx::IxDataMember *pDataMemberId = (pDataMemberX ? pDataMemberX->getId_WithDaoStrategy() : NULL); \ + if (!pDataMemberId) \ + { \ + qDebug("[QxOrm] problem with 'qxFetchById()' method : '%s'", "data member id not registered"); \ + qAssert(false); \ + } \ + if (!pDataMemberId) \ + { \ + return QSqlError("[QxOrm] problem with 'qxFetchById()' method : 'data member id not registered'", "", QSqlError::UnknownError); \ + } \ + pDataMemberId->fromVariant(this, id, -1, qx::cvt::context::e_database); \ + } \ + QSqlError err; \ + if (relation.count() == 0) \ + { \ + err = qx::dao::fetch_by_id((*this), pDatabase, columns); \ + } \ + else \ + { \ + err = qx::dao::fetch_by_id_with_relation(relation, (*this), pDatabase); \ + } \ + return err; \ + } \ + \ + QSqlError className::qxFetchAll(qx::IxPersistableCollection *list, const QStringList &columns, const QStringList &relation, QSqlDatabase *pDatabase) \ + { \ + QX_PERSISTABLE_CAST_COLLECTION(className) \ + if (!list_typed) \ + { \ + qDebug("[QxOrm] problem with 'qxFetchAll()' method : '%s'", "dynamic_cast failed using collection qx::QxCollection< type_primary_key, std::shared_ptr >"); \ + qAssert(false); \ + } \ + if (!list_typed) \ + { \ + return QSqlError("[QxOrm] problem with 'qxFetchAll()' method : 'dynamic_cast failed using collection qx::QxCollection< type_primary_key, std::shared_ptr >'", "", QSqlError::UnknownError); \ + } \ + QSqlError err; \ + if (relation.count() == 0) \ + { \ + err = qx::dao::fetch_all((*list_typed), pDatabase, columns); \ + } \ + else \ + { \ + err = qx::dao::fetch_all_with_relation(relation, (*list_typed), pDatabase); \ + } \ + return err; \ + } \ + \ + QSqlError className::qxFetchByQuery(const qx::QxSqlQuery &query, qx::IxPersistableCollection *list, const QStringList &columns, const QStringList &relation, QSqlDatabase *pDatabase) \ + { \ + QX_PERSISTABLE_CAST_COLLECTION(className) \ + if (!list_typed) \ + { \ + qDebug("[QxOrm] problem with 'qxFetchByQuery()' method : '%s'", "dynamic_cast failed using collection qx::QxCollection< type_primary_key, std::shared_ptr >"); \ + qAssert(false); \ + } \ + if (!list_typed) \ + { \ + return QSqlError("[QxOrm] problem with 'qxFetchByQuery()' method : 'dynamic_cast failed using collection qx::QxCollection< type_primary_key, std::shared_ptr >'", "", QSqlError::UnknownError); \ + } \ + QSqlError err; \ + if (relation.count() == 0) \ + { \ + err = qx::dao::fetch_by_query(query, (*list_typed), pDatabase, columns); \ + } \ + else \ + { \ + err = qx::dao::fetch_by_query_with_relation(relation, query, (*list_typed), pDatabase); \ + } \ + return err; \ + } \ + \ + QSqlError className::qxInsert(const QStringList &relation, QSqlDatabase *pDatabase, bool bUseExecBatch) \ + { \ + QSqlError err; \ + if (relation.count() == 0) \ + { \ + err = qx::dao::insert((*this), pDatabase, bUseExecBatch); \ + } \ + else \ + { \ + err = qx::dao::insert_with_relation(relation, (*this), pDatabase); \ + } \ + return err; \ + } \ + \ + QSqlError className::qxUpdate(const qx::QxSqlQuery &query, const QStringList &columns, const QStringList &relation, QSqlDatabase *pDatabase, bool bUseExecBatch) \ + { \ + QSqlError err; \ + if (relation.count() == 0) \ + { \ + err = qx::dao::update_by_query(query, (*this), pDatabase, columns, bUseExecBatch); \ + } \ + else \ + { \ + err = qx::dao::update_by_query_with_relation(relation, query, (*this), pDatabase); \ + } \ + return err; \ + } \ + \ + QSqlError className::qxSave(const QStringList &relation, QSqlDatabase *pDatabase, qx::dao::save_mode::e_save_mode eSaveRecursiveMode) \ + { \ + QSqlError err; \ + if (eSaveRecursiveMode != qx::dao::save_mode::e_none) \ + { \ + err = qx::dao::save_with_relation_recursive((*this), eSaveRecursiveMode, pDatabase); \ + } \ + else if (relation.count() == 0) \ + { \ + err = qx::dao::save((*this), pDatabase); \ + } \ + else \ + { \ + err = qx::dao::save_with_relation(relation, (*this), pDatabase); \ + } \ + return err; \ + } \ + \ + QSqlError className::qxDeleteById(const QVariant &id, QSqlDatabase *pDatabase, bool bUseExecBatch) \ + { \ + if (id.isValid()) \ + { \ + qx::IxDataMemberX *pDataMemberX = qx::QxClass::getSingleton()->getDataMemberX(); \ + qx::IxDataMember *pDataMemberId = (pDataMemberX ? pDataMemberX->getId_WithDaoStrategy() : NULL); \ + if (!pDataMemberId) \ + { \ + qDebug("[QxOrm] problem with 'qxDeleteById()' method : '%s'", "data member id not registered"); \ + qAssert(false); \ + } \ + if (!pDataMemberId) \ + { \ + return QSqlError("[QxOrm] problem with 'qxDeleteById()' method : 'data member id not registered'", "", QSqlError::UnknownError); \ + } \ + pDataMemberId->fromVariant(this, id, -1, qx::cvt::context::e_database); \ + } \ + return qx::dao::delete_by_id((*this), pDatabase, bUseExecBatch); \ + } \ + \ + QSqlError className::qxDeleteAll(QSqlDatabase *pDatabase) \ + { \ + return qx::dao::delete_all(pDatabase); \ + } \ + \ + QSqlError className::qxDeleteByQuery(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase) \ + { \ + return qx::dao::delete_by_query(query, pDatabase); \ + } \ + \ + QSqlError className::qxDestroyById(const QVariant &id, QSqlDatabase *pDatabase, bool bUseExecBatch) \ + { \ + if (id.isValid()) \ + { \ + qx::IxDataMemberX *pDataMemberX = qx::QxClass::getSingleton()->getDataMemberX(); \ + qx::IxDataMember *pDataMemberId = (pDataMemberX ? pDataMemberX->getId_WithDaoStrategy() : NULL); \ + if (!pDataMemberId) \ + { \ + qDebug("[QxOrm] problem with 'qxDestroyById()' method : '%s'", "data member id not registered"); \ + qAssert(false); \ + } \ + if (!pDataMemberId) \ + { \ + return QSqlError("[QxOrm] problem with 'qxDestroyById()' method : 'data member id not registered'", "", QSqlError::UnknownError); \ + } \ + pDataMemberId->fromVariant(this, id, -1, qx::cvt::context::e_database); \ + } \ + return qx::dao::destroy_by_id((*this), pDatabase, bUseExecBatch); \ + } \ + \ + QSqlError className::qxDestroyAll(QSqlDatabase *pDatabase) \ + { \ + return qx::dao::destroy_all(pDatabase); \ + } \ + \ + QSqlError className::qxDestroyByQuery(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase) \ + { \ + return qx::dao::destroy_by_query(query, pDatabase); \ + } \ + \ + QSqlError className::qxExecuteQuery(qx::QxSqlQuery &query, QSqlDatabase *pDatabase) \ + { \ + return qx::dao::execute_query(query, (*this), pDatabase); \ + } \ + \ + QSqlError className::qxExecuteQuery(qx::QxSqlQuery &query, qx::IxPersistableCollection *list, QSqlDatabase *pDatabase) \ + { \ + QX_PERSISTABLE_CAST_COLLECTION(className) \ + if (!list_typed) \ + { \ + qDebug("[QxOrm] problem with 'qxExecuteQuery()' method : '%s'", "dynamic_cast failed using collection qx::QxCollection< type_primary_key, std::shared_ptr >"); \ + qAssert(false); \ + } \ + if (!list_typed) \ + { \ + return QSqlError("[QxOrm] problem with 'qxExecuteQuery()' method : 'dynamic_cast failed using collection qx::QxCollection< type_primary_key, std::shared_ptr >'", "", QSqlError::UnknownError); \ + } \ + return qx::dao::execute_query(query, (*list_typed), pDatabase); \ + } \ + \ + qx_bool className::qxExist(const QVariant &id, QSqlDatabase *pDatabase) \ + { \ + if (id.isValid()) \ + { \ + qx::IxDataMemberX *pDataMemberX = qx::QxClass::getSingleton()->getDataMemberX(); \ + qx::IxDataMember *pDataMemberId = (pDataMemberX ? pDataMemberX->getId_WithDaoStrategy() : NULL); \ + if (!pDataMemberId) \ + { \ + qDebug("[QxOrm] problem with 'qxExist()' method : '%s'", "data member id not registered"); \ + qAssert(false); \ + } \ + if (!pDataMemberId) \ + { \ + return qx_bool(false); \ + } \ + pDataMemberId->fromVariant(this, id, -1, qx::cvt::context::e_database); \ + } \ + return qx::dao::exist((*this), pDatabase); \ + } \ + \ + qx::QxInvalidValueX className::qxValidate(const QStringList &groups) \ + { \ + return qx::validate((*this), groups); \ + } \ + \ + std::shared_ptr className::qxNewPersistableCollection(bool bAsList) const \ + { \ + if (bAsList) \ + { \ + std::shared_ptr coll = std::make_shared>(); \ + return coll; \ + } \ + else \ + { \ + std::shared_ptr coll = std::make_shared::type>(); \ + return coll; \ + } \ + } \ + \ + qx::IxClass *className::qxClass() const \ + { \ + return qx::QxClass::getSingleton(); \ + } \ + \ + QX_PERSISTABLE_JSON_CPP(className) + +QX_REGISTER_CLASS_NAME(qx::IxPersistable) + +#include + +#endif // _IX_PERSISTABLE_H_ diff --git a/include/QxDao/IxPersistableCollection.h b/include/QxDao/IxPersistableCollection.h new file mode 100644 index 0000000..6a31a17 --- /dev/null +++ b/include/QxDao/IxPersistableCollection.h @@ -0,0 +1,364 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_PERSISTABLE_COLLECTION_H_ +#define _IX_PERSISTABLE_COLLECTION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxPersistableCollection.h + * \author XDL Team + * \ingroup QxDao + * \brief Common interface (abstract class) for collection persistent classes based on qx::IxPersistable and qx::IxCollection + */ + +#include + +#include + +#include +#include + +#ifndef _QX_NO_JSON +#include +#endif // _QX_NO_JSON + +namespace qx +{ + + /*! + * \ingroup QxDao + * \brief qx::IxPersistableCollection : common interface (abstract class) for collection persistent classes based on qx::IxPersistable and qx::IxCollection + */ + class QX_DLL_EXPORT IxPersistableCollection : public qx::IxPersistable + { + + public: + IxPersistableCollection(); + virtual ~IxPersistableCollection(); + + virtual long __count() const = 0; + virtual void __clear() = 0; + virtual bool __remove(long idx) = 0; + virtual qx::IxPersistable_ptr __at(long idx) const = 0; + }; + + typedef std::shared_ptr IxPersistableCollection_ptr; + + /*! + * \ingroup QxDao + * \brief qx::QxPersistableCollection : concrete class for collection persistent classes based on qx::IxPersistableCollection and qx::QxCollection + */ + template + class QxPersistableCollection : public qx::IxPersistableCollection, public qx::QxCollection + { + + enum + { + qx_is_valid = qx::trait::is_qx_registered::value + }; + + public: + QxPersistableCollection() : qx::IxPersistableCollection(), qx::QxCollection() { static_assert(qx_is_valid, "qx_is_valid"); } + virtual ~QxPersistableCollection() { ; } + + virtual long __count() const + { + const qx::QxCollection *coll = this; + return coll->count(); + } + virtual void __clear() + { + qx::QxCollection *coll = this; + coll->clear(); + } + virtual bool __remove(long idx) + { + qx::QxCollection *coll = this; + return coll->removeByIndex(idx); + } + virtual qx::IxPersistable_ptr __at(long idx) const + { + const qx::QxCollection *coll = this; + Value val = coll->getByIndex(idx); + return std::static_pointer_cast(val); + } + + virtual long qxCount(const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL, const QStringList &relation = QStringList()) + { + if (relation.count() == 0) + { + return qx::dao::count(query, pDatabase); + } + else + { + long lCount(0); + qx::dao::count_with_relation(lCount, relation, query, pDatabase); + return lCount; + } + } + + virtual QSqlError qxCount(long &lCount, const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL, const QStringList &relation = QStringList()) + { + if (relation.count() == 0) + { + return qx::dao::count(lCount, query, pDatabase); + } + else + { + return qx::dao::count_with_relation(lCount, relation, query, pDatabase); + } + } + + virtual QSqlError qxFetchById(const QVariant &id = QVariant(), const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(id); + QSqlError err; + qx::QxCollection *coll = this; + if (relation.count() == 0) + { + err = qx::dao::fetch_by_id((*coll), pDatabase, columns); + } + else + { + err = qx::dao::fetch_by_id_with_relation(relation, (*coll), pDatabase); + } + return err; + } + + virtual QSqlError qxFetchAll(qx::IxPersistableCollection *list = NULL, const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(list); + QSqlError err; + qx::QxCollection *coll = this; + if (relation.count() == 0) + { + err = qx::dao::fetch_all((*coll), pDatabase, columns); + } + else + { + err = qx::dao::fetch_all_with_relation(relation, (*coll), pDatabase); + } + return err; + } + + virtual QSqlError qxFetchByQuery(const qx::QxSqlQuery &query, qx::IxPersistableCollection *list = NULL, const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(list); + QSqlError err; + qx::QxCollection *coll = this; + if (relation.count() == 0) + { + err = qx::dao::fetch_by_query(query, (*coll), pDatabase, columns); + } + else + { + err = qx::dao::fetch_by_query_with_relation(relation, query, (*coll), pDatabase); + } + return err; + } + + virtual QSqlError qxInsert(const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + QSqlError err; + qx::QxCollection *coll = this; + if (relation.count() == 0) + { + err = qx::dao::insert((*coll), pDatabase, bUseExecBatch); + } + else + { + err = qx::dao::insert_with_relation(relation, (*coll), pDatabase); + } + return err; + } + + virtual QSqlError qxUpdate(const qx::QxSqlQuery &query = qx::QxSqlQuery(), const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + QSqlError err; + qx::QxCollection *coll = this; + if (relation.count() == 0) + { + err = qx::dao::update_by_query(query, (*coll), pDatabase, columns, bUseExecBatch); + } + else + { + err = qx::dao::update_by_query_with_relation(relation, query, (*coll), pDatabase); + } + return err; + } + + virtual QSqlError qxSave(const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, qx::dao::save_mode::e_save_mode eSaveRecursiveMode = qx::dao::save_mode::e_none) + { + QSqlError err; + qx::QxCollection *coll = this; + if (eSaveRecursiveMode != qx::dao::save_mode::e_none) + { + err = qx::dao::save_with_relation_recursive((*coll), eSaveRecursiveMode, pDatabase); + } + else if (relation.count() == 0) + { + err = qx::dao::save((*coll), pDatabase); + } + else + { + err = qx::dao::save_with_relation(relation, (*coll), pDatabase); + } + return err; + } + + virtual QSqlError qxDeleteById(const QVariant &id = QVariant(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + Q_UNUSED(id); + qx::QxCollection *coll = this; + return qx::dao::delete_by_id((*coll), pDatabase, bUseExecBatch); + } + + virtual QSqlError qxDeleteAll(QSqlDatabase *pDatabase = NULL) + { + return qx::dao::delete_all(pDatabase); + } + + virtual QSqlError qxDeleteByQuery(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::delete_by_query(query, pDatabase); + } + + virtual QSqlError qxDestroyById(const QVariant &id = QVariant(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + Q_UNUSED(id); + qx::QxCollection *coll = this; + return qx::dao::destroy_by_id((*coll), pDatabase, bUseExecBatch); + } + + virtual QSqlError qxDestroyAll(QSqlDatabase *pDatabase = NULL) + { + return qx::dao::destroy_all(pDatabase); + } + + virtual QSqlError qxDestroyByQuery(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::destroy_by_query(query, pDatabase); + } + + virtual QSqlError qxExecuteQuery(qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) + { + qx::QxCollection *coll = this; + return qx::dao::execute_query(query, (*coll), pDatabase); + } + + virtual QSqlError qxExecuteQuery(qx::QxSqlQuery &query, qx::IxPersistableCollection *list = NULL, QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(list); + return qxExecuteQuery(query, pDatabase); + } + + virtual qx_bool qxExist(const QVariant &id = QVariant(), QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(id); + qx::QxCollection *coll = this; + return qx::dao::exist((*coll), pDatabase); + } + + virtual qx::QxInvalidValueX qxValidate(const QStringList &groups = QStringList()) + { + qx::QxCollection *coll = this; + return qx::validate((*coll), groups); + } + + virtual std::shared_ptr qxNewPersistableCollection(bool bAsList = false) const + { + Q_UNUSED(bAsList); + std::shared_ptr coll = std::make_shared>(); + return coll; + } + + virtual qx::IxClass *qxClass() const + { + return qx::QxClass::getSingleton(); + } + +#ifndef _QX_NO_JSON + + virtual QString toJson(const QString &format = QString()) const + { + const qx::QxCollection *coll = this; + return qx::serialization::json::to_string((*coll), 1, format); + } + + virtual QJsonValue toJson_(const QString &format = QString()) const + { + const qx::QxCollection *coll = this; + return qx::cvt::to_json((*coll), format); + } + + virtual qx_bool fromJson(const QString &json, const QString &format = QString()) + { + qx::QxCollection *coll = this; + return qx::serialization::json::from_string((*coll), json, 1, format); + } + + virtual qx_bool fromJson_(const QJsonValue &json, const QString &format = QString()) + { + qx::QxCollection *coll = this; + return qx::cvt::from_json(json, (*coll), format); + } + +#endif // _QX_NO_JSON + }; + + /*! + * \ingroup QxDao + * \brief qx::QxPersistableCollectionHelper::type : return the collection type used by qx::IxPersistable interface, qx::QxPersistableCollection> + */ + template + class QxPersistableCollectionHelper + { + + private: + typedef typename qx::trait::get_primary_key::type qx_type_primary_key; + typedef std::shared_ptr qx_type_ptr; + + public: + typedef qx::QxPersistableCollection type; + typedef qx::QxCollection type_coll; + }; + +} // namespace qx + +QX_REGISTER_CLASS_NAME(qx::IxPersistableCollection) +QX_REGISTER_CLASS_NAME_TEMPLATE_3(qx::QxPersistableCollection) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(qx::QxPersistableCollectionHelper) + +#endif // _IX_PERSISTABLE_COLLECTION_H_ diff --git a/include/QxDao/IxPersistableList.h b/include/QxDao/IxPersistableList.h new file mode 100644 index 0000000..6edac02 --- /dev/null +++ b/include/QxDao/IxPersistableList.h @@ -0,0 +1,311 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_PERSISTABLE_LIST_H_ +#define _IX_PERSISTABLE_LIST_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxPersistableList.h + * \author XDL Team + * \ingroup QxDao + * \brief Common interface (abstract class) for list persistent classes based on qx::IxPersistable + */ + +#include +#include + +#include +#include + +#ifndef _QX_NO_JSON +#include +#endif // _QX_NO_JSON + +namespace qx +{ + + /*! + * \ingroup QxDao + * \brief qx::QxPersistableList : concrete class for list persistent classes based on qx::IxPersistableCollection (as an array instead of key/value hash-map) + */ + template + class QxPersistableList : public qx::IxPersistableCollection + { + + enum + { + qx_is_valid = qx::trait::is_qx_registered::value + }; + + protected: + QList> m_list; //!< List of persistable instances + + public: + QxPersistableList() : qx::IxPersistableCollection() { static_assert(qx_is_valid, "qx_is_valid"); } + virtual ~QxPersistableList() { ; } + + virtual long __count() const { return static_cast(m_list.size()); } + virtual void __clear() { m_list.clear(); } + virtual bool __remove(long idx) + { + if ((idx < 0) || (idx >= static_cast(m_list.size()))) + { + return false; + } + m_list.removeAt(idx); + return true; + } + virtual qx::IxPersistable_ptr __at(long idx) const + { + if ((idx < 0) || (idx >= static_cast(m_list.size()))) + { + return qx::IxPersistable_ptr(); + } + return std::static_pointer_cast(m_list.at(idx)); + } + + virtual long qxCount(const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL, const QStringList &relation = QStringList()) + { + if (relation.count() == 0) + { + return qx::dao::count(query, pDatabase); + } + else + { + long lCount(0); + qx::dao::count_with_relation(lCount, relation, query, pDatabase); + return lCount; + } + } + + virtual QSqlError qxCount(long &lCount, const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL, const QStringList &relation = QStringList()) + { + if (relation.count() == 0) + { + return qx::dao::count(lCount, query, pDatabase); + } + else + { + return qx::dao::count_with_relation(lCount, relation, query, pDatabase); + } + } + + virtual QSqlError qxFetchById(const QVariant &id = QVariant(), const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(id); + QSqlError err; + if (relation.count() == 0) + { + err = qx::dao::fetch_by_id(m_list, pDatabase, columns); + } + else + { + err = qx::dao::fetch_by_id_with_relation(relation, m_list, pDatabase); + } + return err; + } + + virtual QSqlError qxFetchAll(qx::IxPersistableCollection *list = NULL, const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(list); + QSqlError err; + if (relation.count() == 0) + { + err = qx::dao::fetch_all(m_list, pDatabase, columns); + } + else + { + err = qx::dao::fetch_all_with_relation(relation, m_list, pDatabase); + } + return err; + } + + virtual QSqlError qxFetchByQuery(const qx::QxSqlQuery &query, qx::IxPersistableCollection *list = NULL, const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(list); + QSqlError err; + if (relation.count() == 0) + { + err = qx::dao::fetch_by_query(query, m_list, pDatabase, columns); + } + else + { + err = qx::dao::fetch_by_query_with_relation(relation, query, m_list, pDatabase); + } + return err; + } + + virtual QSqlError qxInsert(const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + QSqlError err; + if (relation.count() == 0) + { + err = qx::dao::insert(m_list, pDatabase, bUseExecBatch); + } + else + { + err = qx::dao::insert_with_relation(relation, m_list, pDatabase); + } + return err; + } + + virtual QSqlError qxUpdate(const qx::QxSqlQuery &query = qx::QxSqlQuery(), const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + QSqlError err; + if (relation.count() == 0) + { + err = qx::dao::update_by_query(query, m_list, pDatabase, columns, bUseExecBatch); + } + else + { + err = qx::dao::update_by_query_with_relation(relation, query, m_list, pDatabase); + } + return err; + } + + virtual QSqlError qxSave(const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, qx::dao::save_mode::e_save_mode eSaveRecursiveMode = qx::dao::save_mode::e_none) + { + QSqlError err; + if (eSaveRecursiveMode != qx::dao::save_mode::e_none) + { + err = qx::dao::save_with_relation_recursive(m_list, eSaveRecursiveMode, pDatabase); + } + else if (relation.count() == 0) + { + err = qx::dao::save(m_list, pDatabase); + } + else + { + err = qx::dao::save_with_relation(relation, m_list, pDatabase); + } + return err; + } + + virtual QSqlError qxDeleteById(const QVariant &id = QVariant(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + Q_UNUSED(id); + return qx::dao::delete_by_id(m_list, pDatabase, bUseExecBatch); + } + + virtual QSqlError qxDeleteAll(QSqlDatabase *pDatabase = NULL) + { + return qx::dao::delete_all(pDatabase); + } + + virtual QSqlError qxDeleteByQuery(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::delete_by_query(query, pDatabase); + } + + virtual QSqlError qxDestroyById(const QVariant &id = QVariant(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + Q_UNUSED(id); + return qx::dao::destroy_by_id(m_list, pDatabase, bUseExecBatch); + } + + virtual QSqlError qxDestroyAll(QSqlDatabase *pDatabase = NULL) + { + return qx::dao::destroy_all(pDatabase); + } + + virtual QSqlError qxDestroyByQuery(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::destroy_by_query(query, pDatabase); + } + + virtual QSqlError qxExecuteQuery(qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::execute_query(query, m_list, pDatabase); + } + + virtual QSqlError qxExecuteQuery(qx::QxSqlQuery &query, qx::IxPersistableCollection *list = NULL, QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(list); + return qxExecuteQuery(query, pDatabase); + } + + virtual qx_bool qxExist(const QVariant &id = QVariant(), QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(id); + return qx::dao::exist(m_list, pDatabase); + } + + virtual qx::QxInvalidValueX qxValidate(const QStringList &groups = QStringList()) + { + return qx::validate(m_list, groups); + } + + virtual std::shared_ptr qxNewPersistableCollection(bool bAsList = false) const + { + Q_UNUSED(bAsList); + std::shared_ptr coll = std::make_shared>(); + return coll; + } + + virtual qx::IxClass *qxClass() const + { + return qx::QxClass::getSingleton(); + } + +#ifndef _QX_NO_JSON + + virtual QString toJson(const QString &format = QString()) const + { + return qx::serialization::json::to_string(m_list, 1, format); + } + + virtual QJsonValue toJson_(const QString &format = QString()) const + { + return qx::cvt::to_json(m_list, format); + } + + virtual qx_bool fromJson(const QString &json, const QString &format = QString()) + { + return qx::serialization::json::from_string(m_list, json, 1, format); + } + + virtual qx_bool fromJson_(const QJsonValue &json, const QString &format = QString()) + { + return qx::cvt::from_json(json, m_list, format); + } + +#endif // _QX_NO_JSON + }; + +} // namespace qx + +QX_REGISTER_CLASS_NAME_TEMPLATE_1(qx::QxPersistableList) + +#endif // _IX_PERSISTABLE_LIST_H_ diff --git a/include/QxDao/IxSqlQueryBuilder.h b/include/QxDao/IxSqlQueryBuilder.h new file mode 100644 index 0000000..075c594 --- /dev/null +++ b/include/QxDao/IxSqlQueryBuilder.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_SQL_QUERY_BUILDER_H_ +#define _IX_SQL_QUERY_BUILDER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxSqlQueryBuilder.h + * \author XDL Team + * \ingroup QxDao + * \brief Common interface to build SQL queries to communicate with database + */ + +#include + +#include +#include +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + class IxDao_Helper; + + } // namespace detail + } // namespace dao +} // namespace qx + +namespace qx +{ + + /*! + * \ingroup QxDao + * \brief qx::IxSqlQueryBuilder : common interface to build SQL queries to communicate with database + */ + class QX_DLL_EXPORT IxSqlQueryBuilder + { + + private: + struct IxSqlQueryBuilderImpl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + + public: + IxSqlQueryBuilder(); + virtual ~IxSqlQueryBuilder() = 0; + + IxDataMemberX *getDataMemberX() const; + QxCollection *getLstDataMember() const; + IxSqlRelationX *getLstRelation() const; + qx::dao::detail::IxDao_Helper *getDaoHelper() const; + void setDaoHelper(qx::dao::detail::IxDao_Helper *p); + + void setHashRelation(const QString &s); + void setCartesianProduct(bool b); + QString getSqlQuery() const; + QString getHashRelation() const; + QString table() const; + QxSoftDelete getSoftDelete() const; + bool getCartesianProduct() const; + long getDataCount() const; + long getRelationCount() const; + IxDataMember *getDataId() const; + IxDataMember *nextData(long &l) const; + IxSqlRelation *nextRelation(long &l) const; + QString &getCurrentBuildingSql() const; + + void initIdX(long lAllRelationCount); + bool insertIdX(long lIndex, const QVariant &idOwner, const QVariant &idData, void *ptr); + void *existIdX(long lIndex, const QVariant &idOwner, const QVariant &idData); + void setSqlQuery(const QString &sql, const QString &key = QString()); + void addSqlQueryAlias(const QString &sql, const QString &sqlAlias); + bool getAddAutoIncrementIdToUpdateQuery() const; + void replaceSqlQueryAlias(QString &sql) const; + + virtual void init(); + virtual void clone(const IxSqlQueryBuilder &other); + virtual IxSqlQueryBuilder &buildSql(const QStringList &columns = QStringList(), QxSqlRelationLinked *pRelationX = NULL) = 0; + + static QString addSqlCondition(const QString &sql) { return (sql.contains(" WHERE ") ? " AND " : " WHERE "); } + + static void sql_CreateTable(QString &sql, IxSqlQueryBuilder &builder); + static void sql_DeleteById(QString &sql, IxSqlQueryBuilder &builder, bool bSoftDelete); + static void sql_Exist(QString &sql, IxSqlQueryBuilder &builder); + static void sql_FetchAll(QString &sql, IxSqlQueryBuilder &builder); + static void sql_FetchAll(QString &sql, IxSqlQueryBuilder &builder, const QStringList &columns); + static void sql_FetchAll_WithRelation(qx::QxSqlRelationLinked *pRelationX, QString &sql, IxSqlQueryBuilder &builder); + static void sql_FetchById(QString &sql, IxSqlQueryBuilder &builder); + static void sql_FetchById(QString &sql, IxSqlQueryBuilder &builder, const QStringList &columns); + static void sql_FetchById_WithRelation(qx::QxSqlRelationLinked *pRelationX, QString &sql, IxSqlQueryBuilder &builder); + static void sql_Insert(QString &sql, IxSqlQueryBuilder &builder); + static void sql_Update(QString &sql, IxSqlQueryBuilder &builder); + static void sql_Update(QString &sql, IxSqlQueryBuilder &builder, const QStringList &columns); + static void sql_Count_WithRelation(qx::QxSqlRelationLinked *pRelationX, QString &sql, IxSqlQueryBuilder &builder); + + static void resolveOutput_FetchAll(void *t, QSqlQuery &query, IxSqlQueryBuilder &builder); + static void resolveOutput_FetchAll(void *t, QSqlQuery &query, IxSqlQueryBuilder &builder, const QStringList &columns); + static void resolveOutput_FetchAll_WithRelation(qx::QxSqlRelationLinked *pRelationX, void *t, QSqlQuery &query, IxSqlQueryBuilder &builder); + + static void resolveInput_Insert(void *t, QSqlQuery &query, IxSqlQueryBuilder &builder); + static void resolveInput_Update(void *t, QSqlQuery &query, IxSqlQueryBuilder &builder); + static void resolveInput_Update(void *t, QSqlQuery &query, IxSqlQueryBuilder &builder, const QStringList &columns); + static void resolveInput_DeleteById(void *t, QSqlQuery &query, IxSqlQueryBuilder &builder); + + protected: + bool verifyColumns(const QStringList &columns) const QX_USED; + + bool isInitDone() const; + QxSoftDelete &softDelete(); + const QxSoftDelete &softDelete() const; + void setSoftDelete(const QxSoftDelete &o); + void setDataMemberX(IxDataMemberX *p); + bool findSqlQuery(const QString &key); + bool findSqlAlias(const QString &key); + void insertSqlAlias(const QString &key); + }; + + typedef std::shared_ptr IxSqlQueryBuilder_ptr; + +} // namespace qx + +#include + +#endif // _IX_SQL_QUERY_BUILDER_H_ diff --git a/include/QxDao/IxSqlRelation.h b/include/QxDao/IxSqlRelation.h new file mode 100644 index 0000000..81225f2 --- /dev/null +++ b/include/QxDao/IxSqlRelation.h @@ -0,0 +1,203 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_SQL_RELATION_H_ +#define _IX_SQL_RELATION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxSqlRelation.h + * \author XDL Team + * \ingroup QxDao + * \brief Common interface for all relationships defined between 2 classes (or between 2 tables in database) + */ + +#include + +#include +#include +#include + +#include + +namespace qx +{ + + class IxClass; + class IxDataMember; + class IxDataMemberX; + class IxSqlRelation; + + typedef QxCollection IxSqlRelationX; + typedef std::shared_ptr IxSqlRelationX_ptr; + + /*! + * \ingroup QxDao + * \brief qx::IxSqlRelation : common interface for all relationships defined between 2 classes (or between 2 tables in database) + */ + class QX_DLL_EXPORT IxSqlRelation : public qx::QxPropertyBag + { + + private: + struct IxSqlRelationImpl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + + public: + enum relation_type + { + no_relation, + one_to_one, + one_to_many, + many_to_one, + many_to_many + }; + + IxSqlRelation(IxDataMember *p); + virtual ~IxSqlRelation() = 0; + + QxCollection *getLstDataMember() const; + IxSqlRelationX *getLstRelation() const; + + void setSqlJoinType(qx::dao::sql_join::join_type e); + qx::dao::sql_join::join_type getSqlJoinType() const; + relation_type getRelationType() const; + IxClass *getClass() const; + IxClass *getClassOwner() const; + IxDataMember *getDataMember() const; + IxDataMemberX *getDataMemberX() const; + IxDataMember *getDataId() const; + IxDataMember *getDataIdOwner() const; + + void linkRelationKeyTo(IxDataMember *p); + IxDataMember *getLinkRelationKey() const; + + QString getKey() const; + QString getForeignKey() const; + QString getForeignKeyOwner() const; + QString getForeignKeyDataType() const; + QString getExtraTable() const; + long getDataCount() const; + long getRelationCount() const; + IxDataMember *getDataByKey(const QString &sKey) const; + IxDataMember *nextData(long &lIndex) const; + IxSqlRelation *nextRelation(long &lIndex) const; + QString table() const; + QString tableAlias(QxSqlRelationParams ¶ms) const; + QString tableAliasOwner(QxSqlRelationParams ¶ms) const; + QString getSqlJoin(qx::dao::sql_join::join_type e = qx::dao::sql_join::no_join) const; + bool traceSqlQuery() const; + + virtual void init(); + virtual QString getDescription() const = 0; + virtual QString createExtraTable() const = 0; + virtual bool getCartesianProduct() const = 0; + virtual QVariant getIdFromQuery(bool bEager, QxSqlRelationParams ¶ms, int iOffset, int iNameIndex) const = 0; + virtual void updateOffset(bool bEager, QxSqlRelationParams ¶ms) const = 0; + virtual void createTable(QxSqlRelationParams ¶ms) const = 0; + virtual void lazySelect(QxSqlRelationParams ¶ms) const = 0; + virtual void eagerSelect(QxSqlRelationParams ¶ms) const = 0; + virtual void lazyFrom(QxSqlRelationParams ¶ms) const = 0; + virtual void eagerFrom(QxSqlRelationParams ¶ms) const = 0; + virtual void lazyJoin(QxSqlRelationParams ¶ms) const = 0; + virtual void eagerJoin(QxSqlRelationParams ¶ms) const = 0; + virtual void lazyWhere(QxSqlRelationParams ¶ms) const = 0; + virtual void eagerWhere(QxSqlRelationParams ¶ms) const = 0; + virtual void lazyWhereSoftDelete(QxSqlRelationParams ¶ms) const = 0; + virtual void eagerWhereSoftDelete(QxSqlRelationParams ¶ms) const = 0; + virtual void lazyFetch_ResolveInput(QxSqlRelationParams ¶ms) const = 0; + virtual void eagerFetch_ResolveInput(QxSqlRelationParams ¶ms) const = 0; + virtual void lazyFetch_ResolveOutput(QxSqlRelationParams ¶ms) const = 0; + virtual void *eagerFetch_ResolveOutput(QxSqlRelationParams ¶ms) const = 0; + virtual void lazyInsert(QxSqlRelationParams ¶ms) const = 0; + virtual void lazyInsert_Values(QxSqlRelationParams ¶ms) const = 0; + virtual void lazyUpdate(QxSqlRelationParams ¶ms) const = 0; + virtual void lazyInsert_ResolveInput(QxSqlRelationParams ¶ms) const = 0; + virtual void lazyUpdate_ResolveInput(QxSqlRelationParams ¶ms) const = 0; + virtual QSqlError onBeforeSave(QxSqlRelationParams ¶ms) const = 0; + virtual QSqlError onAfterSave(QxSqlRelationParams ¶ms) const = 0; + + bool verifyOffset(QxSqlRelationParams ¶ms, bool bId) const QX_USED; + + static void setTraceRelationInit(bool bTrace); + + protected: + QVariant getIdFromQuery_ManyToMany(bool bEager, QxSqlRelationParams ¶ms, int iOffset, int iNameIndex) const; + QVariant getIdFromQuery_ManyToOne(bool bEager, QxSqlRelationParams ¶ms, int iOffset, int iNameIndex) const; + QVariant getIdFromQuery_OneToMany(bool bEager, QxSqlRelationParams ¶ms, int iOffset, int iNameIndex) const; + QVariant getIdFromQuery_OneToOne(bool bEager, QxSqlRelationParams ¶ms, int iOffset, int iNameIndex) const; + + void updateOffset_ManyToMany(bool bEager, QxSqlRelationParams ¶ms) const; + void updateOffset_ManyToOne(bool bEager, QxSqlRelationParams ¶ms) const; + void updateOffset_OneToMany(bool bEager, QxSqlRelationParams ¶ms) const; + void updateOffset_OneToOne(bool bEager, QxSqlRelationParams ¶ms) const; + + void eagerSelect_ManyToMany(QxSqlRelationParams ¶ms) const; + void eagerSelect_ManyToOne(QxSqlRelationParams ¶ms) const; + void eagerSelect_OneToMany(QxSqlRelationParams ¶ms) const; + void eagerSelect_OneToOne(QxSqlRelationParams ¶ms) const; + + void eagerJoin_ManyToMany(QxSqlRelationParams ¶ms) const; + void eagerJoin_ManyToOne(QxSqlRelationParams ¶ms) const; + void eagerJoin_OneToMany(QxSqlRelationParams ¶ms) const; + void eagerJoin_OneToOne(QxSqlRelationParams ¶ms) const; + + void eagerWhereSoftDelete_ManyToMany(QxSqlRelationParams ¶ms) const; + void eagerWhereSoftDelete_ManyToOne(QxSqlRelationParams ¶ms) const; + void eagerWhereSoftDelete_OneToMany(QxSqlRelationParams ¶ms) const; + void eagerWhereSoftDelete_OneToOne(QxSqlRelationParams ¶ms) const; + + void lazySelect_ManyToOne(QxSqlRelationParams ¶ms) const; + void lazyInsert_ManyToOne(QxSqlRelationParams ¶ms) const; + void lazyInsert_Values_ManyToOne(QxSqlRelationParams ¶ms) const; + void lazyUpdate_ManyToOne(QxSqlRelationParams ¶ms) const; + + void createTable_ManyToOne(QxSqlRelationParams ¶ms) const; + QSqlError deleteFromExtraTable_ManyToMany(QxSqlRelationParams ¶ms) const; + QString createExtraTable_ManyToMany() const; + + bool addLazyRelation(QxSqlRelationParams ¶ms, IxSqlRelation *pRelation) const; + + bool canInit() const; + void setIsSameDataOwner(int i); + void setClass(IxClass *pClass, IxClass *pClassOwner); + void setRelationType(relation_type e); + void setForeignKey(const QString &s) const; + void setForeignKeyOwner(const QString &s) const; + void setForeignKeyDataType(const QString &s) const; + void setExtraTable(const QString &s) const; + }; + +} // namespace qx + +#endif // _IX_SQL_RELATION_H_ diff --git a/include/QxDao/QxDao.h b/include/QxDao/QxDao.h new file mode 100644 index 0000000..bc531d0 --- /dev/null +++ b/include/QxDao/QxDao.h @@ -0,0 +1,938 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_DAO_H_ +#define _QX_DAO_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxDao.h + * \author XDL Team + * \ingroup QxDao + * \brief Provide template functions to map C++ class registered into QxOrm context with table database (ORM - Object Relational Mapping) + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace qx +{ + class QxSqlRelationParams; + namespace dao + { + + namespace detail + { + class IxDao_Helper; + template + struct QxDao_Count; + template + struct QxDao_Count_WithRelation; + template + struct QxDao_FetchById; + template + struct QxDao_FetchById_WithRelation; + template + struct QxDao_FetchAll; + template + struct QxDao_FetchAll_WithRelation; + template + struct QxDao_Insert; + template + struct QxDao_Insert_WithRelation; + template + struct QxDao_Update; + template + struct QxDao_Update_Optimized; + template + struct QxDao_Update_WithRelation; + template + struct QxDao_Save; + template + struct QxDao_Save_WithRelation; + template + struct QxDao_Save_WithRelation_Recursive; + template + struct QxDao_DeleteById; + template + struct QxDao_DeleteAll; + template + struct QxDao_Exist; + template + struct QxDao_CreateTable; + template + struct QxDao_Trigger; + template + struct QxDao_ExecuteQuery; + } // namespace detail + + /*! + * \ingroup QxDao + * \brief Return the number of lines in the table (database) mapped to the C++ class T (registered into QxOrm context) and filtered by a user SQL query + * \param query Define a user SQL query added to default SQL query builded by QxOrm library (optional parameter) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * + * qx::dao::count() execute following SQL query :
    + * SELECT COUNT(*) FROM my_table + WHERE my_query... + */ + template + inline long count(const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_Count::count(query, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Return the number of lines in the table (database) mapped to the C++ class T (registered into QxOrm context) and filtered by a user SQL query + * \param lCount Output parameter with the number of lines in the table associated to the SQL query + * \param query Define a user SQL query added to default SQL query builded by QxOrm library (optional parameter) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * + * qx::dao::count() execute following SQL query :
    + * SELECT COUNT(*) FROM my_table + WHERE my_query... + */ + template + inline QSqlError count(long &lCount, const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_Count::count(lCount, query, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Return the number of lines in the table (database) mapped to the C++ class T (registered into QxOrm context) and filtered by a user SQL query (with possibility to add relations to SQL query) + * \param lCount Output parameter with the number of lines in the table associated to the SQL query + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) + * \param query Define a user SQL query added to default SQL query builded by QxOrm library (optional parameter) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * + * qx::dao::count_with_relation() execute following SQL query :
    + * SELECT COUNT(*) FROM my_table + XXX_JOINS_XXX + WHERE my_query... + */ + template + inline QSqlError count_with_relation(long &lCount, const QStringList &relation, const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_Count_WithRelation::count(lCount, relation, query, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Insert an element or a list of elements into database + * \param t Element (or list of elements) to be inserted into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance inserting a list of instances to database (but doesn't fill the last inserted identifier in the C++ instances) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::insert() execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + */ + template + inline QSqlError insert(T &t, QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + return qx::dao::detail::QxDao_Insert::insert(t, pDatabase, bUseExecBatch); + } + + /*! + * \ingroup QxDao + * \brief Insert (if no exist) or update (if already exist) an element or a list of elements into database + * \param t Element (or list of elements) to be inserted (if no exist) or updated (if already exist) into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::save() execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + *
    or (if already exist into database) :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + */ + template + inline QSqlError save(T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_Save::save(t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Delete a line (or list of lines) of a table (database) mapped to a C++ object of type T (registered into QxOrm context) + * \param t Element (or list of elements) to be deleted into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance deleting a list of instances in database + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::delete_by_id() execute following SQL query :
    + * DELETE FROM my_table WHERE my_id = ?
    + *
    + * If a soft delete behavior is defined for class T, qx::dao::delete_by_id() execute following SQL query :
    + * UPDATE my_table SET is_deleted='1' WHERE my_id = ? + */ + template + inline QSqlError delete_by_id(T &t, QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + return qx::dao::detail::QxDao_DeleteById::deleteById(t, pDatabase, true, bUseExecBatch); + } + + /*! + * \ingroup QxDao + * \brief Destroy a line (or list of lines) of a table (database) mapped to a C++ object of type T (registered into QxOrm context), even if a soft delete behavior is defined for class T + * \param t Element (or list of elements) to be destroyed into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance destroying a list of instances in database + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::destroy_by_id() execute following SQL query :
    + * DELETE FROM my_table WHERE my_id = ? + */ + template + inline QSqlError destroy_by_id(T &t, QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + return qx::dao::detail::QxDao_DeleteById::deleteById(t, pDatabase, false, bUseExecBatch); + } + + /*! + * \ingroup QxDao + * \brief Delete all lines of a table (database) mapped to a C++ class T (registered into QxOrm context) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::delete_all() execute following SQL query :
    + * DELETE FROM my_table
    + *
    + * If a soft delete behavior is defined for class T, qx::dao::delete_all() execute following SQL query :
    + * UPDATE my_table SET is_deleted='1' + */ + template + inline QSqlError delete_all(QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_DeleteAll::deleteAll("", pDatabase, true); + } + + /*! + * \ingroup QxDao + * \brief Destroy all lines of a table (database) mapped to a C++ class T (registered into QxOrm context), even if a soft delete behavior is defined for class T + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::destroy_all() execute following SQL query :
    + * DELETE FROM my_table + */ + template + inline QSqlError destroy_all(QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_DeleteAll::deleteAll("", pDatabase, false); + } + + /*! + * \ingroup QxDao + * \brief Delete all lines of a table (database) mapped to a C++ class T (registered into QxOrm context) and filtered by a user SQL query + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::delete_by_query() execute following SQL query :
    + * DELETE FROM my_table + WHERE my_query...
    + *
    + * If a soft delete behavior is defined for class T, qx::dao::delete_by_query() execute following SQL query :
    + * UPDATE my_table SET is_deleted='1' + WHERE my_query... + */ + template + inline QSqlError delete_by_query(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_DeleteAll::deleteAll(query, pDatabase, true); + } + + /*! + * \ingroup QxDao + * \brief Destroy all lines of a table (database) mapped to a C++ class T (registered into QxOrm context) and filtered by a user SQL query, even if a soft delete behavior is defined for class T + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::destroy_by_query() execute following SQL query :
    + * DELETE FROM my_table + WHERE my_query... + */ + template + inline QSqlError destroy_by_query(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_DeleteAll::deleteAll(query, pDatabase, false); + } + + /*! + * \ingroup QxDao + * \brief Create a table into database (with all columns) mapped to a C++ class T (registered into QxOrm context) : be careful, this function can be used only with a SQLite database to create examples or prototypes; For other databases, it is recommended to use QxEntityEditor application or to manage the database schema with an external tool provided by the SGBD (SQLite Manager for SQLite, pgAdmin for PostgreSQL, MySQL Workbench for MySQL, etc...) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::create_table() execute following SQL query :
    + * CREATE TABLE my_table (my_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, my_column_1 TEXT, my_column_2 TEXT, etc.) + */ + template + inline QSqlError create_table(QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_CreateTable::createTable(pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Search if an element (or list of elements) already exists into database + * \param t Element (or list of elements) to be searched into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Return true if element already exists into database; otherwise return false; if an error occurred, qx_bool object contains a description of database error executing SQL query + * + * qx::dao::exist() execute following SQL query :
    + * SELECT * FROM my_table WHERE my_id = ? + */ + template + inline qx_bool exist(T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_Exist::exist(t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Fetch an object t (retrieve all its properties) of type T (registered into QxOrm context) mapped to a table in the database (t must have a valid id before to be fetched without error) + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) : use "|" separator to put many relationships keys into this parameter + * \param t Instance (with a valid id) to be fetched (retrieve all properties from database) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::fetch_by_id_with_relation() execute following SQL query :
    + * SELECT * FROM my_table WHERE my_id = ? + */ + template + inline QSqlError fetch_by_id_with_relation(const QString &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_FetchById_WithRelation::fetchById(relation, t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Fetch an object t (retrieve all its properties) of type T (registered into QxOrm context) mapped to a table in the database (t must have a valid id before to be fetched without error) + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) + * \param t Instance (with a valid id) to be fetched (retrieve all properties from database) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::fetch_by_id_with_relation() execute following SQL query :
    + * SELECT * FROM my_table WHERE my_id = ? + */ + template + inline QSqlError fetch_by_id_with_relation(const QStringList &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_FetchById_WithRelation::fetchById(relation, t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Fetch an object t (retrieve all its properties and relationships) of type T (registered into QxOrm context) mapped to a table in the database (t must have a valid id before to be fetched without error) + * \param t Instance (with a valid id) to be fetched (retrieve all properties and relationships from database) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::fetch_by_id_with_all_relation() execute following SQL query :
    + * SELECT * FROM my_table WHERE my_id = ? + */ + template + inline QSqlError fetch_by_id_with_all_relation(T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_FetchById_WithRelation::fetchById("*", t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Fetch a list of objects (retrieve all elements and properties associated) of type T (container registered into QxOrm context) mapped to a table in the database + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) : use "|" separator to put many relationships keys into this parameter + * \param t Container to be fetched (retrieve all elements and properties associated); t is cleared before executing SQL query + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::fetch_all_with_relation() execute following SQL query :
    + * SELECT * FROM my_table + */ + template + inline QSqlError fetch_all_with_relation(const QString &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_FetchAll_WithRelation::fetchAll(relation, "", t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Fetch a list of objects (retrieve all elements and properties associated) of type T (container registered into QxOrm context) mapped to a table in the database + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) + * \param t Container to be fetched (retrieve all elements and properties associated); t is cleared before executing SQL query + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::fetch_all_with_relation() execute following SQL query :
    + * SELECT * FROM my_table + */ + template + inline QSqlError fetch_all_with_relation(const QStringList &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_FetchAll_WithRelation::fetchAll(relation, "", t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Fetch a list of objects (retrieve all elements and properties + all relationships associated) of type T (container registered into QxOrm context) mapped to a table in the database + * \param t Container to be fetched (retrieve all elements and properties + all relationships associated); t is cleared before executing SQL query + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::fetch_all_with_all_relation() execute following SQL query :
    + * SELECT * FROM my_table + */ + template + inline QSqlError fetch_all_with_all_relation(T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_FetchAll_WithRelation::fetchAll("*", "", t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Fetch a list of objects (retrieve all elements and properties associated) of type T (container registered into QxOrm context) mapped to a table in the database and filtered by a user SQL query + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) : use "|" separator to put many relationships keys into this parameter + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param t Container to be fetched (retrieve all elements and properties associated); t is cleared before executing SQL query + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::fetch_by_query_with_relation() execute following SQL query :
    + * SELECT * FROM my_table + WHERE my_query... + */ + template + inline QSqlError fetch_by_query_with_relation(const QString &relation, const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_FetchAll_WithRelation::fetchAll(relation, query, t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Fetch a list of objects (retrieve all elements and properties associated) of type T (container registered into QxOrm context) mapped to a table in the database and filtered by a user SQL query + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param t Container to be fetched (retrieve all elements and properties associated); t is cleared before executing SQL query + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::fetch_by_query_with_relation() execute following SQL query :
    + * SELECT * FROM my_table + WHERE my_query... + */ + template + inline QSqlError fetch_by_query_with_relation(const QStringList &relation, const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_FetchAll_WithRelation::fetchAll(relation, query, t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Fetch a list of objects (retrieve all elements and properties + all relationships associated) of type T (container registered into QxOrm context) mapped to a table in the database and filtered by a user SQL query + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param t Container to be fetched (retrieve all elements and properties + all relationships associated); t is cleared before executing SQL query + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::fetch_by_query_with_all_relation() execute following SQL query :
    + * SELECT * FROM my_table + WHERE my_query... + */ + template + inline QSqlError fetch_by_query_with_all_relation(const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_FetchAll_WithRelation::fetchAll("*", query, t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Insert an element and its relationships (or a list of elements + relationships) into database + * \param relation List of relationships keys to be inserted in others tables of database : use "|" separator to put many relationships keys into this parameter + * \param t Element (or list of elements) to be inserted into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::insert_with_relation() execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + */ + template + inline QSqlError insert_with_relation(const QString &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_Insert_WithRelation::insert(relation, t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Insert an element and its relationships (or a list of elements + relationships) into database + * \param relation List of relationships keys to be inserted in others tables of database + * \param t Element (or list of elements) to be inserted into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::insert_with_relation() execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + */ + template + inline QSqlError insert_with_relation(const QStringList &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_Insert_WithRelation::insert(relation, t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Insert an element and all its relationships (or a list of elements + all relationships) into database + * \param t Element (or list of elements) to be inserted into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::insert_with_all_relation() execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + */ + template + inline QSqlError insert_with_all_relation(T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_Insert_WithRelation::insert("*", t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Update an element and its relationships (or a list of elements + relationships) into database + * \param relation List of relationships keys to be updated in others tables of database : use "|" separator to put many relationships keys into this parameter + * \param t Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::update_with_relation() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + */ + template + inline QSqlError update_with_relation(const QString &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_Update_WithRelation::update(relation, "", t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Update an element and its relationships (or a list of elements + relationships) into database (adding a user SQL query to the default SQL query builded by QxOrm library) + * \param relation List of relationships keys to be updated in others tables of database : use "|" separator to put many relationships keys into this parameter + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param t Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::update_by_query_with_relation() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + WHERE my_query... + */ + template + inline QSqlError update_by_query_with_relation(const QString &relation, const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_Update_WithRelation::update(relation, query, t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Update an element and its relationships (or a list of elements + relationships) into database + * \param relation List of relationships keys to be updated in others tables of database + * \param t Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::update_with_relation() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + */ + template + inline QSqlError update_with_relation(const QStringList &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_Update_WithRelation::update(relation, "", t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Update an element and its relationships (or a list of elements + relationships) into database (adding a user SQL query to the default SQL query builded by QxOrm library) + * \param relation List of relationships keys to be updated in others tables of database + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param t Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::update_by_query_with_relation() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + WHERE my_query... + */ + template + inline QSqlError update_by_query_with_relation(const QStringList &relation, const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_Update_WithRelation::update(relation, query, t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Update an element and all its relationships (or a list of elements + all relationships) into database + * \param t Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::update_with_all_relation() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + */ + template + inline QSqlError update_with_all_relation(T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_Update_WithRelation::update("*", "", t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Update an element and all its relationships (or a list of elements + all relationships) into database (adding a user SQL query to the default SQL query builded by QxOrm library) + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param t Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::update_by_query_with_all_relation() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + WHERE my_query... + */ + template + inline QSqlError update_by_query_with_all_relation(const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_Update_WithRelation::update("*", query, t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Insert (if no exist) or update (if already exist) an element and its relationships (or a list of elements + relationships) into database + * \param relation List of relationships keys to be inserted or updated in others tables of database : use "|" separator to put many relationships keys into this parameter + * \param t Element (or list of elements) to be inserted (if no exist) or updated (if already exist) into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::save_with_relation() execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + *
    or (if already exist into database) :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + */ + template + inline QSqlError save_with_relation(const QString &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_Save_WithRelation::save(relation, t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Insert (if no exist) or update (if already exist) an element and its relationships (or a list of elements + relationships) into database + * \param relation List of relationships keys to be inserted or updated in others tables of database + * \param t Element (or list of elements) to be inserted (if no exist) or updated (if already exist) into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::save_with_relation() execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + *
    or (if already exist into database) :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + */ + template + inline QSqlError save_with_relation(const QStringList &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_Save_WithRelation::save(relation, t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Insert (if no exist) or update (if already exist) an element and all its relationships (or a list of elements + all relationships) into database + * \param t Element (or list of elements) to be inserted (if no exist) or updated (if already exist) into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::save_with_all_relation() execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + *
    or (if already exist into database) :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + */ + template + inline QSqlError save_with_all_relation(T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_Save_WithRelation::save("*", t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Insert (if no exist) or update (if already exist) recursively an element and all levels of relationships (or a list of elements + all levels of relationships) into database, useful to save a tree structure for example + * \param t Element (or list of elements) to be inserted (if no exist) or updated (if already exist) into database + * \param eSaveMode To improve performance, use this parameter to indicate if you just want to insert or update all elements in database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param pRelationParams Keep this parameter as NULL, it is used internally by QxOrm library to iterate over each level of relationship + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::save_with_relation_recursive() execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + *
    or (if already exist into database) :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc.
    + *
    + * Note : to improve performance, and if you know that you are just inserting or updating items in database, you can use the parameter eSaveMode :
    + * - qx::dao::save_mode::e_check_insert_or_update : check before saving if item already exists in database ;
    + * - qx::dao::save_mode::e_insert_only : only insert items in database (use only 1 SQL query to insert collection of items) ;
    + * - qx::dao::save_mode::e_update_only : only update items in database (use only 1 SQL query to update collection of items). + */ + template + inline QSqlError save_with_relation_recursive(T &t, qx::dao::save_mode::e_save_mode eSaveMode = qx::dao::save_mode::e_check_insert_or_update, QSqlDatabase *pDatabase = NULL, qx::QxSqlRelationParams *pRelationParams = NULL) + { + return qx::dao::detail::QxDao_Save_WithRelation_Recursive::save(t, eSaveMode, pDatabase, pRelationParams); + } + + /*! + * \ingroup QxDao + * \brief Fetch an object t (retrieve all its properties) of type T (registered into QxOrm context) mapped to a table in the database (t must have a valid id before to be fetched without error) + * \param t Instance (with a valid id) to be fetched (retrieve all properties from database) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param columns List of database table columns (mapped to properties of C++ class T) to be fetched (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::fetch_by_id() execute following SQL query :
    + * SELECT * FROM my_table WHERE my_id = ? + */ + template + inline QSqlError fetch_by_id(T &t, QSqlDatabase *pDatabase = NULL, const QStringList &columns = QStringList()) + { + return qx::dao::detail::QxDao_FetchById::fetchById(t, pDatabase, columns); + } + + /*! + * \ingroup QxDao + * \brief Fetch a list of objects (retrieve all elements and properties associated) of type T (container registered into QxOrm context) mapped to a table in the database + * \param t Container to be fetched (retrieve all elements and properties associated); t is cleared before executing SQL query + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param columns List of database table columns (mapped to properties of C++ class T) to be fetched (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::fetch_all() execute following SQL query :
    + * SELECT * FROM my_table + */ + template + inline QSqlError fetch_all(T &t, QSqlDatabase *pDatabase = NULL, const QStringList &columns = QStringList()) + { + return qx::dao::detail::QxDao_FetchAll::fetchAll("", t, pDatabase, columns); + } + + /*! + * \ingroup QxDao + * \brief Fetch a list of objects (retrieve all elements and properties associated) of type T (container registered into QxOrm context) mapped to a table in the database and filtered by a user SQL query + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param t Container to be fetched (retrieve all elements and properties associated); t is cleared before executing SQL query + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param columns List of database table columns (mapped to properties of C++ class T) to be fetched (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::fetch_by_query() execute following SQL query :
    + * SELECT * FROM my_table + WHERE my_query... + */ + template + inline QSqlError fetch_by_query(const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase = NULL, const QStringList &columns = QStringList()) + { + return qx::dao::detail::QxDao_FetchAll::fetchAll(query, t, pDatabase, columns); + } + + /*! + * \ingroup QxDao + * \brief Update an element or a list of elements into database + * \param t Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param columns List of database table columns (mapped to properties of C++ class T) to be updated (optional parameter) + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance updating a list of instances in database + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::update() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + */ + template + inline QSqlError update(T &t, QSqlDatabase *pDatabase = NULL, const QStringList &columns = QStringList(), bool bUseExecBatch = false) + { + return qx::dao::detail::QxDao_Update::update("", t, pDatabase, columns, bUseExecBatch); + } + + /*! + * \ingroup QxDao + * \brief Update an element or a list of elements into database (adding a user SQL query to the default SQL query builded by QxOrm library) + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param t Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param columns List of database table columns (mapped to properties of C++ class T) to be updated (optional parameter) + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance updating a list of instances in database + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::update_by_query() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + WHERE my_query... + */ + template + inline QSqlError update_by_query(const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase = NULL, const QStringList &columns = QStringList(), bool bUseExecBatch = false) + { + return qx::dao::detail::QxDao_Update::update(query, t, pDatabase, columns, bUseExecBatch); + } + + /*! + * \ingroup QxDao + * \brief Update only modified fields/properties of an element or a list of elements into database (using is dirty pattern and qx::dao::ptr smart-pointer) + * \param ptr Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance updating a list of instances in database + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::update_optimized() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + */ + template + inline QSqlError update_optimized(qx::dao::ptr &ptr, QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + return qx::dao::detail::QxDao_Update_Optimized::update_optimized("", ptr, pDatabase, bUseExecBatch); + } + + /*! + * \ingroup QxDao + * \brief Update only modified fields/properties of an element or a list of elements into database (using is dirty pattern and qx::dao::ptr smart-pointer), adding a user SQL query to the default SQL query builded by QxOrm library + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param ptr Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance updating a list of instances in database + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + * + * qx::dao::update_optimized_by_query() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + WHERE my_query... + */ + template + inline QSqlError update_optimized_by_query(const qx::QxSqlQuery &query, qx::dao::ptr &ptr, QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + return qx::dao::detail::QxDao_Update_Optimized::update_optimized(query, ptr, pDatabase, bUseExecBatch); + } + + /*! + * \ingroup QxDao + * \brief Execute a custom SQL query or a stored procedure, all columns that can be mapped to the instance of type T will be fetched automatically + * \param query Define a custom SQL query or a stored procedure to call + * \param t Instance of type T, all columns that can be mapped to this instance will be fetched automatically + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + template + inline QSqlError execute_query(qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase = NULL) + { + return qx::dao::detail::QxDao_ExecuteQuery::executeQuery(query, t, pDatabase); + } + + /*! + * \ingroup QxDao + * \brief Callback before inserting an object into database (here is an example using QxOrm Trigger) + */ + template + inline void on_before_insert(T *t, qx::dao::detail::IxDao_Helper *dao) + { + qx::dao::detail::QxDao_Trigger::onBeforeInsert(t, dao); + } + + /*! + * \ingroup QxDao + * \brief Callback before updating an object into database (here is an example using QxOrm Trigger) + */ + template + inline void on_before_update(T *t, qx::dao::detail::IxDao_Helper *dao) + { + qx::dao::detail::QxDao_Trigger::onBeforeUpdate(t, dao); + } + + /*! + * \ingroup QxDao + * \brief Callback before deleting an object into database (here is an example using QxOrm Trigger) + */ + template + inline void on_before_delete(T *t, qx::dao::detail::IxDao_Helper *dao) + { + qx::dao::detail::QxDao_Trigger::onBeforeDelete(t, dao); + } + + /*! + * \ingroup QxDao + * \brief Callback before fetching an object from database (here is an example using QxOrm Trigger) + */ + template + inline void on_before_fetch(T *t, qx::dao::detail::IxDao_Helper *dao) + { + qx::dao::detail::QxDao_Trigger::onBeforeFetch(t, dao); + } + + /*! + * \ingroup QxDao + * \brief Callback after inserting an object into database (here is an example using QxOrm Trigger) + */ + template + inline void on_after_insert(T *t, qx::dao::detail::IxDao_Helper *dao) + { + qx::dao::detail::QxDao_Trigger::onAfterInsert(t, dao); + } + + /*! + * \ingroup QxDao + * \brief Callback after updating an object into database (here is an example using QxOrm Trigger) + */ + template + inline void on_after_update(T *t, qx::dao::detail::IxDao_Helper *dao) + { + qx::dao::detail::QxDao_Trigger::onAfterUpdate(t, dao); + } + + /*! + * \ingroup QxDao + * \brief Callback after deleting an object into database (here is an example using QxOrm trigger) + */ + template + inline void on_after_delete(T *t, qx::dao::detail::IxDao_Helper *dao) + { + qx::dao::detail::QxDao_Trigger::onAfterDelete(t, dao); + } + + /*! + * \ingroup QxDao + * \brief Callback after fetching an object from database (here is an example using QxOrm trigger) + */ + template + inline void on_after_fetch(T *t, qx::dao::detail::IxDao_Helper *dao) + { + qx::dao::detail::QxDao_Trigger::onAfterFetch(t, dao); + } + + } // namespace dao +} // namespace qx + +#endif // _QX_DAO_H_ diff --git a/include/QxDao/QxDaoAsync.h b/include/QxDao/QxDaoAsync.h new file mode 100644 index 0000000..830aee3 --- /dev/null +++ b/include/QxDao/QxDaoAsync.h @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_DAO_ASYNC_H_ +#define _QX_DAO_ASYNC_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxDaoAsync.h + * \author XDL Team + * \ingroup QxDao + * \brief Helper class to execute SQL queries in another thread (asynchronous way) using qx::IxPersistable interface + */ + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +#include + +#include + +#ifndef Q_MOC_RUN +#include +#include +#endif // Q_MOC_RUN + +namespace qx +{ + namespace dao + { + namespace detail + { + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::QxDaoAsyncParams : all parameters for qx::QxDaoAsync class to execute queries + */ + struct QxDaoAsyncParams + { + + enum dao_action + { + dao_none, + dao_count, + dao_fetch_by_id, + dao_fetch_all, + dao_fetch_by_query, + dao_insert, + dao_update, + dao_save, + dao_delete_by_id, + dao_delete_all, + dao_delete_by_query, + dao_destroy_by_id, + dao_destroy_all, + dao_destroy_by_query, + dao_execute_query, + dao_call_query + }; + + dao_action daoAction; //!< Action to execute into the thread (asynchronous way) + QString className; //!< Classname parameter to execute action (must implement qx::IxPersistable interface) + qx::QxSqlQuery query; //!< Query parameter to execute action + QSqlDatabase *pDatabase; //!< Database parameter to execute action + IxPersistable_ptr pInstance; //!< Current instance parameter to execute action + IxPersistableCollection_ptr pListOfInstances; //!< List of instances fetched by query + QStringList listColumns; //!< List of columns parameter to execute action + QStringList listRelations; //!< List of relationships parameter to execute action + QVariant id; //!< Current instance id parameter to execute action + long daoCount; //!< Dao count value returned by qx::dao::count query + bool useExecBatch; //!< If true then use the QSqlQuery::execBatch() method to improve performance inserting/updating/deleting a list of instances to database (but doesn't fill the last inserted identifier in the C++ instances) + + QxDaoAsyncParams() : daoAction(dao_none), pDatabase(NULL), daoCount(0), useExecBatch(false) { ; } + ~QxDaoAsyncParams() { ; } + }; + + typedef std::shared_ptr QxDaoAsyncParams_ptr; + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::QxDaoAsyncRunner : class with a slot to execute queries in another thread + */ + class QX_DLL_EXPORT QxDaoAsyncRunner : public QObject + { + + Q_OBJECT + + public: + QxDaoAsyncRunner(); + virtual ~QxDaoAsyncRunner(); + + protected: + QSqlError runQuery(qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams); + + Q_SIGNALS: + + void queryFinished(const QSqlError &daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams); + + public Q_SLOTS: + + void onQueryStarted(qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams); + }; + + } // namespace detail + } // namespace dao + + /*! + * \ingroup QxDao + * \brief qx::QxDaoAsync : helper class to execute SQL queries in another thread (asynchronous way) using qx::IxPersistable interface + * + * To use qx::QxDaoAsync helper class : + * 1- be careful to work only with classes implementing qx::IxPersistable interface ; + * 2- create an instance of qx::QxDaoAsync type (for example, a property of a QWidget derived class) ; + * 3- connect a SLOT to the qx::QxDaoAsync::queryFinished() SIGNAL (for example, a SLOT of a QWidget derived class) ; + * 4- run a query using one of qx::QxDaoAsync::asyncXXXX() methods. + * + * For example, with a MyWidget class : + * \code + class MyWidget : public QWidget + { + Q_OBJECT + //... + qx::QxDaoAsync m_daoAsync; + //... + Q_SLOTS: + void onQueryFinished(const QSqlError & daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams); + //... + }; + * \endcode + * And here is the implementation of MyWidget class : + * \code + MyWidget::MyWidget() : QObject() + { + //... + QObject::connect((& m_daoAsync), SIGNAL(queryFinished(const QSqlError &, qx::dao::detail::QxDaoAsyncParams_ptr)), this, SLOT(onQueryFinished(const QSqlError &, qx::dao::detail::QxDaoAsyncParams_ptr))); + //... + } + void MyWidget::onQueryFinished(const QSqlError & daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams) + { + if (! pDaoParams) { return; } + qx::QxSqlQuery query = pDaoParams->query; + if (! daoError.isValid()) { ; } + // If the async query is associated to a simple object, just use 'pDaoParams->pInstance' method + qx::IxPersistable_ptr ptr = pDaoParams->pInstance; + // If the async query is associated to a list of objects, just use 'pDaoParams->pListOfInstances' method + qx::IxPersistableCollection_ptr lst = pDaoParams->pListOfInstances; + //... + } + * \endcode + */ + class QX_DLL_EXPORT QxDaoAsync : public QThread + { + + Q_OBJECT + + protected: + QMutex m_mutex; //!< Mutex => qx::QxDaoAsync is thread-safe + qx::dao::detail::QxDaoAsyncParams_ptr m_pDaoParams; //!< Parameters to execute query + + public: + QxDaoAsync(); + virtual ~QxDaoAsync(); + + bool asyncCount(const QString &className, const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL); + bool asyncFetchById(IxPersistable_ptr pToFetch, const QVariant &id = QVariant(), const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL); + bool asyncFetchAll(const QString &className, const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL); + bool asyncFetchByQuery(const QString &className, const qx::QxSqlQuery &query, const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL); + bool asyncInsert(IxPersistable_ptr pToInsert, const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL); + bool asyncUpdate(IxPersistable_ptr pToUpdate, const qx::QxSqlQuery &query = qx::QxSqlQuery(), const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL); + bool asyncSave(IxPersistable_ptr pToSave, const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL); + bool asyncDeleteById(IxPersistable_ptr pToDelete, const QVariant &id = QVariant(), QSqlDatabase *pDatabase = NULL); + bool asyncDeleteAll(const QString &className, QSqlDatabase *pDatabase = NULL); + bool asyncDeleteByQuery(const QString &className, const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL); + bool asyncDestroyById(IxPersistable_ptr pToDestroy, const QVariant &id = QVariant(), QSqlDatabase *pDatabase = NULL); + bool asyncDestroyAll(const QString &className, QSqlDatabase *pDatabase = NULL); + bool asyncDestroyByQuery(const QString &className, const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL); + bool asyncExecuteQuery(const QString &className, qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL); + bool asyncCallQuery(qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL); + + bool isQueryRunning() const { return (m_pDaoParams.get() != NULL); } + + protected: + virtual void run(); + + void startQuery(); + + Q_SIGNALS: + + void queryStarted(qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams); + void queryFinished(const QSqlError &daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams); + + private Q_SLOTS: + + void onQueryFinished(const QSqlError &daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams); + }; + + typedef std::shared_ptr QxDaoAsync_ptr; + +} // namespace qx + +QX_DLL_EXPORT QDataStream &operator<<(QDataStream &stream, const qx::dao::detail::QxDaoAsyncParams &t) QX_USED; +QX_DLL_EXPORT QDataStream &operator>>(QDataStream &stream, qx::dao::detail::QxDaoAsyncParams &t) QX_USED; + +#endif // _QX_DAO_ASYNC_H_ diff --git a/include/QxDao/QxDaoPointer.h b/include/QxDao/QxDaoPointer.h new file mode 100644 index 0000000..e84f604 --- /dev/null +++ b/include/QxDao/QxDaoPointer.h @@ -0,0 +1,333 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_DAO_POINTER_H_ +#define _QX_DAO_POINTER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxDaoPointer.h + * \author XDL Team + * \ingroup QxDao + * \brief qx::dao::ptr : provide a classic smart-pointer (like boost::shared_ptr or QSharedPointer) with some features associated with QxDao module of QxOrm library + */ + +#include +#include +#include + +#include + +namespace qx +{ + template + QSharedPointer clone_to_qt_shared_ptr(const T &obj); +} // namespace qx + +namespace qx +{ + namespace dao + { + template + class ptr; + } // namespace dao +} // namespace qx + +template +QDataStream &operator<<(QDataStream &stream, const qx::dao::ptr &t); +template +QDataStream &operator>>(QDataStream &stream, qx::dao::ptr &t); + +namespace qx +{ + namespace dao + { + + namespace detail + { + template + struct QxDao_IsDirty; + } // namespace detail + + /*! + * \ingroup QxDao + * \brief qx::dao::ptr : provide a classic smart-pointer (like boost::shared_ptr or QSharedPointer) with some features associated with QxDao module of QxOrm library + * + * QxOrm can be used with smart-pointers of boost and Qt libraries. + * QxOrm smart-pointer is based on QSharedPointer and provides new features with qx::dao::xxx functions of QxDao module. + * qx::dao::ptr keeps automatically values from database. + * So it's possible to detect if an instance has been modified using the method isDirty() : this method can return list of properties changed. + * qx::dao::ptr can also be used with the function qx::dao::update_optimized() to update in database only properties changed. + * qx::dao::ptr can be used with a simple object and with many containers : stl, boost, Qt and qx::QxCollection. + * + * Quick sample using qx::dao::ptr smart-pointer : + * \code + // Test 'isDirty()' method + qx::dao::ptr blog_isdirty = qx::dao::ptr(new blog()); + blog_isdirty->m_id = blog_1->m_id; + daoError = qx::dao::fetch_by_id(blog_isdirty); + qAssert(! daoError.isValid() && ! blog_isdirty.isDirty()); + + blog_isdirty->m_text = "blog property 'text' modified => blog is dirty !!!"; + QStringList lstDiff; bool bDirty = blog_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 1) && (lstDiff.at(0) == "blog_text")); + if (bDirty) { qDebug("[QxOrm] test dirty 1 : blog is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + // Update only property 'm_text' of 'blog_isdirty' + daoError = qx::dao::update_optimized(blog_isdirty); + qAssert(! daoError.isValid() && ! blog_isdirty.isDirty()); + qx::dump(blog_isdirty); + + // Test 'isDirty()' method with a container + typedef qx::dao::ptr< QList > type_lst_author_test_is_dirty; + + type_lst_author_test_is_dirty container_isdirty = type_lst_author_test_is_dirty(new QList()); + daoError = qx::dao::fetch_all(container_isdirty); + qAssert(! daoError.isValid() && ! container_isdirty.isDirty() && (container_isdirty->count() == 3)); + + author_ptr author_ptr_dirty = container_isdirty->at(1); + author_ptr_dirty->m_name = "author name modified at index 1 => container is dirty !!!"; + bDirty = container_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 1)); + if (bDirty) { qDebug("[QxOrm] test dirty 2 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + author_ptr_dirty = container_isdirty->at(2); + author_ptr_dirty->m_birthdate = QDate(1998, 03, 06); + bDirty = container_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 2)); + if (bDirty) { qDebug("[QxOrm] test dirty 3 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + // Update only property 'm_name' at position 1, only property 'm_birthdate' at position 2 and nothing at position 0 + daoError = qx::dao::update_optimized(container_isdirty); + qAssert(! daoError.isValid() && ! container_isdirty.isDirty()); + qx::dump(container_isdirty); + + // Fetch only property 'm_dt_creation' of blog + QStringList lstColumns = QStringList() << "date_creation"; + list_blog lst_blog_with_only_date_creation; + daoError = qx::dao::fetch_all(lst_blog_with_only_date_creation, NULL, lstColumns); + qAssert(! daoError.isValid() && (lst_blog_with_only_date_creation.size() > 0)); + + if ((lst_blog_with_only_date_creation.size() > 0) && (lst_blog_with_only_date_creation[0] != NULL)) + { qAssert(lst_blog_with_only_date_creation[0]->m_text.isEmpty()); } + + qx::dump(lst_blog_with_only_date_creation); + * \endcode + */ + template + class ptr + { + + template + friend QDataStream & ::operator<<(QDataStream & stream, const qx::dao::ptr &t); + template + friend QDataStream & ::operator>>(QDataStream & stream, qx::dao::ptr &t); + + private: + QSharedPointer m_pWork; //!< Default pointer => user works with this pointer + QSharedPointer m_pOriginal; //!< Keep original pointer containing all values from database + + public: + ptr() { ; } + explicit ptr(T *ptr) : m_pWork(ptr) { ; } + explicit ptr(T *ptr, T *original) : m_pWork(ptr), m_pOriginal(original) { ; } + ptr(const qx::dao::ptr &other) : m_pWork(other.m_pWork), m_pOriginal(other.m_pOriginal) { ; } + ptr(const QSharedPointer &other) : m_pWork(other) { ; } + ptr(const QSharedPointer &other, const QSharedPointer &original) : m_pWork(other), m_pOriginal(original) { ; } + ptr(const QWeakPointer &other) : m_pWork(other) { ; } + ptr(const QWeakPointer &other, const QWeakPointer &original) : m_pWork(other), m_pOriginal(original) { ; } + virtual ~ptr() { ; } + + template + ptr(T *ptr, Deleter deleter) : m_pWork(ptr, deleter) { ; } + template + ptr(T *ptr, T *original, Deleter deleter) : m_pWork(ptr, deleter), m_pOriginal(original) { ; } + + template + ptr(const qx::dao::ptr &other) : m_pWork(qSharedPointerCast(other.m_pWork)), m_pOriginal(qSharedPointerCast(other.m_pOriginal)) { ; } + template + ptr(const QSharedPointer &other) : m_pWork(qSharedPointerCast(other)) { ; } + template + ptr(const QSharedPointer &other, const QSharedPointer &original) : m_pWork(qSharedPointerCast(other)), m_pOriginal(qSharedPointerCast(original)) { ; } + template + ptr(const QWeakPointer &other) : m_pWork(qSharedPointerCast(other.toStrongRef())) { ; } + template + ptr(const QWeakPointer &other, const QWeakPointer &original) : m_pWork(qSharedPointerCast(other.toStrongRef())), m_pOriginal(qSharedPointerCast(original.toStrongRef())) { ; } + + qx::dao::ptr &operator=(const qx::dao::ptr &other) + { + m_pWork = other.m_pWork; + m_pOriginal = other.m_pOriginal; + return (*this); + } + qx::dao::ptr &operator=(const QSharedPointer &other) + { + m_pWork = other; + m_pOriginal.clear(); + return (*this); + } + qx::dao::ptr &operator=(const QWeakPointer &other) + { + m_pWork = other; + m_pOriginal.clear(); + return (*this); + } + + template + qx::dao::ptr &operator=(const qx::dao::ptr &other) + { + m_pWork = qSharedPointerCast(other.m_pWork); + m_pOriginal = qSharedPointerCast(other.m_pOriginal); + return (*this); + } + template + qx::dao::ptr &operator=(const QSharedPointer &other) + { + m_pWork = qSharedPointerCast(other); + m_pOriginal.clear(); + return (*this); + } + template + qx::dao::ptr &operator=(const QWeakPointer &other) + { + m_pWork = qSharedPointerCast(other.toStrongRef()); + m_pOriginal.clear(); + return (*this); + } + + inline T *get() const { return m_pWork.data(); } + inline T *getOriginal() const { return m_pOriginal.data(); } + inline T *data() const { return m_pWork.data(); } + inline T *dataOriginal() const { return m_pOriginal.data(); } + inline bool isNull() const { return m_pWork.isNull(); } + inline operator bool() const { return (!m_pWork.isNull()); } + inline bool operator!() const { return m_pWork.isNull(); } + inline T &operator*() const { return (*m_pWork.data()); } + inline T *operator->() const { return m_pWork.data(); } + inline void clear() + { + m_pWork.clear(); + m_pOriginal.clear(); + } + inline void reset() + { + m_pWork.clear(); + m_pOriginal.clear(); + } + inline void reset(const QSharedPointer &ptr) + { + m_pWork = ptr; + m_pOriginal.clear(); + } + inline void resetOriginal(const QSharedPointer &ptr) { m_pOriginal = ptr; } + inline bool isDirty() const + { + QStringList lstDiff; + return isDirty(lstDiff); + } + inline QSharedPointer toQtSharedPointer() const { return m_pWork; } + inline void saveToOriginal() + { + m_pOriginal.clear(); + if (m_pWork) + { + m_pOriginal = qx::clone_to_qt_shared_ptr(*m_pWork); + } + } + inline void restoreFromOriginal() + { + m_pWork.clear(); + if (m_pOriginal) + { + m_pWork = qx::clone_to_qt_shared_ptr(*m_pOriginal); + } + } + + template + qx::dao::ptr staticCast() const { return qx::dao::ptr(m_pWork.template staticCast(), m_pOriginal.template staticCast()); } + template + qx::dao::ptr dynamicCast() const { return qx::dao::ptr(m_pWork.template dynamicCast(), m_pOriginal.template dynamicCast()); } + template + qx::dao::ptr constCast() const { return qx::dao::ptr(m_pWork.template constCast(), m_pOriginal.template constCast()); } + + bool isDirty(QStringList &lstDiff) const + { + lstDiff.clear(); + if (m_pWork.isNull() || m_pOriginal.isNull()) + { + lstDiff.append("*"); + return true; + } + if (m_pWork == m_pOriginal) + { + return false; + } + qx::dao::detail::QxDao_IsDirty::compare((*m_pWork), (*m_pOriginal), lstDiff); + return (!lstDiff.isEmpty()); + } + }; + + } // namespace dao +} // namespace qx + +template +bool operator==(const qx::dao::ptr &ptr1, const qx::dao::ptr &ptr2) { return (ptr1.toQtSharedPointer() == ptr2.toQtSharedPointer()); } +template +bool operator!=(const qx::dao::ptr &ptr1, const qx::dao::ptr &ptr2) { return (ptr1.toQtSharedPointer() != ptr2.toQtSharedPointer()); } +template +bool operator==(const qx::dao::ptr &ptr1, const X *ptr2) { return (ptr1.toQtSharedPointer() == ptr2); } +template +bool operator!=(const qx::dao::ptr &ptr1, const X *ptr2) { return (ptr1.toQtSharedPointer() != ptr2); } +template +bool operator==(const T *ptr1, const qx::dao::ptr &ptr2) { return (ptr1 == ptr2.toQtSharedPointer()); } +template +bool operator!=(const T *ptr1, const qx::dao::ptr &ptr2) { return (ptr1 != ptr2.toQtSharedPointer()); } + +template +QDataStream &operator<<(QDataStream &stream, const qx::dao::ptr &t) +{ + stream << t.m_pWork; + stream << t.m_pOriginal; + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, qx::dao::ptr &t) +{ + stream >> t.m_pWork; + stream >> t.m_pOriginal; + return stream; +} + +#endif // _QX_DAO_POINTER_H_ diff --git a/include/QxDao/QxDaoStrategy.h b/include/QxDao/QxDaoStrategy.h new file mode 100644 index 0000000..caef20c --- /dev/null +++ b/include/QxDao/QxDaoStrategy.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_DAO_STRATEGY_H_ +#define _QX_DAO_STRATEGY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxDaoStrategy.h + * \author XDL Team + * \ingroup QxDao + * \brief Class inheritance strategy and database (Concrete Table Inheritance is the default strategy used by QxOrm library) + */ + +namespace qx +{ + namespace dao + { + + /*! + * \ingroup QxDao + * \brief qx::dao::strategy : class inheritance strategy and database (Concrete Table Inheritance is the default strategy used by QxOrm library) + * + * With ORM tools, there is usually 3 strategies to manage inheritance and database : + * - Single Table Inheritance + * - Class Table Inheritance + * - Concrete Table Inheritance + * + * QxOrm works by default with Concrete Table Inheritance strategy (others are not supported yet). + * Many tutorials and forums are available on internet to more details about ORM inheritance and database. + * You can find a sample in the directory ./test/qxDllSample/dll2/ with the class BaseClassTrigger. + */ + struct strategy + { + + enum inheritance + { + single_table_inheritance, + class_table_inheritance, + concrete_table_inheritance + }; + }; + + } // namespace dao +} // namespace qx + +#endif // _QX_DAO_STRATEGY_H_ diff --git a/include/QxDao/QxDaoThrowable.h b/include/QxDao/QxDaoThrowable.h new file mode 100644 index 0000000..d9272a2 --- /dev/null +++ b/include/QxDao/QxDaoThrowable.h @@ -0,0 +1,955 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_DAO_THROWABLE_H_ +#define _QX_DAO_THROWABLE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxDaoThrowable.h + * \author XDL Team + * \ingroup QxDao + * \brief Same functions as qx::dao namespace, but throw a qx::dao::sql_error exception when a SQL error occurred (instead of returning a QSqlError instance) + */ + +#include +#include + +namespace qx +{ + namespace dao + { + + /*! + * \ingroup QxDao + * \brief Same functions as qx::dao namespace, but throw a qx::dao::sql_error exception when a SQL error occurred (instead of returning a QSqlError instance) + */ + namespace throwable + { + + /// @cond TO_AVOID_DOXYGEN_WARNINGS + + /*! + * \ingroup QxDao + * \brief Return the number of lines in the table (database) mapped to the C++ class T (registered into QxOrm context) and filtered by a user SQL query + * \param lCount Output parameter with the number of lines in the table associated to the SQL query + * \param query Define a user SQL query added to default SQL query builded by QxOrm library (optional parameter) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * + * qx::dao::throwable::count() execute following SQL query :
    + * SELECT COUNT(*) FROM my_table + WHERE my_query... + */ + template + inline void count(long &lCount, const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_Count::count(lCount, query, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Return the number of lines in the table (database) mapped to the C++ class T (registered into QxOrm context) and filtered by a user SQL query (with possibility to add relations to SQL query) + * \param lCount Output parameter with the number of lines in the table associated to the SQL query + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) + * \param query Define a user SQL query added to default SQL query builded by QxOrm library (optional parameter) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * + * qx::dao::throwable::count_with_relation() execute following SQL query :
    + * SELECT COUNT(*) FROM my_table + XXX_JOINS_XXX + WHERE my_query... + */ + template + inline void count_with_relation(long &lCount, const QStringList &relation, const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_Count_WithRelation::count(lCount, relation, query, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Insert an element or a list of elements into database + * \param t Element (or list of elements) to be inserted into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance inserting a list of instances to database (but doesn't fill the last inserted identifier in the C++ instances) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::insert() execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + */ + template + inline void insert(T &t, QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + QSqlError err = qx::dao::detail::QxDao_Insert::insert(t, pDatabase, bUseExecBatch); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Insert (if no exist) or update (if already exist) an element or a list of elements into database + * \param t Element (or list of elements) to be inserted (if no exist) or updated (if already exist) into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::save() execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + *
    or (if already exist into database) :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + */ + template + inline void save(T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_Save::save(t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Delete a line (or list of lines) of a table (database) mapped to a C++ object of type T (registered into QxOrm context) + * \param t Element (or list of elements) to be deleted into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance deleting a list of instances in database + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::delete_by_id() execute following SQL query :
    + * DELETE FROM my_table WHERE my_id = ?
    + *
    + * If a soft delete behavior is defined for class T, qx::dao::throwable::delete_by_id() execute following SQL query :
    + * UPDATE my_table SET is_deleted='1' WHERE my_id = ? + */ + template + inline void delete_by_id(T &t, QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + QSqlError err = qx::dao::detail::QxDao_DeleteById::deleteById(t, pDatabase, true, bUseExecBatch); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Destroy a line (or list of lines) of a table (database) mapped to a C++ object of type T (registered into QxOrm context), even if a soft delete behavior is defined for class T + * \param t Element (or list of elements) to be destroyed into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance destroying a list of instances in database + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::destroy_by_id() execute following SQL query :
    + * DELETE FROM my_table WHERE my_id = ? + */ + template + inline void destroy_by_id(T &t, QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + QSqlError err = qx::dao::detail::QxDao_DeleteById::deleteById(t, pDatabase, false, bUseExecBatch); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Delete all lines of a table (database) mapped to a C++ class T (registered into QxOrm context) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::delete_all() execute following SQL query :
    + * DELETE FROM my_table
    + *
    + * If a soft delete behavior is defined for class T, qx::dao::throwable::delete_all() execute following SQL query :
    + * UPDATE my_table SET is_deleted='1' + */ + template + inline void delete_all(QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_DeleteAll::deleteAll("", pDatabase, true); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Destroy all lines of a table (database) mapped to a C++ class T (registered into QxOrm context), even if a soft delete behavior is defined for class T + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::destroy_all() execute following SQL query :
    + * DELETE FROM my_table + */ + template + inline void destroy_all(QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_DeleteAll::deleteAll("", pDatabase, false); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Delete all lines of a table (database) mapped to a C++ class T (registered into QxOrm context) and filtered by a user SQL query + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::delete_by_query() execute following SQL query :
    + * DELETE FROM my_table + WHERE my_query...
    + *
    + * If a soft delete behavior is defined for class T, qx::dao::throwable::delete_by_query() execute following SQL query :
    + * UPDATE my_table SET is_deleted='1' + WHERE my_query... + */ + template + inline void delete_by_query(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_DeleteAll::deleteAll(query, pDatabase, true); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Destroy all lines of a table (database) mapped to a C++ class T (registered into QxOrm context) and filtered by a user SQL query, even if a soft delete behavior is defined for class T + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::destroy_by_query() execute following SQL query :
    + * DELETE FROM my_table + WHERE my_query... + */ + template + inline void destroy_by_query(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_DeleteAll::deleteAll(query, pDatabase, false); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Create a table into database (with all columns) mapped to a C++ class T (registered into QxOrm context) : be careful, this function can be used only with a SQLite database to create examples or prototypes; For other databases, it is recommended to use QxEntityEditor application or to manage the database schema with an external tool provided by the SGBD (SQLite Manager for SQLite, pgAdmin for PostgreSQL, MySQL Workbench for MySQL, etc...) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::create_table() execute following SQL query :
    + * CREATE TABLE my_table (my_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, my_column_1 TEXT, my_column_2 TEXT, etc.) + */ + template + inline void create_table(QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_CreateTable::createTable(pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Fetch an object t (retrieve all its properties) of type T (registered into QxOrm context) mapped to a table in the database (t must have a valid id before to be fetched without error) + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) : use "|" separator to put many relationships keys into this parameter + * \param t Instance (with a valid id) to be fetched (retrieve all properties from database) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::fetch_by_id_with_relation() execute following SQL query :
    + * SELECT * FROM my_table WHERE my_id = ? + */ + template + inline void fetch_by_id_with_relation(const QString &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_FetchById_WithRelation::fetchById(relation, t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Fetch an object t (retrieve all its properties) of type T (registered into QxOrm context) mapped to a table in the database (t must have a valid id before to be fetched without error) + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) + * \param t Instance (with a valid id) to be fetched (retrieve all properties from database) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::fetch_by_id_with_relation() execute following SQL query :
    + * SELECT * FROM my_table WHERE my_id = ? + */ + template + inline void fetch_by_id_with_relation(const QStringList &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_FetchById_WithRelation::fetchById(relation, t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Fetch an object t (retrieve all its properties and relationships) of type T (registered into QxOrm context) mapped to a table in the database (t must have a valid id before to be fetched without error) + * \param t Instance (with a valid id) to be fetched (retrieve all properties and relationships from database) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::fetch_by_id_with_all_relation() execute following SQL query :
    + * SELECT * FROM my_table WHERE my_id = ? + */ + template + inline void fetch_by_id_with_all_relation(T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_FetchById_WithRelation::fetchById("*", t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Fetch a list of objects (retrieve all elements and properties associated) of type T (container registered into QxOrm context) mapped to a table in the database + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) : use "|" separator to put many relationships keys into this parameter + * \param t Container to be fetched (retrieve all elements and properties associated); t is cleared before executing SQL query + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::fetch_all_with_relation() execute following SQL query :
    + * SELECT * FROM my_table + */ + template + inline void fetch_all_with_relation(const QString &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_FetchAll_WithRelation::fetchAll(relation, "", t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Fetch a list of objects (retrieve all elements and properties associated) of type T (container registered into QxOrm context) mapped to a table in the database + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) + * \param t Container to be fetched (retrieve all elements and properties associated); t is cleared before executing SQL query + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::fetch_all_with_relation() execute following SQL query :
    + * SELECT * FROM my_table + */ + template + inline void fetch_all_with_relation(const QStringList &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_FetchAll_WithRelation::fetchAll(relation, "", t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Fetch a list of objects (retrieve all elements and properties + all relationships associated) of type T (container registered into QxOrm context) mapped to a table in the database + * \param t Container to be fetched (retrieve all elements and properties + all relationships associated); t is cleared before executing SQL query + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::fetch_all_with_all_relation() execute following SQL query :
    + * SELECT * FROM my_table + */ + template + inline void fetch_all_with_all_relation(T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_FetchAll_WithRelation::fetchAll("*", "", t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Fetch a list of objects (retrieve all elements and properties associated) of type T (container registered into QxOrm context) mapped to a table in the database and filtered by a user SQL query + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) : use "|" separator to put many relationships keys into this parameter + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param t Container to be fetched (retrieve all elements and properties associated); t is cleared before executing SQL query + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::fetch_by_query_with_relation() execute following SQL query :
    + * SELECT * FROM my_table + WHERE my_query... + */ + template + inline void fetch_by_query_with_relation(const QString &relation, const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_FetchAll_WithRelation::fetchAll(relation, query, t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Fetch a list of objects (retrieve all elements and properties associated) of type T (container registered into QxOrm context) mapped to a table in the database and filtered by a user SQL query + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param t Container to be fetched (retrieve all elements and properties associated); t is cleared before executing SQL query + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::fetch_by_query_with_relation() execute following SQL query :
    + * SELECT * FROM my_table + WHERE my_query... + */ + template + inline void fetch_by_query_with_relation(const QStringList &relation, const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_FetchAll_WithRelation::fetchAll(relation, query, t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Fetch a list of objects (retrieve all elements and properties + all relationships associated) of type T (container registered into QxOrm context) mapped to a table in the database and filtered by a user SQL query + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param t Container to be fetched (retrieve all elements and properties + all relationships associated); t is cleared before executing SQL query + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::fetch_by_query_with_all_relation() execute following SQL query :
    + * SELECT * FROM my_table + WHERE my_query... + */ + template + inline void fetch_by_query_with_all_relation(const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_FetchAll_WithRelation::fetchAll("*", query, t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Insert an element and its relationships (or a list of elements + relationships) into database + * \param relation List of relationships keys to be inserted in others tables of database : use "|" separator to put many relationships keys into this parameter + * \param t Element (or list of elements) to be inserted into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::insert_with_relation() execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + */ + template + inline void insert_with_relation(const QString &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_Insert_WithRelation::insert(relation, t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Insert an element and its relationships (or a list of elements + relationships) into database + * \param relation List of relationships keys to be inserted in others tables of database + * \param t Element (or list of elements) to be inserted into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::insert_with_relation() execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + */ + template + inline void insert_with_relation(const QStringList &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_Insert_WithRelation::insert(relation, t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Insert an element and all its relationships (or a list of elements + all relationships) into database + * \param t Element (or list of elements) to be inserted into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::insert_with_all_relation() execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + */ + template + inline void insert_with_all_relation(T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_Insert_WithRelation::insert("*", t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Update an element and its relationships (or a list of elements + relationships) into database + * \param relation List of relationships keys to be updated in others tables of database : use "|" separator to put many relationships keys into this parameter + * \param t Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::update_with_relation() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + */ + template + inline void update_with_relation(const QString &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_Update_WithRelation::update(relation, "", t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Update an element and its relationships (or a list of elements + relationships) into database (adding a user SQL query to the default SQL query builded by QxOrm library) + * \param relation List of relationships keys to be updated in others tables of database : use "|" separator to put many relationships keys into this parameter + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param t Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::update_by_query_with_relation() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + WHERE my_query... + */ + template + inline void update_by_query_with_relation(const QString &relation, const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_Update_WithRelation::update(relation, query, t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Update an element and its relationships (or a list of elements + relationships) into database + * \param relation List of relationships keys to be updated in others tables of database + * \param t Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::update_with_relation() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + */ + template + inline void update_with_relation(const QStringList &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_Update_WithRelation::update(relation, "", t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Update an element and its relationships (or a list of elements + relationships) into database (adding a user SQL query to the default SQL query builded by QxOrm library) + * \param relation List of relationships keys to be updated in others tables of database + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param t Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::update_by_query_with_relation() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + WHERE my_query... + */ + template + inline void update_by_query_with_relation(const QStringList &relation, const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_Update_WithRelation::update(relation, query, t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Update an element and all its relationships (or a list of elements + all relationships) into database + * \param t Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::update_with_all_relation() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + */ + template + inline void update_with_all_relation(T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_Update_WithRelation::update("*", "", t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Update an element and all its relationships (or a list of elements + all relationships) into database (adding a user SQL query to the default SQL query builded by QxOrm library) + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param t Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::update_by_query_with_all_relation() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + WHERE my_query... + */ + template + inline void update_by_query_with_all_relation(const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_Update_WithRelation::update("*", query, t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Insert (if no exist) or update (if already exist) an element and its relationships (or a list of elements + relationships) into database + * \param relation List of relationships keys to be inserted or updated in others tables of database : use "|" separator to put many relationships keys into this parameter + * \param t Element (or list of elements) to be inserted (if no exist) or updated (if already exist) into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::save_with_relation() execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + *
    or (if already exist into database) :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + */ + template + inline void save_with_relation(const QString &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_Save_WithRelation::save(relation, t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Insert (if no exist) or update (if already exist) an element and its relationships (or a list of elements + relationships) into database + * \param relation List of relationships keys to be inserted or updated in others tables of database + * \param t Element (or list of elements) to be inserted (if no exist) or updated (if already exist) into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::save_with_relation() execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + *
    or (if already exist into database) :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + */ + template + inline void save_with_relation(const QStringList &relation, T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_Save_WithRelation::save(relation, t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Insert (if no exist) or update (if already exist) an element and all its relationships (or a list of elements + all relationships) into database + * \param t Element (or list of elements) to be inserted (if no exist) or updated (if already exist) into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::save_with_all_relation() execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + *
    or (if already exist into database) :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + */ + template + inline void save_with_all_relation(T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_Save_WithRelation::save("*", t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Insert (if no exist) or update (if already exist) recursively an element and all levels of relationships (or a list of elements + all levels of relationships) into database, useful to save a tree structure for example + * \param t Element (or list of elements) to be inserted (if no exist) or updated (if already exist) into database + * \param eSaveMode To improve performance, use this parameter to indicate if you just want to insert or update all elements in database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param pRelationParams Keep this parameter as NULL, it is used internally by QxOrm library to iterate over each level of relationship + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::save_with_relation_recursive() execute following SQL query :
    + * INSERT INTO my_table (my_column_1, my_column_2, etc.) VALUES (?, ?, etc.) + *
    or (if already exist into database) :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc.
    + *
    + * Note : to improve performance, and if you know that you are just inserting or updating items in database, you can use the parameter eSaveMode :
    + * - qx::dao::save_mode::e_check_insert_or_update : check before saving if item already exists in database ;
    + * - qx::dao::save_mode::e_insert_only : only insert items in database (use only 1 SQL query to insert collection of items) ;
    + * - qx::dao::save_mode::e_update_only : only update items in database (use only 1 SQL query to update collection of items). + */ + template + inline void save_with_relation_recursive(T &t, qx::dao::save_mode::e_save_mode eSaveMode = qx::dao::save_mode::e_check_insert_or_update, QSqlDatabase *pDatabase = NULL, qx::QxSqlRelationParams *pRelationParams = NULL) + { + QSqlError err = qx::dao::detail::QxDao_Save_WithRelation_Recursive::save(t, eSaveMode, pDatabase, pRelationParams); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Fetch an object t (retrieve all its properties) of type T (registered into QxOrm context) mapped to a table in the database (t must have a valid id before to be fetched without error) + * \param t Instance (with a valid id) to be fetched (retrieve all properties from database) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param columns List of database table columns (mapped to properties of C++ class T) to be fetched (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::fetch_by_id() execute following SQL query :
    + * SELECT * FROM my_table WHERE my_id = ? + */ + template + inline void fetch_by_id(T &t, QSqlDatabase *pDatabase = NULL, const QStringList &columns = QStringList()) + { + QSqlError err = qx::dao::detail::QxDao_FetchById::fetchById(t, pDatabase, columns); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Fetch a list of objects (retrieve all elements and properties associated) of type T (container registered into QxOrm context) mapped to a table in the database + * \param t Container to be fetched (retrieve all elements and properties associated); t is cleared before executing SQL query + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param columns List of database table columns (mapped to properties of C++ class T) to be fetched (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::fetch_all() execute following SQL query :
    + * SELECT * FROM my_table + */ + template + inline void fetch_all(T &t, QSqlDatabase *pDatabase = NULL, const QStringList &columns = QStringList()) + { + QSqlError err = qx::dao::detail::QxDao_FetchAll::fetchAll("", t, pDatabase, columns); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Fetch a list of objects (retrieve all elements and properties associated) of type T (container registered into QxOrm context) mapped to a table in the database and filtered by a user SQL query + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param t Container to be fetched (retrieve all elements and properties associated); t is cleared before executing SQL query + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param columns List of database table columns (mapped to properties of C++ class T) to be fetched (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::fetch_by_query() execute following SQL query :
    + * SELECT * FROM my_table + WHERE my_query... + */ + template + inline void fetch_by_query(const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase = NULL, const QStringList &columns = QStringList()) + { + QSqlError err = qx::dao::detail::QxDao_FetchAll::fetchAll(query, t, pDatabase, columns); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Update an element or a list of elements into database + * \param t Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param columns List of database table columns (mapped to properties of C++ class T) to be updated (optional parameter) + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance updating a list of instances in database + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::update() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + */ + template + inline void update(T &t, QSqlDatabase *pDatabase = NULL, const QStringList &columns = QStringList(), bool bUseExecBatch = false) + { + QSqlError err = qx::dao::detail::QxDao_Update::update("", t, pDatabase, columns, bUseExecBatch); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Update an element or a list of elements into database (adding a user SQL query to the default SQL query builded by QxOrm library) + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param t Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param columns List of database table columns (mapped to properties of C++ class T) to be updated (optional parameter) + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance updating a list of instances in database + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::update_by_query() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + WHERE my_query... + */ + template + inline void update_by_query(const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase = NULL, const QStringList &columns = QStringList(), bool bUseExecBatch = false) + { + QSqlError err = qx::dao::detail::QxDao_Update::update(query, t, pDatabase, columns, bUseExecBatch); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Update only modified fields/properties of an element or a list of elements into database (using is dirty pattern and qx::dao::ptr smart-pointer) + * \param ptr Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance updating a list of instances in database + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::update_optimized() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + */ + template + inline void update_optimized(qx::dao::ptr &ptr, QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + QSqlError err = qx::dao::detail::QxDao_Update_Optimized::update_optimized("", ptr, pDatabase, bUseExecBatch); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Update only modified fields/properties of an element or a list of elements into database (using is dirty pattern and qx::dao::ptr smart-pointer), adding a user SQL query to the default SQL query builded by QxOrm library + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param ptr Element (or list of elements) to be updated into database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance updating a list of instances in database + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + * + * qx::dao::throwable::update_optimized_by_query() execute following SQL query :
    + * UPDATE my_table SET my_column_1 = ?, my_column_2 = ?, etc. + WHERE my_query... + */ + template + inline void update_optimized_by_query(const qx::QxSqlQuery &query, qx::dao::ptr &ptr, QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + QSqlError err = qx::dao::detail::QxDao_Update_Optimized::update_optimized(query, ptr, pDatabase, bUseExecBatch); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /*! + * \ingroup QxDao + * \brief Execute a custom SQL query or a stored procedure, all columns that can be mapped to the instance of type T will be fetched automatically + * \param query Define a custom SQL query or a stored procedure to call + * \param t Instance of type T, all columns that can be mapped to this instance will be fetched automatically + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Throw a qx::dao::sql_error exception when a SQL error occurred + */ + template + inline void execute_query(qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::detail::QxDao_ExecuteQuery::executeQuery(query, t, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + inline void call_query(qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) + { + QSqlError err = qx::dao::call_query(query, pDatabase); + if (err.isValid()) + { + throw qx::dao::sql_error(err); + } + } + + /// @endcond + + } // namespace throwable + } // namespace dao +} // namespace qx + +#endif // _QX_DAO_THROWABLE_H_ diff --git a/include/QxDao/QxDao_Impl.h b/include/QxDao/QxDao_Impl.h new file mode 100644 index 0000000..24b9808 --- /dev/null +++ b/include/QxDao/QxDao_Impl.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_DAO_IMPL_H_ +#define _QX_DAO_IMPL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _QX_ENABLE_MONGODB +#include +#ifndef _QX_NO_JSON +#include +#include +#endif // _QX_NO_JSON +#endif // _QX_ENABLE_MONGODB + +#include "../../inl/QxDao/QxDao_Helper.inl" +#include "../../inl/QxDao/QxDao_Count.inl" +#include "../../inl/QxDao/QxDao_FetchById.inl" +#include "../../inl/QxDao/QxDao_FetchById_WithRelation.inl" +#include "../../inl/QxDao/QxDao_FetchAll.inl" +#include "../../inl/QxDao/QxDao_FetchAll_WithRelation.inl" +#include "../../inl/QxDao/QxDao_Insert.inl" +#include "../../inl/QxDao/QxDao_Insert_WithRelation.inl" +#include "../../inl/QxDao/QxDao_Update.inl" +#include "../../inl/QxDao/QxDao_Update_Optimized.inl" +#include "../../inl/QxDao/QxDao_Update_WithRelation.inl" +#include "../../inl/QxDao/QxDao_Save.inl" +#include "../../inl/QxDao/QxDao_Save_WithRelation.inl" +#include "../../inl/QxDao/QxDao_Save_WithRelation_Recursive.inl" +#include "../../inl/QxDao/QxDao_DeleteById.inl" +#include "../../inl/QxDao/QxDao_DeleteAll.inl" +#include "../../inl/QxDao/QxDao_Exist.inl" +#include "../../inl/QxDao/QxDao_CreateTable.inl" +#include "../../inl/QxDao/QxDao_Trigger.inl" +#include "../../inl/QxDao/QxDao_ExecuteQuery.inl" + +#endif // _QX_DAO_IMPL_H_ diff --git a/include/QxDao/QxDao_IsDirty.h b/include/QxDao/QxDao_IsDirty.h new file mode 100644 index 0000000..230b471 --- /dev/null +++ b/include/QxDao/QxDao_IsDirty.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_DAO_IS_DIRTY_H_ +#define _QX_DAO_IS_DIRTY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#include +#include + +#include +#include +#include +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + inline void is_dirty(const T &obj1, const T &obj2, QStringList &lstDiff); + + template + struct QxDao_IsDirty_Generic + { + + static void compare(const T &obj1, const T &obj2, QStringList &lstDiff) + { + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + + qx::QxSqlQueryBuilder_Count builder; + builder.init(); + qx::IxDataMember *pId = builder.getDataId(); + if (pId && (!pId->isEqual((&obj1), (&obj2)))) + { + lstDiff.append(pId->getKey()); + } + + long l = 0; + qx::IxDataMember *p = NULL; + while ((p = builder.nextData(l))) + { + if (p && (!p->isEqual((&obj1), (&obj2)))) + { + lstDiff.append(p->getKey()); + } + } + } + }; + + template + struct QxDao_IsDirty_Container + { + + static void compare(const T &obj1, const T &obj2, QStringList &lstDiff) + { + if (qx::trait::generic_container::size(obj1) <= 0) + { + return; + } + if (qx::trait::generic_container::size(obj1) != qx::trait::generic_container::size(obj2)) + { + lstDiff.append("*"); + return; + } + + long lCurrIndex = 0; + typename T::const_iterator it2 = obj2.begin(); + + for (typename T::const_iterator it1 = obj1.begin(); it1 != obj1.end(); ++it1) + { + QStringList lstDiffItem; + qx::dao::detail::is_dirty((*it1), (*it2), lstDiffItem); + if (lstDiffItem.count() > 0) + { + lstDiff.append(QString::number(lCurrIndex) + "|" + lstDiffItem.join("|")); + } + ++lCurrIndex; + ++it2; + } + } + }; + + template + struct QxDao_IsDirty_Ptr + { + + static void compare(const T &obj1, const T &obj2, QStringList &lstDiff) + { + qx::dao::detail::is_dirty((*obj1), (*obj2), lstDiff); + } + }; + + template + struct QxDao_IsDirty + { + + static void compare(const T &obj1, const T &obj2, QStringList &lstDiff) + { + typedef typename std::conditional::value, qx::dao::detail::QxDao_IsDirty_Ptr, qx::dao::detail::QxDao_IsDirty_Generic>::type type_dao_1; + typedef typename std::conditional::value, qx::dao::detail::QxDao_IsDirty_Ptr, type_dao_1>::type type_dao_2; + typedef typename std::conditional::value, qx::dao::detail::QxDao_IsDirty_Container, type_dao_2>::type type_dao_3; + type_dao_3::compare(obj1, obj2, lstDiff); + } + }; + + template + inline void is_dirty(const T &obj1, const T &obj2, QStringList &lstDiff) + { + return qx::dao::detail::QxDao_IsDirty::compare(obj1, obj2, lstDiff); + } + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _QX_DAO_IS_DIRTY_H_ diff --git a/include/QxDao/QxDateNeutral.h b/include/QxDao/QxDateNeutral.h new file mode 100644 index 0000000..c15725b --- /dev/null +++ b/include/QxDao/QxDateNeutral.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_DATE_NEUTRAL_H_ +#define _QX_DATE_NEUTRAL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxDateNeutral.h + * \author XDL Team + * \ingroup QxDao + * \brief Helper class to store a date value into database under neutral format (YYYYMMDD) => cross database compatibility + */ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#include +#include +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#include +#include + +#include + +#include + +namespace qx +{ + class QxDateNeutral; +} // namespace qx + +QX_DLL_EXPORT QDataStream &operator<<(QDataStream &stream, const qx::QxDateNeutral &t) QX_USED; +QX_DLL_EXPORT QDataStream &operator>>(QDataStream &stream, qx::QxDateNeutral &t) QX_USED; + +namespace qx +{ + + /*! + * \ingroup QxDao + * \brief qx::QxDateNeutral : helper class to store a date value into database under neutral format (YYYYMMDD) => cross database compatibility + */ + class QxDateNeutral + { + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + friend class boost::serialization::access; +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + friend QX_DLL_EXPORT QDataStream & ::operator<<(QDataStream &stream, const qx::QxDateNeutral &t); + friend QX_DLL_EXPORT QDataStream & ::operator>>(QDataStream &stream, qx::QxDateNeutral &t); + + private: + QDate m_date; //!< Data value under QDate format from Qt library + QString m_neutral; //!< Data value under neutral format 'yyyyMMdd' + + public: + QxDateNeutral() { ; } + explicit QxDateNeutral(const QDate &date) : m_date(date) { update(); } + explicit QxDateNeutral(const QString &neutral) : m_neutral(neutral) { update(); } + virtual ~QxDateNeutral() { ; } + + inline QDate toDate() const { return m_date; } + inline QString toNeutral() const { return m_neutral; } + inline bool isValid() const { return m_date.isValid(); } + + inline void setDate(const QDate &date) + { + m_neutral = ""; + m_date = date; + update(); + } + inline void setNeutral(const QString &neutral) + { + m_date = QDate(); + m_neutral = neutral; + update(); + } + + static QxDateNeutral fromDate(const QDate &date) { return QxDateNeutral(date); } + static QxDateNeutral fromNeutral(const QString &neutral) { return QxDateNeutral(neutral); } + + private: + static inline const char *format() { return "yyyyMMdd"; } + + void update() + { + if (m_neutral.isEmpty() && !m_date.isValid()) + { + return; + } + else if (m_date.isValid()) + { + m_neutral = m_date.toString(format()); + } + else + { + qAssert(m_neutral.size() == QString(format()).size()); + m_date = QDate::fromString(m_neutral, format()); + qAssert(m_date.isValid()); + } + } + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + template + void serialize(Archive &ar, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("date_neutral", m_neutral); + if (Archive::is_loading::value) + { + m_date = QDate(); + update(); + } + } +#endif // _QX_ENABLE_BOOST_SERIALIZATION + }; + +} // namespace qx + +QX_REGISTER_CLASS_NAME(qx::QxDateNeutral) + +#endif // _QX_DATE_NEUTRAL_H_ diff --git a/include/QxDao/QxDateTimeNeutral.h b/include/QxDao/QxDateTimeNeutral.h new file mode 100644 index 0000000..1f487a3 --- /dev/null +++ b/include/QxDao/QxDateTimeNeutral.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_DATE_TIME_NEUTRAL_H_ +#define _QX_DATE_TIME_NEUTRAL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxDateTimeNeutral.h + * \author XDL Team + * \ingroup QxDao + * \brief Helper class to store a date-time value into database under neutral format (YYYYMMDDHHMMSS) => cross database compatibility + */ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#include +#include +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#include +#include + +#include + +#include + +namespace qx +{ + class QxDateTimeNeutral; +} // namespace qx + +QX_DLL_EXPORT QDataStream &operator<<(QDataStream &stream, const qx::QxDateTimeNeutral &t) QX_USED; +QX_DLL_EXPORT QDataStream &operator>>(QDataStream &stream, qx::QxDateTimeNeutral &t) QX_USED; + +namespace qx +{ + + /*! + * \ingroup QxDao + * \brief qx::QxDateTimeNeutral : helper class to store a date-time value into database under neutral format (YYYYMMDDHHMMSS) => cross database compatibility + */ + class QxDateTimeNeutral + { + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + friend class boost::serialization::access; +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + friend QX_DLL_EXPORT QDataStream & ::operator<<(QDataStream &stream, const qx::QxDateTimeNeutral &t); + friend QX_DLL_EXPORT QDataStream & ::operator>>(QDataStream &stream, qx::QxDateTimeNeutral &t); + + private: + QDateTime m_dt; //!< Data value under QDateTime format from Qt library + QString m_neutral; //!< Data value under neutral format 'yyyyMMddhhmmss' + + public: + QxDateTimeNeutral() { ; } + explicit QxDateTimeNeutral(const QDateTime &dt) : m_dt(dt) { update(); } + explicit QxDateTimeNeutral(const QString &neutral) : m_neutral(neutral) { update(); } + virtual ~QxDateTimeNeutral() { ; } + + inline QDateTime toDateTime() const { return m_dt; } + inline QString toNeutral() const { return m_neutral; } + inline bool isValid() const { return m_dt.isValid(); } + + inline void setDateTime(const QDateTime &dt) + { + m_neutral = ""; + m_dt = dt; + update(); + } + inline void setNeutral(const QString &neutral) + { + m_dt = QDateTime(); + m_neutral = neutral; + update(); + } + + static QxDateTimeNeutral fromDateTime(const QDateTime &dt) { return QxDateTimeNeutral(dt); } + static QxDateTimeNeutral fromNeutral(const QString &neutral) { return QxDateTimeNeutral(neutral); } + + private: + static inline const char *format() { return "yyyyMMddhhmmss"; } + + void update() + { + if (m_neutral.isEmpty() && !m_dt.isValid()) + { + return; + } + else if (m_dt.isValid()) + { + m_neutral = m_dt.toString(format()); + } + else + { + qAssert(m_neutral.size() == QString(format()).size()); + m_dt = QDateTime::fromString(m_neutral, format()); + qAssert(m_dt.isValid()); + } + } + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + template + void serialize(Archive &ar, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("dt_neutral", m_neutral); + if (Archive::is_loading::value) + { + m_dt = QDateTime(); + update(); + } + } +#endif // _QX_ENABLE_BOOST_SERIALIZATION + }; + +} // namespace qx + +QX_REGISTER_CLASS_NAME(qx::QxDateTimeNeutral) + +#endif // _QX_DATE_TIME_NEUTRAL_H_ diff --git a/include/QxDao/QxMongoDB/QxMongoDB_Helper.h b/include/QxDao/QxMongoDB/QxMongoDB_Helper.h new file mode 100644 index 0000000..d4e671b --- /dev/null +++ b/include/QxDao/QxMongoDB/QxMongoDB_Helper.h @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_MONGODB +#ifndef _QX_DAO_MONGODB_HELPER_H_ +#define _QX_DAO_MONGODB_HELPER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxMongoDB_Helper.h + * \author XDL Team + * \ingroup QxDao + * \brief Helper class to store all QxOrm registered classes in a MongoDB database : qx::QxSqlDatabase::getSingleton()->setDriverName("QXMONGODB"); + */ + +#include + +#include + +namespace qx +{ + + class IxClass; + class QxSqlQuery; + +} // namespace qx + +namespace qx +{ + namespace dao + { + namespace detail + { + + class IxDao_Helper; + + } // namespace detail + } // namespace dao +} // namespace qx + +namespace qx +{ + namespace dao + { + namespace mongodb + { + + struct QxMongoDB_Fetcher; + + /*! + * \ingroup QxDao + * \brief qx::dao::mongodb::QxMongoDB_Helper : helper class to store all QxOrm registered classes in a MongoDB database : qx::QxSqlDatabase::getSingleton()->setDriverName("QXMONGODB"); + */ + class QX_DLL_EXPORT QxMongoDB_Helper : public QxSingleton + { + + friend class QxSingleton; + + public: + enum opts + { + opts_collection_insert_one, + opts_collection_insert_many, + opts_collection_update_one, + opts_collection_update_many, + opts_collection_delete_one, + opts_collection_delete_many, + opts_collection_find, + opts_collection_command, + opts_collection_count, + opts_collection_create_bulk_operation, + opts_bulk_operation_update_one, + opts_bulk_operation_remove_one, + opts_collection_aggregate + }; + + private: + struct QxMongoDB_HelperImpl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + + QxMongoDB_Helper(); + virtual ~QxMongoDB_Helper(); + + public: + static QSqlError insertOne(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QString &json, QString &insertedId); + static QSqlError insertMany(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QStringList &json, QStringList &insertedId); + static QSqlError updateOne(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QString &json, const qx::QxSqlQuery *query = NULL); + static QSqlError updateMany(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QStringList &json, const qx::QxSqlQuery *query = NULL); + static QSqlError deleteOne(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QString &json, const qx::QxSqlQuery *query = NULL); + static QSqlError deleteMany(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QStringList &json, const qx::QxSqlQuery *query = NULL); + static QSqlError findOne(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, QString &json, const qx::QxSqlQuery *query = NULL); + static QSqlError findMany(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, QStringList &json, const qx::QxSqlQuery *query = NULL, QxMongoDB_Fetcher *pFetcher = NULL); + static QSqlError aggregate(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, QStringList &json, const qx::QxSqlQuery *query = NULL, const QString &lookup = QString(), QxMongoDB_Fetcher *pFetcher = NULL); + static QSqlError count(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, long &cnt, const qx::QxSqlQuery *query = NULL); + static QSqlError executeCommand(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, qx::QxSqlQuery *query); + + static QSqlError autoCreateIndexes(bool log = true); + static bool setOptions(opts e, const QString &optsAsJson); + static void setLogDatabaseReply(bool b); + static void setLogDatabaseInfo(bool b); + static void clearPoolConnection(); + }; + + /*! + * \ingroup QxDao + * \brief qx::dao::mongodb::QxMongoDB_Fetcher : used to fetch a list of items from MongoDB database without having to put them in a buffer before fetching + */ + struct QX_DLL_EXPORT QxMongoDB_Fetcher + { + + QxMongoDB_Fetcher(); + virtual ~QxMongoDB_Fetcher(); + + virtual void fetch(const QString &json) = 0; + }; + + } // namespace mongodb + } // namespace dao +} // namespace qx + +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::dao::mongodb::QxMongoDB_Helper) + +#endif // _QX_DAO_MONGODB_HELPER_H_ +#endif // _QX_ENABLE_MONGODB diff --git a/include/QxDao/QxRepository/IxRepository.h b/include/QxDao/QxRepository/IxRepository.h new file mode 100644 index 0000000..1286b2e --- /dev/null +++ b/include/QxDao/QxRepository/IxRepository.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_REPOSITORY_H_ +#define _IX_REPOSITORY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxRepository.h + * \author XDL Team + * \ingroup QxDao + * \brief Common interface for all repositories to provide access to database by introspection using QObject class or qx::IxCollection class + */ + +#include +#include +#include +#include + +#include + +#include + +#include + +namespace qx +{ + + class IxClass; + class QxSession; + + /*! + * \ingroup QxDao + * \brief qx::IxRepository : common interface for all repositories to provide access to database by introspection using QObject class or qx::IxCollection class + * + * There is a type verification at runtime using dynamic_cast function. + * For example, if you are working with a class named MyType, you can call all methods of qx::IxRepository interface using : + * - MyType * for a single object, if MyType inherits from QObject ; + * - qx::QxCollection< Key, QSharedPointer > * for a list of objects, where Key is the primary key type defined for MyType class (long by default). + * + * Note : if a bad type is detected at runtime, an exception of type qx::dao::sql_error is thrown. + */ + class QX_DLL_EXPORT IxRepository + { + + protected: + bool m_bRegister; //!< Register repository into QxRepositoryX collection + QString m_sKeyRepository; //!< Repository key used by QxRepositoryX collection + QSqlDatabase m_database; //!< Database connection associated to the repository + QxSession *m_pSession; //!< Session associated to the repository + + public: + IxRepository(bool bRegister, const QString &sKey); + IxRepository(bool bRegister, const QString &sKey, const QSqlDatabase &database); + IxRepository(bool bRegister, const QString &sKey, QxSession *pSession); + virtual ~IxRepository(); + + QSqlDatabase *database(); + QxSession *session() const; + + virtual long _count(const qx::QxSqlQuery &query = qx::QxSqlQuery()) = 0; + virtual void *_fetchById(const QVariant &id, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) = 0; + virtual QSqlError _fetchById(QObject *p, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) = 0; + virtual QSqlError _fetchById(qx::IxCollection *p, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) = 0; + virtual QSqlError _fetchAll(QObject *p, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) = 0; + virtual QSqlError _fetchAll(qx::IxCollection *p, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) = 0; + virtual QSqlError _fetchByQuery(const qx::QxSqlQuery &query, QObject *p, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) = 0; + virtual QSqlError _fetchByQuery(const qx::QxSqlQuery &query, qx::IxCollection *p, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) = 0; + virtual QSqlError _insert(QObject *p, const QStringList &relation = QStringList()) = 0; + virtual QSqlError _insert(qx::IxCollection *p, const QStringList &relation = QStringList()) = 0; + virtual QSqlError _update(QObject *p, const qx::QxSqlQuery &query = qx::QxSqlQuery(), const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) = 0; + virtual QSqlError _update(qx::IxCollection *p, const qx::QxSqlQuery &query = qx::QxSqlQuery(), const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) = 0; + virtual QSqlError _save(QObject *p, const QStringList &relation = QStringList()) = 0; + virtual QSqlError _save(qx::IxCollection *p, const QStringList &relation = QStringList()) = 0; + virtual QSqlError _deleteById(const QVariant &id) = 0; + virtual QSqlError _deleteById(QObject *p) = 0; + virtual QSqlError _deleteById(qx::IxCollection *p) = 0; + virtual QSqlError _deleteAll() = 0; + virtual QSqlError _deleteByQuery(const qx::QxSqlQuery &query) = 0; + virtual QSqlError _destroyById(const QVariant &id) = 0; + virtual QSqlError _destroyById(QObject *p) = 0; + virtual QSqlError _destroyById(qx::IxCollection *p) = 0; + virtual QSqlError _destroyAll() = 0; + virtual QSqlError _destroyByQuery(const qx::QxSqlQuery &query) = 0; + virtual qx_bool _exist(QObject *p) = 0; + virtual qx_bool _exist(qx::IxCollection *p) = 0; + virtual qx::IxCollection_ptr _newCollection() const = 0; + virtual qx::IxClass *_getClass() const = 0; + + public: + static qx::IxCollection_ptr _fetchAll(const QString &repositoryKey, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()); + static qx::IxCollection_ptr _fetchByQuery(const QString &repositoryKey, const qx::QxSqlQuery &query, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()); + }; + +} // namespace qx + +#endif // _IX_REPOSITORY_H_ diff --git a/include/QxDao/QxRepository/QxRepository.h b/include/QxDao/QxRepository/QxRepository.h new file mode 100644 index 0000000..510d9bc --- /dev/null +++ b/include/QxDao/QxRepository/QxRepository.h @@ -0,0 +1,606 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_REPOSITORY_H_ +#define _QX_REPOSITORY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxRepository.h + * \author XDL Team + * \ingroup QxDao + * \brief Repository to provide a common interface to communicate with database + */ + +#include +#include + +#include +#include +#include + +#include + +#include + +#include + +#define QX_REPOSITORY_COLLECTION_DYNAMIC_CAST_ERROR QSqlError("[QxOrm] qx::QxRepository : 'invalid collection pointer, dynamic_cast failed'", "", QSqlError::UnknownError) +#define QX_REPOSITORY_POINTER_DYNAMIC_CAST_ERROR QSqlError("[QxOrm] qx::QxRepository : 'invalid pointer, dynamic_cast failed'", "", QSqlError::UnknownError) +#define QX_REPOSITORY_QOBJECT_BASE_CLASS_ERROR QSqlError("[QxOrm] qx::QxRepository : 'invalid pointer, need to inherit from QObject class to use qx::IxRepository interface'", "", QSqlError::UnknownError) + +#ifndef _QX_NO_RTTI +#define QX_REPOSITORY_CAST_COLLECTION \ + type_collection_qt *x = dynamic_cast(p); \ + type_collection_boost *y = (x ? NULL : dynamic_cast(p)); \ + if (!x && !y) \ + { \ + throw qx::dao::sql_error(QX_REPOSITORY_COLLECTION_DYNAMIC_CAST_ERROR); \ + } +#else // _QX_NO_RTTI +#define QX_REPOSITORY_CAST_COLLECTION \ + type_collection_qt *x = NULL; \ + type_collection_boost *y = static_cast(p); +#endif // _QX_NO_RTTI + +namespace qx +{ + + template + inline void register_repository(const QString &sKey); + + /*! + * \ingroup QxDao + * \brief qx::QxRepository : repository to provide a common interface to communicate with database + */ + template + class QxRepository : public IxRepository + { + + template + friend inline void register_repository(const QString &sKey); + + private: + QxRepository(const QString &sKey) : IxRepository(true, sKey) { ; } + + public: + QxRepository() : IxRepository(false, QString("")) { ; } + QxRepository(const QSqlDatabase &database) : IxRepository(false, QString(""), database) { ; } + QxRepository(QxSession *pSession) : IxRepository(false, QString(""), pSession) { ; } + virtual ~QxRepository() { ; } + + long count(const qx::QxSqlQuery &query = qx::QxSqlQuery()) + { + return qx::dao::count(query, this->database()); + } + + T *fetchById(const QVariant &id, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) + { + IxDataMemberX *pDataMemberX = QxClass::getSingleton()->getDataMemberX(); + IxDataMember *pDataMemberId = (pDataMemberX ? pDataMemberX->getId_WithDaoStrategy() : NULL); + if (!pDataMemberId) + { + qAssert(false); + return NULL; + } + T *t = new T(); + QSqlError err; + pDataMemberId->fromVariant(t, id, -1, qx::cvt::context::e_database); + if (relation.count() == 0) + { + err = qx::dao::fetch_by_id((*t), this->database(), columns); + } + else + { + err = qx::dao::fetch_by_id_with_relation(relation, (*t), this->database()); + } + if (err.isValid() && m_pSession) + { + delete t; + t = NULL; + (*m_pSession) += err; + } + else if (err.isValid()) + { + delete t; + t = NULL; + } + return t; + } + + template + QSqlError fetchById(U &u, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) + { + QSqlError err; + if (relation.count() == 0) + { + err = qx::dao::fetch_by_id(u, this->database(), columns); + } + else + { + err = qx::dao::fetch_by_id_with_relation(relation, u, this->database()); + } + if (err.isValid() && m_pSession) + { + (*m_pSession) += err; + } + return err; + } + + template + QSqlError fetchAll(U &u, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) + { + QSqlError err; + if (relation.count() == 0) + { + err = qx::dao::fetch_all(u, this->database(), columns); + } + else + { + err = qx::dao::fetch_all_with_relation(relation, u, this->database()); + } + if (err.isValid() && m_pSession) + { + (*m_pSession) += err; + } + return err; + } + + template + QSqlError fetchByQuery(const qx::QxSqlQuery &query, U &u, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) + { + QSqlError err; + if (relation.count() == 0) + { + err = qx::dao::fetch_by_query(query, u, this->database(), columns); + } + else + { + err = qx::dao::fetch_by_query_with_relation(relation, query, u, this->database()); + } + if (err.isValid() && m_pSession) + { + (*m_pSession) += err; + } + return err; + } + + template + QSqlError insert(U &u, const QStringList &relation = QStringList(), bool bUseExecBatch = false) + { + QSqlError err; + if (relation.count() == 0) + { + err = qx::dao::insert(u, this->database(), bUseExecBatch); + } + else + { + err = qx::dao::insert_with_relation(relation, u, this->database()); + } + if (err.isValid() && m_pSession) + { + (*m_pSession) += err; + } + return err; + } + + template + QSqlError update(U &u, const qx::QxSqlQuery &query = qx::QxSqlQuery(), const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), bool bUseExecBatch = false) + { + QSqlError err; + if (relation.count() == 0) + { + err = qx::dao::update_by_query(query, u, this->database(), columns, bUseExecBatch); + } + else + { + err = qx::dao::update_by_query_with_relation(relation, query, u, this->database()); + } + if (err.isValid() && m_pSession) + { + (*m_pSession) += err; + } + return err; + } + + template + QSqlError save(U &u, const QStringList &relation = QStringList()) + { + QSqlError err; + if (relation.count() == 0) + { + err = qx::dao::save(u, this->database()); + } + else + { + err = qx::dao::save_with_relation(relation, u, this->database()); + } + if (err.isValid() && m_pSession) + { + (*m_pSession) += err; + } + return err; + } + + QSqlError deleteById(const QVariant &id) + { + IxDataMemberX *pDataMemberX = QxClass::getSingleton()->getDataMemberX(); + IxDataMember *pDataMemberId = (pDataMemberX ? pDataMemberX->getId_WithDaoStrategy() : NULL); + if (!pDataMemberId) + { + qAssert(false); + return QSqlError(); + } + std::shared_ptr t = std::make_shared(); + pDataMemberId->fromVariant(t.get(), id, -1, qx::cvt::context::e_database); + QSqlError err = qx::dao::delete_by_id((*t), this->database()); + if (err.isValid() && m_pSession) + { + (*m_pSession) += err; + } + return err; + } + + template + QSqlError deleteById(U &u, bool bUseExecBatch = false) + { + QSqlError err = qx::dao::delete_by_id(u, this->database(), bUseExecBatch); + if (err.isValid() && m_pSession) + { + (*m_pSession) += err; + } + return err; + } + + QSqlError deleteAll() + { + QSqlError err = qx::dao::delete_all(this->database()); + if (err.isValid() && m_pSession) + { + (*m_pSession) += err; + } + return err; + } + + QSqlError deleteByQuery(const qx::QxSqlQuery &query) + { + QSqlError err = qx::dao::delete_by_query(query, this->database()); + if (err.isValid() && m_pSession) + { + (*m_pSession) += err; + } + return err; + } + + QSqlError destroyById(const QVariant &id) + { + IxDataMemberX *pDataMemberX = QxClass::getSingleton()->getDataMemberX(); + IxDataMember *pDataMemberId = (pDataMemberX ? pDataMemberX->getId_WithDaoStrategy() : NULL); + if (!pDataMemberId) + { + qAssert(false); + return QSqlError(); + } + std::shared_ptr t = std::make_shared(); + pDataMemberId->fromVariant(t.get(), id, -1, qx::cvt::context::e_database); + QSqlError err = qx::dao::destroy_by_id((*t), this->database()); + if (err.isValid() && m_pSession) + { + (*m_pSession) += err; + } + return err; + } + + template + QSqlError destroyById(U &u, bool bUseExecBatch = false) + { + QSqlError err = qx::dao::destroy_by_id(u, this->database(), bUseExecBatch); + if (err.isValid() && m_pSession) + { + (*m_pSession) += err; + } + return err; + } + + QSqlError destroyAll() + { + QSqlError err = qx::dao::destroy_all(this->database()); + if (err.isValid() && m_pSession) + { + (*m_pSession) += err; + } + return err; + } + + QSqlError destroyByQuery(const qx::QxSqlQuery &query) + { + QSqlError err = qx::dao::destroy_by_query(query, this->database()); + if (err.isValid() && m_pSession) + { + (*m_pSession) += err; + } + return err; + } + + template + qx_bool exist(U &u) + { + return qx::dao::exist(u, this->database()); + } + + private: + typedef typename qx::trait::get_primary_key::type type_primary_key; + typedef qx::QxCollection> type_collection_qt; + typedef qx::QxCollection> type_collection_boost; + + template + struct qxVerifyPointer + { + static inline T *get(QObject *p) + { + Q_UNUSED(p); + throw qx::dao::sql_error(QX_REPOSITORY_QOBJECT_BASE_CLASS_ERROR); + return NULL; + } + }; + + template + struct qxVerifyPointer +#ifdef _QX_NO_RTTI + { + static inline T *get(QObject *p) + { + T *t = qobject_cast(p); + if (!t) + { + throw qx::dao::sql_error(QX_REPOSITORY_POINTER_DYNAMIC_CAST_ERROR); + }; + return t; + } + }; +#else // _QX_NO_RTTI + { + static inline T *get(QObject *p) + { + T *t = dynamic_cast(p); + if (!t) + { + throw qx::dao::sql_error(QX_REPOSITORY_POINTER_DYNAMIC_CAST_ERROR); + }; + return t; + } + }; +#endif // _QX_NO_RTTI + + public: + virtual long _count(const qx::QxSqlQuery &query = qx::QxSqlQuery()) + { + return this->count(query); + } + + virtual void *_fetchById(const QVariant &id, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) + { + return static_cast(this->fetchById(id, columns, relation)); + } + + virtual QSqlError _fetchById(QObject *p, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) + { + T *t = qxVerifyPointer::value, 0>::get(p); + return this->fetchById((*t), columns, relation); + } + + virtual QSqlError _fetchById(qx::IxCollection *p, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) + { + QX_REPOSITORY_CAST_COLLECTION + return (x ? this->fetchById((*x), columns, relation) : this->fetchById((*y), columns, relation)); + } + + virtual QSqlError _fetchAll(QObject *p, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) + { + T *t = qxVerifyPointer::value, 0>::get(p); + return this->fetchAll((*t), columns, relation); + } + + virtual QSqlError _fetchAll(qx::IxCollection *p, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) + { + QX_REPOSITORY_CAST_COLLECTION + return (x ? this->fetchAll((*x), columns, relation) : this->fetchAll((*y), columns, relation)); + } + + virtual QSqlError _fetchByQuery(const qx::QxSqlQuery &query, QObject *p, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) + { + T *t = qxVerifyPointer::value, 0>::get(p); + return this->fetchByQuery(query, (*t), columns, relation); + } + + virtual QSqlError _fetchByQuery(const qx::QxSqlQuery &query, qx::IxCollection *p, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) + { + QX_REPOSITORY_CAST_COLLECTION + return (x ? this->fetchByQuery(query, (*x), columns, relation) : this->fetchByQuery(query, (*y), columns, relation)); + } + + virtual QSqlError _insert(QObject *p, const QStringList &relation = QStringList()) + { + T *t = qxVerifyPointer::value, 0>::get(p); + return this->insert((*t), relation); + } + + virtual QSqlError _insert(qx::IxCollection *p, const QStringList &relation = QStringList()) + { + QX_REPOSITORY_CAST_COLLECTION + return (x ? this->insert((*x), relation) : this->insert((*y), relation)); + } + + virtual QSqlError _update(QObject *p, const qx::QxSqlQuery &query = qx::QxSqlQuery(), const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) + { + T *t = qxVerifyPointer::value, 0>::get(p); + return this->update((*t), query, columns, relation); + } + + virtual QSqlError _update(qx::IxCollection *p, const qx::QxSqlQuery &query = qx::QxSqlQuery(), const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) + { + QX_REPOSITORY_CAST_COLLECTION + return (x ? this->update((*x), query, columns, relation) : this->update((*y), query, columns, relation)); + } + + virtual QSqlError _save(QObject *p, const QStringList &relation = QStringList()) + { + T *t = qxVerifyPointer::value, 0>::get(p); + return this->save((*t), relation); + } + + virtual QSqlError _save(qx::IxCollection *p, const QStringList &relation = QStringList()) + { + QX_REPOSITORY_CAST_COLLECTION + return (x ? this->save((*x), relation) : this->save((*y), relation)); + } + + virtual QSqlError _deleteById(const QVariant &id) + { + return this->deleteById(id); + } + + virtual QSqlError _deleteById(QObject *p) + { + T *t = qxVerifyPointer::value, 0>::get(p); + return this->deleteById(*t); + } + + virtual QSqlError _deleteById(qx::IxCollection *p) + { + QX_REPOSITORY_CAST_COLLECTION + return (x ? this->deleteById(*x) : this->deleteById(*y)); + } + + virtual QSqlError _deleteAll() + { + return this->deleteAll(); + } + + virtual QSqlError _deleteByQuery(const qx::QxSqlQuery &query) + { + return this->deleteByQuery(query); + } + + virtual QSqlError _destroyById(const QVariant &id) + { + return this->destroyById(id); + } + + virtual QSqlError _destroyById(QObject *p) + { + T *t = qxVerifyPointer::value, 0>::get(p); + return this->destroyById(*t); + } + + virtual QSqlError _destroyById(qx::IxCollection *p) + { + QX_REPOSITORY_CAST_COLLECTION + return (x ? this->destroyById(*x) : this->destroyById(*y)); + } + + virtual QSqlError _destroyAll() + { + return this->destroyAll(); + } + + virtual QSqlError _destroyByQuery(const qx::QxSqlQuery &query) + { + return this->destroyByQuery(query); + } + + virtual qx_bool _exist(QObject *p) + { + T *t = qxVerifyPointer::value, 0>::get(p); + return this->exist(*t); + } + + virtual qx_bool _exist(qx::IxCollection *p) + { + QX_REPOSITORY_CAST_COLLECTION + return (x ? this->exist(*x) : this->exist(*y)); + } + + virtual qx::IxCollection_ptr _newCollection() const + { + qx::IxCollection_ptr lst = std::make_shared(); + return lst; + } + + virtual qx::IxClass *_getClass() const + { + return qx::QxClass::getSingleton(); + } + + public: + static T *getById(const QVariant &id, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) + { + IxDataMemberX *pDataMemberX = QxClass::getSingleton()->getDataMemberX(); + IxDataMember *pDataMemberId = (pDataMemberX ? pDataMemberX->getId_WithDaoStrategy() : NULL); + if (!pDataMemberId) + { + qAssert(false); + return NULL; + } + T *t = new T(); + QSqlError err; + pDataMemberId->fromVariant(t, id, -1, qx::cvt::context::e_database); + if (relation.count() == 0) + { + err = qx::dao::fetch_by_id((*t), NULL, columns); + } + else + { + err = qx::dao::fetch_by_id_with_relation(relation, (*t), NULL); + } + if (err.isValid()) + { + delete t; + t = NULL; + } + return t; + } + }; + + template + inline void register_repository(const QString &sKey) + { + // 'pNewRepository' instance will be destroyed by 'qx::QxRepositoryX::unregisterRepository()' method + qx::QxRepository *pNewRepository = new qx::QxRepository(sKey); + Q_UNUSED(pNewRepository); + } + +} // namespace qx + +#endif // _QX_REPOSITORY_H_ diff --git a/include/QxDao/QxRepository/QxRepositoryX.h b/include/QxDao/QxRepository/QxRepositoryX.h new file mode 100644 index 0000000..d9bb42d --- /dev/null +++ b/include/QxDao/QxRepository/QxRepositoryX.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_REPOSITORY_X_H_ +#define _QX_REPOSITORY_X_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxRepositoryX.h + * \author XDL Team + * \ingroup QxDao + * \brief List of all repositories registered using qx::register_repository function + */ + +#include +#include + +#include + +#include + +namespace qx +{ + + /*! + * \ingroup QxDao + * \brief qx::QxRepositoryX : list of all repositories registered using qx::register_repository function + * + * Note : you can register automatically all repositories using the macro _QX_AUTO_REGISTER_REPOSITORY into QxConfig.h file. + */ + class QX_DLL_EXPORT QxRepositoryX : public QxSingleton + { + + friend class IxRepository; + friend class QxSingleton; + + protected: + QHash m_mapRepositoryX; //!< Collection of all 'IxRepository' pointer + QMutex m_oMutexRepositoryX; //!< Mutex -> 'QxRepositoryX' is thread-safe + bool m_bUnregisterAllRepository; //!< Flag to know if collection is clearing + + private: + QxRepositoryX() : QxSingleton("qx::QxRepositoryX"), m_bUnregisterAllRepository(false) { ; } + virtual ~QxRepositoryX() { unregisterAllRepository(); } + + void registerRepository(const QString &sKey, IxRepository *pRepository); + void unregisterRepository(const QString &sKey); + void unregisterAllRepository(); + + public: + static IxRepository *get(const QString &sKey); + }; + +} // namespace qx + +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::QxRepositoryX) + +#endif // _QX_REPOSITORY_X_H_ diff --git a/include/QxDao/QxSession.h b/include/QxDao/QxSession.h new file mode 100644 index 0000000..791072d --- /dev/null +++ b/include/QxDao/QxSession.h @@ -0,0 +1,457 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_DAO_SESSION_H_ +#define _QX_DAO_SESSION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSession.h + * \author XDL Team + * \ingroup QxDao + * \brief Define a session to manage automatically database transactions (using C++ RAII) + */ + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include + +namespace qx +{ + + /*! + * \ingroup QxDao + * \brief qx::QxSession : define a session to manage automatically database transactions (using C++ RAII) + * + * A database transaction is a sequence of operations performed as a single logical unit of work. + * If no errors occurred during the execution of the transaction then the system commits the transaction. + * If an error occurs during the transaction, or if the user specifies a rollback operation, the data manipulations within the transaction are not persisted to the database. + * + * The qx::QxSession class of QxOrm library is designed to manage automatically database transactions (using C++ RAII) : + * \code + { // Start a scope where a new session is instantiated + + // Create a session : a valid database connexion by thread is automatically assigned to the session and a transaction is opened + qx::QxSession session; + + // Execute some operations with database (using += operator of qx::QxSession class and session database connexion) + session += qx::dao::insert(my_object, session.database()); + session += qx::dao::update(my_object, session.database()); + session += qx::dao::fetch_by_id(my_object, session.database()); + session += qx::dao::delete_by_id(my_object, session.database()); + + // If the session is not valid (so an error occured) => display first error + if (! session.isValid()) { qDebug("[QxOrm] session error : '%s'", qPrintable(session.firstError().text())); } + + } // End of scope : session is destroyed (transaction => automatically commit or rollback if there is an error) + * \endcode + * + * Note : a session can throw a qx::dao::sql_error exception when a SQL error occured (by default, there is no exception). + * You can setup this feature using : + * - qx::QxSession constructor (for a specific session) ; + * - qx::QxSqlDatabase::getSingleton()->setSessionThrowable(bool b) parameter (for all sessions). + * + * Other note : don't forget to pass the session database connexion to each qx::dao::xxx functions (using session.database() method). + * Moreover, you can manage your own database connexion (from a connexion pool for example) using constructor of qx::QxSession class. + * + * qx::QxSession class provides also persistent methods (CRUD) to make easier to write C++ code. + * Here is the same example using methods of qx::QxSession class instead of functions into namespace qx::dao : + * \code + { // Start a scope where a new session is instantiated + + // Create a session : a valid database connexion by thread is automatically assigned to the session and a transaction is opened + qx::QxSession session; + + // Execute some operations with database + session.insert(my_object); + session.update(my_object); + session.fetchById(my_object); + session.deleteById(my_object); + + // If the session is not valid (so an error occured) => display first error + if (! session.isValid()) { qDebug("[QxOrm] session error : '%s'", qPrintable(session.firstError().text())); } + + } // End of scope : session is destroyed (transaction => automatically commit or rollback if there is an error) + * \endcode + */ + class QX_DLL_EXPORT QxSession + { + + private: + struct QxSessionImpl; + std::shared_ptr m_pImpl; //!< Private implementation idiom (use std::shared_ptr instead of std::unique_ptr because of incomplete type) + + public: + QxSession(); + QxSession(const QSqlDatabase &database); + QxSession(const QSqlDatabase &database, bool bOpenTransaction); + QxSession(const QSqlDatabase &database, bool bOpenTransaction, bool bThrowable, bool bAutoRollbackWhenDestroyed = false); + virtual ~QxSession(); + + bool isThrowable() const; + bool isOpened() const; + bool isValid() const; + bool isAutoRollbackWhenDestroyed() const; + void setAutoRollbackWhenDestroyed(bool b); + QSqlError firstError() const; + QSqlError lastError() const; + QList allErrors() const; + const QSqlDatabase *database() const; + QSqlDatabase *database(); + + bool open(); + bool close(); + bool commit(); + bool rollback(); + + QxSession &operator+=(const QSqlError &err); + + static QxSession *getActiveSession(QSqlDatabase *db); + + void ignoreSoftDelete(bool bIgnoreSoftDelete = true, const QStringList &classesToIgnore = QStringList()); + bool checkIgnoreSoftDelete(const QString &classKey) const; + QString getIgnoreSoftDeleteHash() const; + + template + long count(const qx::QxSqlQuery &query = qx::QxSqlQuery()) + { + return qx::dao::count(query, this->database()); + } + + template + QSqlError count(long &lCount, const qx::QxSqlQuery &query = qx::QxSqlQuery()) + { + return qx::dao::count(lCount, query, this->database()); + } + + template + T *fetchById(const QVariant &id, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) + { + IxDataMemberX *pDataMemberX = QxClass::getSingleton()->getDataMemberX(); + IxDataMember *pDataMemberId = (pDataMemberX ? pDataMemberX->getId_WithDaoStrategy() : NULL); + if (!pDataMemberId) + { + qAssert(false); + return NULL; + } + T *t = new T(); + QSqlError err; + pDataMemberId->fromVariant(t, id, -1, qx::cvt::context::e_database); + if (relation.count() == 0) + { + err = qx::dao::fetch_by_id((*t), this->database(), columns); + } + else + { + err = qx::dao::fetch_by_id_with_relation(relation, (*t), this->database()); + } + if (err.isValid()) + { + delete t; + t = NULL; + (*this) += err; + } + return t; + } + + template + QSqlError fetchById(T &t, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) + { + QSqlError err; + if (relation.count() == 0) + { + err = qx::dao::fetch_by_id(t, this->database(), columns); + } + else + { + err = qx::dao::fetch_by_id_with_relation(relation, t, this->database()); + } + if (err.isValid()) + { + (*this) += err; + } + return err; + } + + template + QSqlError fetchAll(T &t, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) + { + QSqlError err; + if (relation.count() == 0) + { + err = qx::dao::fetch_all(t, this->database(), columns); + } + else + { + err = qx::dao::fetch_all_with_relation(relation, t, this->database()); + } + if (err.isValid()) + { + (*this) += err; + } + return err; + } + + template + QSqlError fetchByQuery(const qx::QxSqlQuery &query, T &t, const QStringList &columns = QStringList(), const QStringList &relation = QStringList()) + { + QSqlError err; + if (relation.count() == 0) + { + err = qx::dao::fetch_by_query(query, t, this->database(), columns); + } + else + { + err = qx::dao::fetch_by_query_with_relation(relation, query, t, this->database()); + } + if (err.isValid()) + { + (*this) += err; + } + return err; + } + + template + QSqlError insert(T &t, const QStringList &relation = QStringList(), bool bUseExecBatch = false) + { + QSqlError err; + if (relation.count() == 0) + { + err = qx::dao::insert(t, this->database(), bUseExecBatch); + } + else + { + err = qx::dao::insert_with_relation(relation, t, this->database()); + } + if (err.isValid()) + { + (*this) += err; + } + return err; + } + + template + QSqlError update(T &t, const qx::QxSqlQuery &query = qx::QxSqlQuery(), const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), bool bUseExecBatch = false) + { + QSqlError err; + if (relation.count() == 0) + { + err = qx::dao::update_by_query(query, t, this->database(), columns, bUseExecBatch); + } + else + { + err = qx::dao::update_by_query_with_relation(relation, query, t, this->database()); + } + if (err.isValid()) + { + (*this) += err; + } + return err; + } + + template + QSqlError save(T &t, const QStringList &relation = QStringList()) + { + QSqlError err; + if (relation.count() == 0) + { + err = qx::dao::save(t, this->database()); + } + else + { + err = qx::dao::save_with_relation(relation, t, this->database()); + } + if (err.isValid()) + { + (*this) += err; + } + return err; + } + + template + QSqlError deleteById(const QVariant &id) + { + IxDataMemberX *pDataMemberX = QxClass::getSingleton()->getDataMemberX(); + IxDataMember *pDataMemberId = (pDataMemberX ? pDataMemberX->getId_WithDaoStrategy() : NULL); + if (!pDataMemberId) + { + qAssert(false); + return QSqlError(); + } + std::shared_ptr t = std::make_shared(); + pDataMemberId->fromVariant(t.get(), id, -1, qx::cvt::context::e_database); + QSqlError err = qx::dao::delete_by_id((*t), this->database()); + if (err.isValid()) + { + (*this) += err; + } + return err; + } + + template + QSqlError deleteById(T &t, bool bUseExecBatch = false) + { + QSqlError err = qx::dao::delete_by_id(t, this->database(), bUseExecBatch); + if (err.isValid()) + { + (*this) += err; + } + return err; + } + + template + QSqlError deleteAll() + { + QSqlError err = qx::dao::delete_all(this->database()); + if (err.isValid()) + { + (*this) += err; + } + return err; + } + + template + QSqlError deleteByQuery(const qx::QxSqlQuery &query) + { + QSqlError err = qx::dao::delete_by_query(query, this->database()); + if (err.isValid()) + { + (*this) += err; + } + return err; + } + + template + QSqlError destroyById(const QVariant &id) + { + IxDataMemberX *pDataMemberX = QxClass::getSingleton()->getDataMemberX(); + IxDataMember *pDataMemberId = (pDataMemberX ? pDataMemberX->getId_WithDaoStrategy() : NULL); + if (!pDataMemberId) + { + qAssert(false); + return QSqlError(); + } + std::shared_ptr t = std::make_shared(); + pDataMemberId->fromVariant(t.get(), id, -1, qx::cvt::context::e_database); + QSqlError err = qx::dao::destroy_by_id((*t), this->database()); + if (err.isValid()) + { + (*this) += err; + } + return err; + } + + template + QSqlError destroyById(T &t, bool bUseExecBatch = false) + { + QSqlError err = qx::dao::destroy_by_id(t, this->database(), bUseExecBatch); + if (err.isValid()) + { + (*this) += err; + } + return err; + } + + template + QSqlError destroyAll() + { + QSqlError err = qx::dao::destroy_all(this->database()); + if (err.isValid()) + { + (*this) += err; + } + return err; + } + + template + QSqlError destroyByQuery(const qx::QxSqlQuery &query) + { + QSqlError err = qx::dao::destroy_by_query(query, this->database()); + if (err.isValid()) + { + (*this) += err; + } + return err; + } + + template + QSqlError executeQuery(qx::QxSqlQuery &query, T &t) + { + QSqlError err = qx::dao::execute_query(query, t, this->database()); + if (err.isValid()) + { + (*this) += err; + } + return err; + } + + QSqlError callQuery(qx::QxSqlQuery &query) + { + QSqlError err = qx::dao::call_query(query, this->database()); + if (err.isValid()) + { + (*this) += err; + } + return err; + } + + template + qx_bool exist(T &t) + { + return qx::dao::exist(t, this->database()); + } + + private: + QxSession(const QxSession &other) { Q_UNUSED(other); } + QxSession &operator=(const QxSession &other) + { + Q_UNUSED(other); + return (*this); + } + }; + +} // namespace qx + +#endif // _QX_DAO_SESSION_H_ diff --git a/include/QxDao/QxSoftDelete.h b/include/QxDao/QxSoftDelete.h new file mode 100644 index 0000000..a5eb5a6 --- /dev/null +++ b/include/QxDao/QxSoftDelete.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SOFT_DELETE_H_ +#define _QX_SOFT_DELETE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSoftDelete.h + * \author XDL Team + * \ingroup QxDao + * \brief Soft delete (or logical delete) behavior to update a row into database (flag it as deleted) instead of delete it from database + */ + +#define QX_DAO_SOFT_DELETE_QDATETIME_FORMAT "yyyyMMddhhmmsszzz" + +namespace qx +{ + + /*! + * \ingroup QxDao + * \brief qx::QxSoftDelete : soft delete (or logical delete) behavior to update a row into database (flag it as deleted) instead of delete it from database + * + * A soft delete doesn't remove rows from database (this is not a physical delete) : a new column is added to the table definition to flag a row as deleted or not. + * This column can contain a boolean (1 means row deleted, 0 or NULL means row not deleted), or can contain deletion date-time (if empty or NULL, row is not deleted). + * So you can reactivate a deleted row by setting NULL or empty value into database. + * + * To define a soft delete behavior with QxOrm library, you have to use the class qx::QxSoftDelete in function mapping by class qx::register_class. + * Here is an example with the class Bar containing 2 properties m_id and m_desc : + * \code + namespace qx { + template <> void register_class(QxClass & t) + { + t.setSoftDelete(qx::QxSoftDelete("deleted_at")); + + t.id(& Bar::m_id, "id"); + t.data(& Bar::m_desc, "desc"); + }} + * \endcode + * + * SQL queries builded by QxOrm library will take into account this soft delete parameter to add conditions (don't fetch deleted item, don't delete physically a row, etc.). + * For example, if you execute this code with the class Bar : + * \code + Bar_ptr pBar; pBar.reset(new Bar()); + pBar->setId(5); + QSqlError daoError = qx::dao::delete_by_id(pBar); qAssert(! daoError.isValid()); + qx_bool bDaoExist = qx::dao::exist(pBar); qAssert(! bDaoExist); + daoError = qx::dao::delete_all(); qAssert(! daoError.isValid()); + long lBarCount = qx::dao::count(); qAssert(lBarCount == 0); + daoError = qx::dao::destroy_all(); qAssert(! daoError.isValid()); + * \endcode + * + * You will obtain following output trace : + * \code + [QxOrm] sql query (93 ms) : UPDATE Bar SET deleted_at = '20110617115148615' WHERE id = :id + [QxOrm] sql query (0 ms) : SELECT Bar.id AS Bar_id_0, Bar.deleted_at FROM Bar WHERE Bar.id = :id AND (Bar.deleted_at IS NULL OR Bar.deleted_at = '') + [QxOrm] sql query (78 ms) : UPDATE Bar SET deleted_at = '20110617115148724' + [QxOrm] sql query (0 ms) : SELECT COUNT(*) FROM Bar WHERE (Bar.deleted_at IS NULL OR Bar.deleted_at = '') + [QxOrm] sql query (110 ms) : DELETE FROM Bar + * \endcode + * + * Note : To delete physically a row from database, you have to use followings functions : qx::dao::destroy_by_id() and qx::dao::destroy_all(). + * + * Other note : it is recommended to define into database an index on column deleted_at to optimize execution of SQL queries. + */ + class QX_DLL_EXPORT QxSoftDelete + { + + public: + enum mode + { + mode_flag, + mode_date_time + }; + + private: + QString m_sTable; //!< Table name where soft delete behavior is applied + QString m_sColumn; //!< Column name to store soft delete information + QString m_sSqlQueryToFetch; //!< Overrided user SQL query to fetch an item, if empty QxOrm library builds a default SQL query + QString m_sSqlQueryToUpdate; //!< Overrided user SQL query to update an item, if empty QxOrm library builds a default SQL query + QString m_sSqlQueryToCreateTable; //!< Overrided user SQL query to create table, if empty QxOrm library builds a default SQL query + mode m_eMode; //!< Soft delete mode : 'mode_flag' with a boolean column, 'mode_date_time' with a date-time column containing deletion date-time + bool m_bFetchInJoin; //!< Add SQL condition to fetch in the JOIN part (default value), for backward compatibility with previous versions of QxOrm library set this value to false (means fetch in the WHERE part) + + public: + QxSoftDelete(); + QxSoftDelete(const QString &sColumn); + QxSoftDelete(const QString &sColumn, mode eMode); + ~QxSoftDelete(); + + QString getTableName() const; + QString getColumnName() const; + QString getSqlQueryToFetch() const; + QString getSqlQueryToUpdate() const; + QString getSqlQueryToCreateTable() const; + mode getMode() const; + bool getSqlFetchInJoin() const; + + void setTableName(const QString &sTable); + void setColumnName(const QString &sColumn); + void setSqlQueryToFetch(const QString &s); + void setSqlQueryToUpdate(const QString &s); + void setSqlQueryToCreateTable(const QString &s); + void setMode(mode eMode); + void setSqlFetchInJoin(bool b); + + bool isEmpty() const; + + QString buildSqlTablePointName(const QString &sTable = QString()) const; + QString buildSqlQueryToFetch(const QString &sTable = QString()) const; + QString buildSqlQueryToUpdate() const; + QString buildSqlQueryToCreateTable() const; + }; + +} // namespace qx + +#endif // _QX_SOFT_DELETE_H_ diff --git a/include/QxDao/QxSqlDatabase.h b/include/QxDao/QxSqlDatabase.h new file mode 100644 index 0000000..cc153d8 --- /dev/null +++ b/include/QxDao/QxSqlDatabase.h @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_DATABASE_H_ +#define _QX_SQL_DATABASE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlDatabase.h + * \author XDL Team + * \ingroup QxDao + * \brief Define all parameters to connect to database and retrieve a valid connection by thread + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + class IxDao_Helper; + + } // namespace detail + } // namespace dao +} // namespace qx + +namespace qx +{ + + /*! + * \ingroup QxDao + * \brief qx::QxSqlDatabase : define all parameters to connect to database and retrieve a valid connection by thread (this class is a singleton and is thread-safe) + */ + class QX_DLL_EXPORT QxSqlDatabase : public QxSingleton + { + + friend class QxSingleton; + friend class qx::dao::detail::IxDao_Helper; + + public: + enum ph_style + { + ph_style_question_mark, + ph_style_2_point_name, + ph_style_at_name + }; + + typedef std::function type_fct_db_open; + + private: + struct QxSqlDatabaseImpl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + + QxSqlDatabase(); + virtual ~QxSqlDatabase(); + + public: + QString getDriverName() const; + QString getConnectOptions() const; + QString getDatabaseName() const; + QString getUserName() const; + QString getPassword() const; + QString getHostName() const; + int getPort() const; + bool getTraceSqlQuery() const; + bool getTraceSqlRecord() const; + bool getTraceSqlBoundValues() const; + bool getTraceSqlBoundValuesOnError() const; + ph_style getSqlPlaceHolderStyle() const; + bool getSessionThrowable() const; + bool getSessionAutoTransaction() const; + bool getValidatorThrowable() const; + bool getAutoReplaceSqlAliasIntoQuery() const; + bool getVerifyOffsetRelation() const; + bool getAddAutoIncrementIdToUpdateQuery() const; + bool getForceParentIdToAllChildren() const; + type_fct_db_open getFctDatabaseOpen() const; + bool getAddSqlSquareBracketsForTableName() const; + bool getAddSqlSquareBracketsForColumnName() const; + bool getFormatSqlQueryBeforeLogging() const; + QStringList getSqlDelimiterForTableName() const; + QStringList getSqlDelimiterForColumnName() const; + QStringList getSqlDelimiterForTableNameAlias() const; + QStringList getSqlDelimiterForColumnNameAlias() const; + int getTraceSqlOnlySlowQueriesDatabase() const; + int getTraceSqlOnlySlowQueriesTotal() const; + bool getDisplayTimerDetails() const; + + void setDriverName(const QString &s, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setConnectOptions(const QString &s, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setDatabaseName(const QString &s, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setUserName(const QString &s, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setPassword(const QString &s, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setHostName(const QString &s, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setPort(int i, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setTraceSqlQuery(bool b, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setTraceSqlRecord(bool b, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setTraceSqlBoundValues(bool b, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setTraceSqlBoundValuesOnError(bool b, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setSqlPlaceHolderStyle(ph_style e, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setSessionThrowable(bool b, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setSessionAutoTransaction(bool b, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setValidatorThrowable(bool b, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setSqlGenerator(qx::dao::detail::IxSqlGenerator_ptr p, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setAutoReplaceSqlAliasIntoQuery(bool b, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setVerifyOffsetRelation(bool b, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setAddAutoIncrementIdToUpdateQuery(bool b, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setForceParentIdToAllChildren(bool b, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setFctDatabaseOpen(type_fct_db_open fct, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setAddSqlSquareBracketsForTableName(bool b, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setAddSqlSquareBracketsForColumnName(bool b, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setFormatSqlQueryBeforeLogging(bool b, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setSqlDelimiterForTableName(const QStringList &lst, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setSqlDelimiterForColumnName(const QStringList &lst, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setSqlDelimiterForTableNameAlias(const QStringList &lst, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setSqlDelimiterForColumnNameAlias(const QStringList &lst, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setTraceSqlOnlySlowQueriesDatabase(int i, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setTraceSqlOnlySlowQueriesTotal(int i, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + void setDisplayTimerDetails(bool b, bool bJustForCurrentThread = false, QSqlDatabase *pJustForThisDatabase = NULL); + + static QSqlDatabase getDatabase(); + static QSqlDatabase getDatabase(QSqlError &dbError); + static QSqlDatabase getDatabaseCloned(); + static QSqlDatabase checkDatabaseByThread(); + static void removeDatabaseByThread(); + static void closeAllDatabases(); + static void clearAllDatabases(); + static bool isEmpty(); + + qx::dao::detail::IxSqlGenerator *getSqlGenerator(); + + void clearAllSettingsForCurrentThread(); + void clearAllSettingsForDatabase(QSqlDatabase *p); + + protected: + bool setCurrentDatabaseByThread(QSqlDatabase *p); + void clearCurrentDatabaseByThread(); + }; + +} // namespace qx + +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::QxSqlDatabase) + +#endif // _QX_SQL_DATABASE_H_ diff --git a/include/QxDao/QxSqlElement/IxSqlElement.h b/include/QxDao/QxSqlElement/IxSqlElement.h new file mode 100644 index 0000000..b3d9914 --- /dev/null +++ b/include/QxDao/QxSqlElement/IxSqlElement.h @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_SQL_ELEMENT_H_ +#define _IX_SQL_ELEMENT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxSqlElement.h + * \author XDL Team + * \ingroup QxDao + * \brief Common interface for all SQL elements to build SQL query + */ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#include +#include +#include +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#ifndef _QX_NO_JSON +#include +#endif // _QX_NO_JSON + +#include + +#include + +#include + +#include +#include +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + class IxSqlElement; + } // namespace detail + } // namespace dao +} // namespace qx + +QX_DLL_EXPORT QDataStream &operator<<(QDataStream &stream, const qx::dao::detail::IxSqlElement &t) QX_USED; +QX_DLL_EXPORT QDataStream &operator>>(QDataStream &stream, qx::dao::detail::IxSqlElement &t) QX_USED; + +#ifndef _QX_NO_JSON +namespace qx +{ + namespace cvt + { + namespace detail + { + template <> + struct QxConvert_ToJson; + template <> + struct QxConvert_FromJson; + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const qx::dao::detail::IxSqlElement &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, qx::dao::detail::IxSqlElement &t, const QString &format) QX_USED; + } // namespace detail + } // namespace cvt +} // namespace qx +#endif // _QX_NO_JSON + +namespace qx +{ + namespace dao + { + namespace detail + { + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::IxSqlElement : common interface for all SQL elements to build SQL query + */ + class QX_DLL_EXPORT IxSqlElement + { + + friend QX_DLL_EXPORT QDataStream & ::operator<<(QDataStream &stream, const qx::dao::detail::IxSqlElement &t); + friend QX_DLL_EXPORT QDataStream & ::operator>>(QDataStream &stream, qx::dao::detail::IxSqlElement &t); + +#ifndef _QX_NO_JSON + friend struct qx::cvt::detail::QxConvert_ToJson; + friend struct qx::cvt::detail::QxConvert_FromJson; + friend QX_DLL_EXPORT QJsonValue qx::cvt::detail::QxConvert_ToJson_Helper(const qx::dao::detail::IxSqlElement &t, const QString &format); + friend QX_DLL_EXPORT qx_bool qx::cvt::detail::QxConvert_FromJson_Helper(const QJsonValue &j, qx::dao::detail::IxSqlElement &t, const QString &format); +#endif // _QX_NO_JSON + + public: + enum type_class + { + _no_type, + _sql_compare, + _sql_element_temp, + _sql_expression, + _sql_free_text, + _sql_in, + _sql_is_between, + _sql_is_null, + _sql_limit, + _sql_sort, + _sql_embed_query + }; + + protected: + int m_iIndex; //!< Index of SQL element to build unique string + QStringList m_lstColumns; //!< List of columns associated to SQL element + QStringList m_lstKeys; //!< List of keys associated to SQL element + QList m_lstValues; //!< List of values associated to SQL element + IxSqlGenerator *m_pSqlGenerator; //!< SQL generator to build SQL query specific for each database + + public: + IxSqlElement(int index); + virtual ~IxSqlElement(); + + void setColumn(const QString &column); + void setColumns(const QStringList &columns); + void setValue(const QVariant &val); + void setValues(const QVariantList &values); + + virtual IxSqlElement::type_class getTypeClass() const = 0; + + virtual QString toString() const = 0; + virtual void resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch = NULL) const = 0; + virtual void postProcess(QString &sql) const = 0; + + virtual void clone(IxSqlElement *other); + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + template + void qxSave(Archive &ar) const + { + QString sExtraSettings = getExtraSettings(); + ar << boost::serialization::make_nvp("index", m_iIndex); + ar << boost::serialization::make_nvp("list_columns", m_lstColumns); + ar << boost::serialization::make_nvp("list_keys", m_lstKeys); + ar << boost::serialization::make_nvp("list_values", m_lstValues); + ar << boost::serialization::make_nvp("extra_settings", sExtraSettings); + } +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + template + void qxLoad(Archive &ar) + { + QString sExtraSettings; + ar >> boost::serialization::make_nvp("index", m_iIndex); + ar >> boost::serialization::make_nvp("list_columns", m_lstColumns); + ar >> boost::serialization::make_nvp("list_keys", m_lstKeys); + ar >> boost::serialization::make_nvp("list_values", m_lstValues); + ar >> boost::serialization::make_nvp("extra_settings", sExtraSettings); + setExtraSettings(sExtraSettings); + } +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + protected: + void updateKeys(); + + virtual QString getExtraSettings() const = 0; + virtual void setExtraSettings(const QString &s) = 0; + }; + + typedef std::shared_ptr IxSqlElement_ptr; + + QX_DLL_EXPORT IxSqlElement_ptr create_sql_element(IxSqlElement::type_class e) QX_USED; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _IX_SQL_ELEMENT_H_ diff --git a/include/QxDao/QxSqlElement/QxSqlCompare.h b/include/QxDao/QxSqlElement/QxSqlCompare.h new file mode 100644 index 0000000..a1cb900 --- /dev/null +++ b/include/QxDao/QxSqlElement/QxSqlCompare.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_COMPARE_H_ +#define _QX_SQL_COMPARE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlCompare.h + * \author XDL Team + * \ingroup QxDao + * \brief SQL element to compare value (==, <, >, <=, >=, LIKE, NOT LIKE, etc.) + */ + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::QxSqlCompare : SQL element to compare value (==, <, >, <=, >=, LIKE, NOT LIKE, etc.) + */ + class QX_DLL_EXPORT QxSqlCompare : public IxSqlElement + { + + public: + enum type + { + _is_equal_to, + _is_not_equal_to, + _is_greater_than, + _is_greater_than_or_equal_to, + _is_less_than, + _is_less_than_or_equal_to, + _like, + _not_like, + _starts_with, + _ends_with, + _contains_string, + _custom_operator, + _is_equal_to_select, + _is_not_equal_to_select + }; + + protected: + QxSqlCompare::type m_type; //!< Compare type + QString m_sCustomOperator; //!< Possibility to define a custom operator with enum _custom_operator (for example <@ for PostgreSQL ltree type) + + public: + QxSqlCompare(); + QxSqlCompare(int index, QxSqlCompare::type t, const QString &sCustomOperator = QString()); + virtual ~QxSqlCompare(); + + virtual QString toString() const; + virtual void resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch = NULL) const; + virtual void postProcess(QString &sql) const; + + virtual IxSqlElement::type_class getTypeClass() const; + + protected: + virtual QString getExtraSettings() const; + virtual void setExtraSettings(const QString &s); + }; + + typedef std::shared_ptr QxSqlCompare_ptr; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _QX_SQL_COMPARE_H_ diff --git a/include/QxDao/QxSqlElement/QxSqlElement.h b/include/QxDao/QxSqlElement/QxSqlElement.h new file mode 100644 index 0000000..8bf8bb7 --- /dev/null +++ b/include/QxDao/QxSqlElement/QxSqlElement.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_ELEMENT_H_ +#define _QX_SQL_ELEMENT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // _QX_SQL_ELEMENT_H_ diff --git a/include/QxDao/QxSqlElement/QxSqlElementTemp.h b/include/QxDao/QxSqlElement/QxSqlElementTemp.h new file mode 100644 index 0000000..5ce498c --- /dev/null +++ b/include/QxDao/QxSqlElement/QxSqlElementTemp.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_ELEMENT_TEMP_H_ +#define _QX_SQL_ELEMENT_TEMP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlElementTemp.h + * \author XDL Team + * \ingroup QxDao + * \brief Temporary SQL element (need to be cloned to be used) + */ + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::QxSqlElementTemp : temporary SQL element (need to be cloned to be used) + */ + class QX_DLL_EXPORT QxSqlElementTemp : public IxSqlElement + { + + public: + QxSqlElementTemp(); + virtual ~QxSqlElementTemp(); + + virtual QString toString() const; + virtual void resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch = NULL) const; + virtual void postProcess(QString &sql) const; + + virtual IxSqlElement::type_class getTypeClass() const; + + protected: + virtual QString getExtraSettings() const; + virtual void setExtraSettings(const QString &s); + }; + + typedef std::shared_ptr QxSqlElementTemp_ptr; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _QX_SQL_ELEMENT_TEMP_H_ diff --git a/include/QxDao/QxSqlElement/QxSqlEmbedQuery.h b/include/QxDao/QxSqlElement/QxSqlEmbedQuery.h new file mode 100644 index 0000000..36b7672 --- /dev/null +++ b/include/QxDao/QxSqlElement/QxSqlEmbedQuery.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_EMBED_QUERY_H_ +#define _QX_SQL_EMBED_QUERY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlEmbedQuery.h + * \author XDL Team + * \ingroup QxDao + * \brief SQL element to embed a SQL sub-query inside a parent SQL query + */ + +#include + +namespace qx +{ + class QxSqlQuery; +} // namespace qx + +namespace qx +{ + namespace dao + { + namespace detail + { + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::QxSqlEmbedQuery : SQL element to embed a SQL sub-query inside a parent SQL query + */ + class QX_DLL_EXPORT QxSqlEmbedQuery : public IxSqlElement + { + + public: + enum type + { + _none, + _in, + _not_in, + _is_equal_to, + _is_not_equal_to + }; + + private: + struct QxSqlEmbedQueryImpl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + + public: + QxSqlEmbedQuery(QxSqlEmbedQuery::type type = QxSqlEmbedQuery::_none); + QxSqlEmbedQuery(int index, QxSqlEmbedQuery::type type = QxSqlEmbedQuery::_none); + virtual ~QxSqlEmbedQuery(); + + void setQuery(const qx::QxSqlQuery &query); + + virtual QString toString() const; + virtual void resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch = NULL) const; + virtual void postProcess(QString &sql) const; + + virtual IxSqlElement::type_class getTypeClass() const; + + protected: + virtual QString getExtraSettings() const; + virtual void setExtraSettings(const QString &s); + }; + + typedef std::shared_ptr QxSqlEmbedQuery_ptr; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _QX_SQL_EMBED_QUERY_H_ diff --git a/include/QxDao/QxSqlElement/QxSqlExpression.h b/include/QxDao/QxSqlElement/QxSqlExpression.h new file mode 100644 index 0000000..b36c710 --- /dev/null +++ b/include/QxDao/QxSqlElement/QxSqlExpression.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_EXPRESSION_H_ +#define _QX_SQL_EXPRESSION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlExpression.h + * \author XDL Team + * \ingroup QxDao + * \brief SQL element to build a SQL expression (WHERE, AND, OR, etc.) + */ + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::QxSqlExpression : SQL element to build a SQL expression (WHERE, AND, OR, etc.) + */ + class QX_DLL_EXPORT QxSqlExpression : public IxSqlElement + { + + public: + enum type + { + _where, + _and, + _or, + _open_parenthesis, + _close_parenthesis + }; + + protected: + QxSqlExpression::type m_type; + + public: + QxSqlExpression(); + QxSqlExpression(int index, QxSqlExpression::type t); + virtual ~QxSqlExpression(); + + virtual QString toString() const; + virtual void resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch = NULL) const; + virtual void postProcess(QString &sql) const; + + virtual IxSqlElement::type_class getTypeClass() const; + + protected: + virtual QString getExtraSettings() const; + virtual void setExtraSettings(const QString &s); + }; + + typedef std::shared_ptr QxSqlExpression_ptr; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _QX_SQL_EXPRESSION_H_ diff --git a/include/QxDao/QxSqlElement/QxSqlFreeText.h b/include/QxDao/QxSqlElement/QxSqlFreeText.h new file mode 100644 index 0000000..51bb811 --- /dev/null +++ b/include/QxDao/QxSqlElement/QxSqlFreeText.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_FREE_TEXT_H_ +#define _QX_SQL_FREE_TEXT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlFreeText.h + * \author XDL Team + * \ingroup QxDao + * \brief Possibility to add free text to SQL query + */ + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::QxSqlFreeText : possibility to add free text to SQL query + */ + class QX_DLL_EXPORT QxSqlFreeText : public IxSqlElement + { + + protected: + QString m_sText; //!< Custom SQL text to insert to SQL query + + public: + QxSqlFreeText(); + QxSqlFreeText(int index); + virtual ~QxSqlFreeText(); + + virtual QString toString() const; + virtual void resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch = NULL) const; + virtual void postProcess(QString &sql) const; + + virtual IxSqlElement::type_class getTypeClass() const; + + void setText(const QString &txt); + + protected: + virtual QString getExtraSettings() const; + virtual void setExtraSettings(const QString &s); + }; + + typedef std::shared_ptr QxSqlFreeText_ptr; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _QX_SQL_FREE_TEXT_H_ diff --git a/include/QxDao/QxSqlElement/QxSqlIn.h b/include/QxDao/QxSqlElement/QxSqlIn.h new file mode 100644 index 0000000..a50637f --- /dev/null +++ b/include/QxDao/QxSqlElement/QxSqlIn.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_IN_H_ +#define _QX_SQL_IN_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlIn.h + * \author XDL Team + * \ingroup QxDao + * \brief SQL element to verify a list of values (IN, NOT IN, etc.) + */ + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::QxSqlIn : SQL element to verify a list of values (IN, NOT IN, etc.) + */ + class QX_DLL_EXPORT QxSqlIn : public IxSqlElement + { + + public: + enum type + { + _in, + _not_in, + _in_select, + _not_in_select + }; + + protected: + QxSqlIn::type m_type; + + public: + QxSqlIn(); + QxSqlIn(int index, QxSqlIn::type t); + virtual ~QxSqlIn(); + + virtual QString toString() const; + virtual void resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch = NULL) const; + virtual void postProcess(QString &sql) const; + + virtual IxSqlElement::type_class getTypeClass() const; + + protected: + virtual QString getExtraSettings() const; + virtual void setExtraSettings(const QString &s); + }; + + typedef std::shared_ptr QxSqlIn_ptr; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _QX_SQL_IN_H_ diff --git a/include/QxDao/QxSqlElement/QxSqlIsBetween.h b/include/QxDao/QxSqlElement/QxSqlIsBetween.h new file mode 100644 index 0000000..c0a9ed2 --- /dev/null +++ b/include/QxDao/QxSqlElement/QxSqlIsBetween.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_IS_BETWEEN_H_ +#define _QX_SQL_IS_BETWEEN_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlIsBetween.h + * \author XDL Team + * \ingroup QxDao + * \brief SQL element to verify if a value is included into 2 other values + */ + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::QxSqlIsBetween : SQL element to verify if a value is included into 2 other values + */ + class QX_DLL_EXPORT QxSqlIsBetween : public IxSqlElement + { + + public: + enum type + { + _is_between, + _is_not_between + }; + + protected: + QxSqlIsBetween::type m_type; + + public: + QxSqlIsBetween(); + QxSqlIsBetween(int index, QxSqlIsBetween::type t); + virtual ~QxSqlIsBetween(); + + virtual QString toString() const; + virtual void resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch = NULL) const; + virtual void postProcess(QString &sql) const; + + virtual IxSqlElement::type_class getTypeClass() const; + + protected: + virtual QString getExtraSettings() const; + virtual void setExtraSettings(const QString &s); + }; + + typedef std::shared_ptr QxSqlIsBetween_ptr; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _QX_SQL_IS_BETWEEN_H_ diff --git a/include/QxDao/QxSqlElement/QxSqlIsNull.h b/include/QxDao/QxSqlElement/QxSqlIsNull.h new file mode 100644 index 0000000..bc5bf3b --- /dev/null +++ b/include/QxDao/QxSqlElement/QxSqlIsNull.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_IS_NULL_H_ +#define _QX_SQL_IS_NULL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlIsNull.h + * \author XDL Team + * \ingroup QxDao + * \brief SQL element to verify if a value is null or not null (IS NULL, IS NOT NULL) + */ + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::QxSqlIsNull : SQL element to verify if a value is null or not null (IS NULL, IS NOT NULL) + */ + class QX_DLL_EXPORT QxSqlIsNull : public IxSqlElement + { + + public: + enum type + { + _is_null, + _is_not_null + }; + + protected: + QxSqlIsNull::type m_type; + + public: + QxSqlIsNull(); + QxSqlIsNull(int index, QxSqlIsNull::type t); + virtual ~QxSqlIsNull(); + + virtual QString toString() const; + virtual void resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch = NULL) const; + virtual void postProcess(QString &sql) const; + + virtual IxSqlElement::type_class getTypeClass() const; + + protected: + virtual QString getExtraSettings() const; + virtual void setExtraSettings(const QString &s); + }; + + typedef std::shared_ptr QxSqlIsNull_ptr; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _QX_SQL_IS_NULL_H_ diff --git a/include/QxDao/QxSqlElement/QxSqlLimit.h b/include/QxDao/QxSqlElement/QxSqlLimit.h new file mode 100644 index 0000000..e8ebe77 --- /dev/null +++ b/include/QxDao/QxSqlElement/QxSqlLimit.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_LIMIT_H_ +#define _QX_SQL_LIMIT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlLimit.h + * \author XDL Team + * \ingroup QxDao + * \brief SQL element to limit rows count fetched from database + */ + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::QxSqlLimit : SQL element to limit rows count fetched from database + */ + class QX_DLL_EXPORT QxSqlLimit : public IxSqlElement + { + + public: + QxSqlLimit(); + QxSqlLimit(int index); + virtual ~QxSqlLimit(); + + virtual QString toString() const; + virtual void resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch = NULL) const; + virtual void postProcess(QString &sql) const; + + int getStartRow() const; + int getRowsCount() const; + int getMaxRow() const; + bool getWithTies() const; + + QString getStartRow_ParamKey() const; + QString getRowsCount_ParamKey() const; + QString getMaxRow_ParamKey() const; + + virtual IxSqlElement::type_class getTypeClass() const; + + protected: + virtual QString getExtraSettings() const; + virtual void setExtraSettings(const QString &s); + }; + + typedef std::shared_ptr QxSqlLimit_ptr; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _QX_SQL_LIMIT_H_ diff --git a/include/QxDao/QxSqlElement/QxSqlSort.h b/include/QxDao/QxSqlElement/QxSqlSort.h new file mode 100644 index 0000000..a657eb3 --- /dev/null +++ b/include/QxDao/QxSqlElement/QxSqlSort.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_SORT_H_ +#define _QX_SQL_SORT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlSort.h + * \author XDL Team + * \ingroup QxDao + * \brief SQL element to sort or to group list of elements fetched from database (ORDER BY, GROUP BY) + */ + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::QxSqlSort : SQL element to sort or to group list of elements fetched from database (ORDER BY, GROUP BY) + */ + class QX_DLL_EXPORT QxSqlSort : public IxSqlElement + { + + public: + enum type + { + _order_asc, + _order_desc, + _group_by + }; + + protected: + QxSqlSort::type m_type; + + public: + QxSqlSort(); + QxSqlSort(int index, QxSqlSort::type t); + virtual ~QxSqlSort(); + + virtual QString toString() const; + virtual void resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch = NULL) const; + virtual void postProcess(QString &sql) const; + + virtual IxSqlElement::type_class getTypeClass() const; + + protected: + virtual QString getExtraSettings() const; + virtual void setExtraSettings(const QString &s); + }; + + typedef std::shared_ptr QxSqlSort_ptr; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _QX_SQL_SORT_H_ diff --git a/include/QxDao/QxSqlError.h b/include/QxDao/QxSqlError.h new file mode 100644 index 0000000..f1ecda3 --- /dev/null +++ b/include/QxDao/QxSqlError.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_ERROR_H_ +#define _QX_SQL_ERROR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlError.h + * \author XDL Team + * \ingroup QxDao + * \brief Define a SQL error exception and retrieve QSqlError type of Qt library + */ + +#include +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + + /*! + * \ingroup QxDao + * \brief qx::dao::sql_error : define a SQL error exception and retrieve QSqlError type of Qt library + */ + class sql_error : public std::exception + { + + private: + QSqlError m_error; + QByteArray m_errorMessage; + + public: +#if (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + sql_error(const QSqlError &err) : std::exception(), m_error(err) + { + if (!m_error.text().isEmpty() && (m_error.type() == QSqlError::NoError)) + { + m_error = QSqlError(m_error.driverText(), m_error.databaseText(), QSqlError::UnknownError, m_error.nativeErrorCode()); + }; + m_errorMessage = m_error.text().toLocal8Bit(); + } +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + sql_error(const QSqlError &err) : std::exception(), m_error(err) + { + if (!m_error.text().isEmpty() && (m_error.type() == QSqlError::NoError)) + { + m_error.setType(QSqlError::UnknownError); + }; + m_errorMessage = m_error.text().toLocal8Bit(); + } +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + virtual ~sql_error() throw() { ; } + + virtual const char *what() const throw() + { + if (m_error.isValid()) + { + return m_errorMessage.constData(); + } + else + { + return ""; + } + } + QSqlError get() const { return m_error; } + }; + + } // namespace dao +} // namespace qx + +#endif // _QX_SQL_ERROR_H_ diff --git a/include/QxDao/QxSqlGenerator/IxSqlGenerator.h b/include/QxDao/QxSqlGenerator/IxSqlGenerator.h new file mode 100644 index 0000000..42372fe --- /dev/null +++ b/include/QxDao/QxSqlGenerator/IxSqlGenerator.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_SQL_GENERATOR_H_ +#define _IX_SQL_GENERATOR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxSqlGenerator.h + * \author XDL Team + * \ingroup QxDao + * \brief Common interface for all SQL generators to build SQL query specific for each database + */ + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + class IxDao_Helper; + class IxSqlElement; + class QxSqlCompare; + class QxSqlElementTemp; + class QxSqlEmbedQuery; + class QxSqlExpression; + class QxSqlFreeText; + class QxSqlIn; + class QxSqlIsBetween; + class QxSqlIsNull; + class QxSqlLimit; + class QxSqlSort; + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::IxSqlGenerator : common interface for all SQL generators to build SQL query specific for each database + */ + class QX_DLL_EXPORT IxSqlGenerator + { + + public: + IxSqlGenerator(); + virtual ~IxSqlGenerator(); + + virtual void init() = 0; + virtual QString getAutoIncrement() const = 0; + virtual QString getWildCard() const = 0; + virtual QString getTableAliasSep() const = 0; + virtual QString getLimit(const QxSqlLimit *pLimit) const = 0; + virtual void resolveLimit(QSqlQuery &query, const QxSqlLimit *pLimit, qx::QxCollection *pLstExecBatch = NULL) const = 0; + virtual void postProcess(QString &sql, const QxSqlLimit *pLimit) const = 0; + virtual void onBeforeInsert(IxDao_Helper *pDaoHelper, void *pOwner) const = 0; + virtual void onAfterInsert(IxDao_Helper *pDaoHelper, void *pOwner) const = 0; + virtual void onBeforeUpdate(IxDao_Helper *pDaoHelper, void *pOwner) const = 0; + virtual void onAfterUpdate(IxDao_Helper *pDaoHelper, void *pOwner) const = 0; + virtual void onBeforeDelete(IxDao_Helper *pDaoHelper, void *pOwner) const = 0; + virtual void onAfterDelete(IxDao_Helper *pDaoHelper, void *pOwner) const = 0; + virtual void checkSqlInsert(IxDao_Helper *pDaoHelper, QString &sql) const = 0; + virtual void onBeforeSqlPrepare(IxDao_Helper *pDaoHelper, QString &sql) const = 0; + virtual void formatSqlQuery(IxDao_Helper *pDaoHelper, QString &sql) const = 0; + }; + + typedef std::shared_ptr IxSqlGenerator_ptr; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _IX_SQL_GENERATOR_H_ diff --git a/include/QxDao/QxSqlGenerator/QxSqlGenerator.h b/include/QxDao/QxSqlGenerator/QxSqlGenerator.h new file mode 100644 index 0000000..09b2722 --- /dev/null +++ b/include/QxDao/QxSqlGenerator/QxSqlGenerator.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_GENERATOR_H_ +#define _QX_SQL_GENERATOR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#endif // _QX_SQL_GENERATOR_H_ diff --git a/include/QxDao/QxSqlGenerator/QxSqlGenerator_MSSQLServer.h b/include/QxDao/QxSqlGenerator/QxSqlGenerator_MSSQLServer.h new file mode 100644 index 0000000..168a607 --- /dev/null +++ b/include/QxDao/QxSqlGenerator/QxSqlGenerator_MSSQLServer.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_GENERATOR_MSSQLSERVER_H_ +#define _QX_SQL_GENERATOR_MSSQLSERVER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlGenerator_MSSQLServer.h + * \author XDL Team + * \ingroup QxDao + * \brief SQL generator for Microsoft SQL Server database + */ + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::QxSqlGenerator_MSSQLServer : SQL generator for Microsoft SQL Server database + */ + class QX_DLL_EXPORT QxSqlGenerator_MSSQLServer : public QxSqlGenerator_Standard + { + + public: + QxSqlGenerator_MSSQLServer(); + virtual ~QxSqlGenerator_MSSQLServer(); + + virtual void init(); + virtual QString getLimit(const QxSqlLimit *pLimit) const; + virtual void resolveLimit(QSqlQuery &query, const QxSqlLimit *pLimit, qx::QxCollection *pLstExecBatch = NULL) const; + virtual void postProcess(QString &sql, const QxSqlLimit *pLimit) const; + + private: + void initSqlTypeByClassName() const; + }; + + typedef std::shared_ptr QxSqlGenerator_MSSQLServer_ptr; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _QX_SQL_GENERATOR_MSSQLSERVER_H_ diff --git a/include/QxDao/QxSqlGenerator/QxSqlGenerator_MySQL.h b/include/QxDao/QxSqlGenerator/QxSqlGenerator_MySQL.h new file mode 100644 index 0000000..3e65c59 --- /dev/null +++ b/include/QxDao/QxSqlGenerator/QxSqlGenerator_MySQL.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_GENERATOR_MYSQL_H_ +#define _QX_SQL_GENERATOR_MYSQL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlGenerator_MySQL.h + * \author XDL Team + * \ingroup QxDao + * \brief SQL generator for MySQL database + */ + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::QxSqlGenerator_MySQL : SQL generator for MySQL database + */ + class QX_DLL_EXPORT QxSqlGenerator_MySQL : public QxSqlGenerator_Standard + { + + public: + QxSqlGenerator_MySQL(); + virtual ~QxSqlGenerator_MySQL(); + + virtual QString getAutoIncrement() const; + + private: + void initSqlTypeByClassName() const; + }; + + typedef std::shared_ptr QxSqlGenerator_MySQL_ptr; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _QX_SQL_GENERATOR_MYSQL_H_ diff --git a/include/QxDao/QxSqlGenerator/QxSqlGenerator_Oracle.h b/include/QxDao/QxSqlGenerator/QxSqlGenerator_Oracle.h new file mode 100644 index 0000000..e5e3dcd --- /dev/null +++ b/include/QxDao/QxSqlGenerator/QxSqlGenerator_Oracle.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_GENERATOR_ORACLE_H_ +#define _QX_SQL_GENERATOR_ORACLE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlGenerator_Oracle.h + * \author XDL Team + * \ingroup QxDao + * \brief SQL generator for Oracle database + */ + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::QxSqlGenerator_Oracle : SQL generator for Oracle database + */ + class QX_DLL_EXPORT QxSqlGenerator_Oracle : public QxSqlGenerator_Standard + { + + protected: + bool m_bOldLimitSyntax; //!< Use old limit syntax (for Oracle version < 12.1), more details here : https://stackoverflow.com/questions/470542/how-do-i-limit-the-number-of-rows-returned-by-an-oracle-query-after-ordering + bool m_bManageLastInsertId; //!< Manage last insert id using RETURNING INTO syntax (thx to Romain Macureau and Abdennour Boutrig) + + public: + QxSqlGenerator_Oracle(); + QxSqlGenerator_Oracle(bool bManageLastInsertId); + virtual ~QxSqlGenerator_Oracle(); + + virtual QString getTableAliasSep() const; + virtual QString getLimit(const QxSqlLimit *pLimit) const; + virtual void resolveLimit(QSqlQuery &query, const QxSqlLimit *pLimit, qx::QxCollection *pLstExecBatch = NULL) const; + virtual void postProcess(QString &sql, const QxSqlLimit *pLimit) const; + virtual void checkSqlInsert(IxDao_Helper *pDaoHelper, QString &sql) const; + virtual void onBeforeInsert(IxDao_Helper *pDaoHelper, void *pOwner) const; + virtual void onAfterInsert(IxDao_Helper *pDaoHelper, void *pOwner) const; + + bool getOldLimitSyntax() const; + void setOldLimitSyntax(bool b); + + bool getManageLastInsertId() const; + void setManageLastInsertId(bool b); + + private: + void initSqlTypeByClassName() const; + }; + + typedef std::shared_ptr QxSqlGenerator_Oracle_ptr; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _QX_SQL_GENERATOR_ORACLE_H_ diff --git a/include/QxDao/QxSqlGenerator/QxSqlGenerator_PostgreSQL.h b/include/QxDao/QxSqlGenerator/QxSqlGenerator_PostgreSQL.h new file mode 100644 index 0000000..ed92331 --- /dev/null +++ b/include/QxDao/QxSqlGenerator/QxSqlGenerator_PostgreSQL.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_GENERATOR_POSTGRESQL_H_ +#define _QX_SQL_GENERATOR_POSTGRESQL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlGenerator_PostgreSQL.h + * \author XDL Team + * \ingroup QxDao + * \brief SQL generator for PostgreSQL database + */ + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::QxSqlGenerator_PostgreSQL : SQL generator for PostgreSQL database + */ + class QX_DLL_EXPORT QxSqlGenerator_PostgreSQL : public QxSqlGenerator_Standard + { + + public: + QxSqlGenerator_PostgreSQL(); + virtual ~QxSqlGenerator_PostgreSQL(); + + virtual void checkSqlInsert(IxDao_Helper *pDaoHelper, QString &sql) const; + virtual void onAfterInsert(IxDao_Helper *pDaoHelper, void *pOwner) const; + + private: + void initSqlTypeByClassName() const; + }; + + typedef std::shared_ptr QxSqlGenerator_PostgreSQL_ptr; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _QX_SQL_GENERATOR_POSTGRESQL_H_ diff --git a/include/QxDao/QxSqlGenerator/QxSqlGenerator_SQLite.h b/include/QxDao/QxSqlGenerator/QxSqlGenerator_SQLite.h new file mode 100644 index 0000000..54e7474 --- /dev/null +++ b/include/QxDao/QxSqlGenerator/QxSqlGenerator_SQLite.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_GENERATOR_SQLITE_H_ +#define _QX_SQL_GENERATOR_SQLITE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlGenerator_SQLite.h + * \author XDL Team + * \ingroup QxDao + * \brief SQL generator for SQLite database + */ + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::QxSqlGenerator_SQLite : SQL generator for SQLite database + */ + class QX_DLL_EXPORT QxSqlGenerator_SQLite : public QxSqlGenerator_Standard + { + + public: + QxSqlGenerator_SQLite(); + virtual ~QxSqlGenerator_SQLite(); + + private: + void initSqlTypeByClassName() const; + }; + + typedef std::shared_ptr QxSqlGenerator_SQLite_ptr; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _QX_SQL_GENERATOR_SQLITE_H_ diff --git a/include/QxDao/QxSqlGenerator/QxSqlGenerator_Standard.h b/include/QxDao/QxSqlGenerator/QxSqlGenerator_Standard.h new file mode 100644 index 0000000..83d7530 --- /dev/null +++ b/include/QxDao/QxSqlGenerator/QxSqlGenerator_Standard.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_GENERATOR_STANDARD_H_ +#define _QX_SQL_GENERATOR_STANDARD_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlGenerator_Standard.h + * \author XDL Team + * \ingroup QxDao + * \brief SQL generator to build standard SQL query + */ + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + /*! + * \ingroup QxDao + * \brief qx::dao::detail::QxSqlGenerator_Standard : SQL generator to build standard SQL query + */ + class QX_DLL_EXPORT QxSqlGenerator_Standard : public IxSqlGenerator + { + + public: + QxSqlGenerator_Standard(); + virtual ~QxSqlGenerator_Standard(); + + virtual void init(); + virtual QString getAutoIncrement() const; + virtual QString getWildCard() const; + virtual QString getTableAliasSep() const; + virtual QString getLimit(const QxSqlLimit *pLimit) const; + virtual void resolveLimit(QSqlQuery &query, const QxSqlLimit *pLimit, qx::QxCollection *pLstExecBatch = NULL) const; + virtual void postProcess(QString &sql, const QxSqlLimit *pLimit) const; + virtual void onBeforeInsert(IxDao_Helper *pDaoHelper, void *pOwner) const; + virtual void onAfterInsert(IxDao_Helper *pDaoHelper, void *pOwner) const; + virtual void onBeforeUpdate(IxDao_Helper *pDaoHelper, void *pOwner) const; + virtual void onAfterUpdate(IxDao_Helper *pDaoHelper, void *pOwner) const; + virtual void onBeforeDelete(IxDao_Helper *pDaoHelper, void *pOwner) const; + virtual void onAfterDelete(IxDao_Helper *pDaoHelper, void *pOwner) const; + virtual void checkSqlInsert(IxDao_Helper *pDaoHelper, QString &sql) const; + virtual void onBeforeSqlPrepare(IxDao_Helper *pDaoHelper, QString &sql) const; + virtual void formatSqlQuery(IxDao_Helper *pDaoHelper, QString &sql) const; + }; + + typedef std::shared_ptr QxSqlGenerator_Standard_ptr; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _QX_SQL_GENERATOR_STANDARD_H_ diff --git a/include/QxDao/QxSqlJoin.h b/include/QxDao/QxSqlJoin.h new file mode 100644 index 0000000..7301c72 --- /dev/null +++ b/include/QxDao/QxSqlJoin.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_JOIN_H_ +#define _QX_SQL_JOIN_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlJoin.h + * \author XDL Team + * \ingroup QxDao + * \brief Define how to join 2 tables into SQL query (LEFT OUTER JOIN, INNER JOIN, etc...) + */ + +#define QX_LEFT_OUTER_JOIN QString("->") +#define QX_INNER_JOIN QString(">>") + +namespace qx +{ + namespace dao + { + + /*! + * \ingroup QxDao + * \brief qx::dao::sql_join : define how to join 2 tables into SQL query (LEFT OUTER JOIN, INNER JOIN, etc...) + */ + struct sql_join + { + + enum join_type + { + no_join, + left_outer_join, + inner_join + }; + }; + + } // namespace dao +} // namespace qx + +#endif // _QX_SQL_JOIN_H_ diff --git a/include/QxDao/QxSqlQuery.h b/include/QxDao/QxSqlQuery.h new file mode 100644 index 0000000..9e87efe --- /dev/null +++ b/include/QxDao/QxSqlQuery.h @@ -0,0 +1,729 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_QUERY_H_ +#define _QX_SQL_QUERY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlQuery.h + * \author XDL Team + * \ingroup QxDao + * \brief Define a user SQL query added to default SQL query builded by QxOrm library, and used by qx::dao::xxx functions to filter elements fetched from database + */ + +#ifdef Q_COMPILER_INITIALIZER_LISTS +#include +#endif // Q_COMPILER_INITIALIZER_LISTS + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#include +#include +#include +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#ifndef _QX_NO_JSON +#include +#include +#include +#include +#endif // _QX_NO_JSON + +#include + +#include + +#include + +#include + +#include + +#include + +#include + +namespace qx +{ + class QxSqlQuery; +} // namespace qx + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const qx::QxSqlQuery &t, const unsigned int file_version); + template + inline void qx_load(Archive &ar, qx::QxSqlQuery &t, const unsigned int file_version); + + } // namespace serialization +} // namespace boost +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +QX_DLL_EXPORT QDataStream &operator<<(QDataStream &stream, const qx::QxSqlQuery &t) QX_USED; +QX_DLL_EXPORT QDataStream &operator>>(QDataStream &stream, qx::QxSqlQuery &t) QX_USED; + +#ifndef _QX_NO_JSON +namespace qx +{ + namespace cvt + { + namespace detail + { + template <> + struct QxConvert_ToJson; + template <> + struct QxConvert_FromJson; + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const qx::QxSqlQuery &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, qx::QxSqlQuery &t, const QString &format) QX_USED; + } // namespace detail + } // namespace cvt +} // namespace qx +#endif // _QX_NO_JSON + +namespace qx +{ + + /*! + * \ingroup QxDao + * \brief qx::QxSqlQuery : define a user SQL query added to default SQL query builded by QxOrm library, and used by qx::dao::xxx functions to filter elements fetched from database + * + * The class qx::QxSqlQuery (or its typedef qx_query) is used to communicate with database (to filter, to sort, etc.) in two different ways : + * - writing manually SQL query ; + * - using C++ methods with a syntax similar to SQL (same concept than the great library SubSonic for .Net). + * + * With the first method (writing manually SQL query), you can use some optimizations specific for each database. + * The second method (using C++ code to build SQL query) binds automatically SQL parameters without using qx::QxSqlQuery::bind() function. + * + * Here is an example with qx::QxSqlQuery class writing manually a SQL query : + * \code + // Build a SQL query to fetch only 'author' of type 'female' + qx::QxSqlQuery query("WHERE author.sex = :sex"); + query.bind(":sex", author::female); + + QList list_of_female; + QSqlError daoError = qx::dao::fetch_by_query(query, list_of_female); + + // Here we can work with the collection provided by database + for (long l = 0; l < list_of_female.count(); l++) { ; } + * \endcode + * + * QxOrm library provides 3 styles to write SQL parameters. + * This style can be modified for a project using the following method qx::QxSqlDatabase::getSingleton()->setSqlPlaceHolderStyle() : + * - ph_style_2_point_name : "WHERE author.sex = :sex" (default style) ; + * - ph_style_at_name : "WHERE author.sex = @sex" ; + * - ph_style_question_mark : "WHERE author.sex = ?". + * + * Here is the same example using C++ code of the class qx::QxSqlQuery (or its typedef qx_query) to build query automatically : + * \code + // Build a SQL query to fetch only 'author' of type 'female' + qx_query query; + query.where("author.sex").isEqualTo(author::female); + + QList list_of_female; + QSqlError daoError = qx::dao::fetch_by_query(query, list_of_female); + + // Here we can work with the collection provided by database + for (long l = 0; l < list_of_female.count(); l++) { ; } + * \endcode + * + * With C++ methods of qx::QxSqlQuery class, you don't have to bind any SQL parameter, and the syntax is similar to real SQL. + * All SQL parameters will be provided to database automatically with the following style : qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle(). + * + * Here is an example with many methods of qx::QxSqlQuery class (or its typedef qx_query) : + * \code + qx_query query; + query.where("sex").isEqualTo(author::female) + .and_("age").isGreaterThan(38) + .or_("last_name").isNotEqualTo("Dupont") + .or_("first_name").like("Alfred") + .and_OpenParenthesis("id").isLessThanOrEqualTo(999) + .and_("birth_date").isBetween(date1, date2) + .closeParenthesis() + .or_("id").in(50, 999, 11, 23, 78945) + .and_("is_deleted").isNotNull() + .orderAsc("last_name", "first_name", "sex") + .limit(50, 150); + * \endcode + * + * This code will produce following SQL for MySQL, PostgreSQL and SQLite databases (for Oracle and SQLServer, there is a specific process for limit() method) : + * \code + WHERE sex = :sex_1_0 + AND age > :age_3_0 + OR last_name <> :last_name_5_0 + OR first_name LIKE :first_name_7_0 + AND ( id <= :id_10_0 AND birth_date BETWEEN :birth_date_12_0_1 AND :birth_date_12_0_2 ) + OR id IN (:id_15_0_0, :id_15_0_1, :id_15_0_2, :id_15_0_3, :id_15_0_4) + AND is_deleted IS NOT NULL + ORDER BY last_name ASC, first_name ASC, sex ASC + LIMIT :limit_rows_count_19_0 OFFSET :offset_start_row_19_0 + * \endcode + * + * Here is the list of all functions available to use qx::QxSqlQuery class (or its typedef qx_query) : + * \code + // with functions into namespace qx::dao + qx::dao::count() + qx::dao::fetch_by_query() + qx::dao::update_by_query() + qx::dao::delete_by_query() + qx::dao::destroy_by_query() + qx::dao::fetch_by_query_with_relation() + qx::dao::fetch_by_query_with_all_relation() + qx::dao::update_by_query_with_relation() + qx::dao::update_by_query_with_all_relation() + qx::dao::update_optimized_by_query() + + // with qx::QxSession class + qx::QxSession::count() + qx::QxSession::fetchByQuery() + qx::QxSession::update() + qx::QxSession::deleteByQuery() + qx::QxSession::destroyByQuery() + + // with qx::QxRepository class + qx::QxRepository::count() + qx::QxRepository::fetchByQuery() + qx::QxRepository::update() + qx::QxRepository::deleteByQuery() + qx::QxRepository::destroyByQuery() + * \endcode + * + * Note : those functions have 2 other optionals parameters : + * - const QStringList & columns : to indicate columns to fetch (by default, all columns are fetched) ; + * - const QStringList & relation : to indicate relations to fetch (one-to-one, one-to-many, many-to-one and many-to-many defined into void qx::register_class() mapping function by class), by default there is no relation fetched. + * + * Other note : it's possible to call a stored procedure using qx::QxSqlQuery class, for example : + * \code + qx_query query("CALL MyStoredProc(:param1, :param2)"); + query.bind(":param1", "myValue1"); + query.bind(":param2", 5024, QSql::InOut); + QSqlError daoError = qx::dao::call_query(query); + QVariant vNewValue = query.boundValue(":param2"); + query.dumpSqlResult(); + * \endcode + * + * If the stored procedure returns a resultset, you can iterate over each rows and fields using the following methods (after calling qx::dao::call_query() function) : + * - long qx::QxSqlQuery::getSqlResultRowCount() const; + * - long qx::QxSqlQuery::getSqlResultColumnCount() const; + * - QVariant qx::QxSqlQuery::getSqlResultAt(long row, long column) const; + * - QVariant qx::QxSqlQuery::getSqlResultAt(long row, const QString & column) const; + * - QVector qx::QxSqlQuery::getSqlResultAllColumns() const; + * - void qx::QxSqlQuery::dumpSqlResult(); + * + * Other note : to add your own SQL query methods (for example, some databases provide non-standard specifics SQL functions) : + * - create a derived class based on qx::QxSqlQuery class ; + * - if your C++ compiler supports covariant return type, add the macro QX_SQL_QUERY_DERIVED_IMPL_COVARIANT_RETURN_TYPE_HPP(myClass) in the definition of your class (myClass.h) ; + * - if your C++ compiler supports covariant return type, add the macro QX_SQL_QUERY_DERIVED_IMPL_COVARIANT_RETURN_TYPE_CPP(myClass) in the implementation of your class (myClass.cpp) ; + * - add all SQL specifics functions in your derived class. + */ + class QX_DLL_EXPORT QxSqlQuery + { + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + template + friend inline void boost::serialization::qx_save(Archive &ar, const qx::QxSqlQuery &t, const unsigned int file_version); + template + friend inline void boost::serialization::qx_load(Archive &ar, qx::QxSqlQuery &t, const unsigned int file_version); +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + friend QX_DLL_EXPORT QDataStream & ::operator<<(QDataStream &stream, const qx::QxSqlQuery &t); + friend QX_DLL_EXPORT QDataStream & ::operator>>(QDataStream &stream, qx::QxSqlQuery &t); + +#ifndef _QX_NO_JSON + friend struct qx::cvt::detail::QxConvert_ToJson; + friend struct qx::cvt::detail::QxConvert_FromJson; + friend QX_DLL_EXPORT QJsonValue qx::cvt::detail::QxConvert_ToJson_Helper(const qx::QxSqlQuery &t, const QString &format); + friend QX_DLL_EXPORT qx_bool qx::cvt::detail::QxConvert_FromJson_Helper(const QJsonValue &j, qx::QxSqlQuery &t, const QString &format); +#endif // _QX_NO_JSON + + public: + typedef std::function type_fct_on_before_sql_prepare; + + protected: + struct QxSqlResult + { + QHash positionByKey; + QHash positionByKeyUpper; + QVector> values; + }; + + typedef std::tuple type_bind_value; + + QStringList m_sQuery; //!< Query SQL with place-holder + QxCollection m_lstValue; //!< Bind value in this array + qx::dao::detail::IxSqlElement_ptr m_pSqlElementTemp; //!< Temporary SQL element + QList m_lstSqlElement; //!< List of all SQL elements to build SQL query + int m_iSqlElementIndex; //!< Current index of SQL element + int m_iParenthesisCount; //!< Current parenthesis count + bool m_bDistinct; //!< Replace SELECT by SELECT DISTINCT in SQL query + std::shared_ptr m_pSqlResult; //!< All results returning by SQL query or stored procedure (after calling qx::dao::call_query function) + QVariant m_vResponse; //!< Can be used to store some responses (from MongoDB database for example in JSON format) + QString m_sType; //!< Query type (for example : 'aggregate' or 'cursor' for MongoDB database) + QHash> m_lstJoinQueryUser; //!< List of SQL queries defined by user to add inside relationships joins (LEFT OUTER JOIN, INNER JOIN), for example : INNER JOIN my_table2 m2 ON (m1.id = m2.parent_id AND (XXX)) + QList> m_lstJoinQueryToResolve; //!< List of SQL queries to resolve (in the right order) to add inside relationships joins (LEFT OUTER JOIN, INNER JOIN), for example : INNER JOIN my_table2 m2 ON (m1.id = m2.parent_id AND (XXX)) + type_fct_on_before_sql_prepare m_fctOnBeforeSqlPrepare; //!< Custom callback function to modify SQL query before preparing in database + + public: + QxSqlQuery(); + QxSqlQuery(const char *query, const QVariantList &values = QVariantList()); + QxSqlQuery(const QString &query, const QVariantList &values = QVariantList()); + QxSqlQuery(const QStringList &query); + QxSqlQuery(const QString &type, const QString &query); + QxSqlQuery(const QString &type, const QStringList &query); + virtual ~QxSqlQuery(); + +#ifndef _QX_NO_JSON +#ifdef Q_COMPILER_INITIALIZER_LISTS + QxSqlQuery(std::initializer_list> json); + QxSqlQuery(std::initializer_list> json, std::initializer_list> opts); + QxSqlQuery(const QString &type, std::initializer_list> json); + QxSqlQuery(const QString &type, std::initializer_list> json, std::initializer_list> opts); +#endif // Q_COMPILER_INITIALIZER_LISTS +#endif // _QX_NO_JSON + + QString query(); + QString queryAt(int idx) const; + void queryAt(int idx, const QString &query); + QVariant response() const; + QString type() const; + bool isEmpty() const; + bool isDistinct() const; + void clear(); + void resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch = NULL) const; + void resolveOutput(QSqlQuery &query, bool bFetchSqlResult); + void postProcess(QString &sql) const; + void setResponse(const QVariant &v); + void setType(const QString &s); + QString getJoinQuery(const QString &relationKey, const QString &relationAlias); + QString getJoinQueryHash(); + + QxSqlQuery &query(const QString &sQuery); + QxSqlQuery &bind(const QVariant &vValue, QSql::ParamType paramType = QSql::In); + QxSqlQuery &bind(const QString &sKey, const QVariant &vValue, QSql::ParamType paramType = QSql::In); + + QVariant boundValue(const QString &sKey) const; + QVariant boundValue(int iPosition) const; + + long getSqlResultRowCount() const; + long getSqlResultColumnCount() const; + QVariant getSqlResultAt(long row, long column) const; + QVariant getSqlResultAt(long row, const QString &column, bool caseSensitive = false) const; + QVector getSqlResultAt(long row) const; + QVector getSqlResultAllColumns() const; + void dumpSqlResult(); + + static void dumpBoundValues(const QSqlQuery &query); + + QxSqlQuery &setFctOnBeforeSqlPrepare(type_fct_on_before_sql_prepare fct); + void onBeforeSqlPrepare(QString &sql); + + private: + void verifyQuery() const QX_USED; + void fetchSqlResult(QSqlQuery &query); + + public: + /* -- All methods to build SQL query using C++ syntax -- */ + + virtual QxSqlQuery &distinct(); + + virtual QxSqlQuery &where(const QString &column); + virtual QxSqlQuery &where_OpenParenthesis(const QString &column); + virtual QxSqlQuery &and_(const QString &column); + virtual QxSqlQuery &and_OpenParenthesis(const QString &column); + virtual QxSqlQuery &or_(const QString &column); + virtual QxSqlQuery &or_OpenParenthesis(const QString &column); + + virtual QxSqlQuery &openParenthesis(); + virtual QxSqlQuery &closeParenthesis(); + + virtual QxSqlQuery &orderAsc(const QStringList &columns); + virtual QxSqlQuery &orderAsc(const QString &col1); + virtual QxSqlQuery &orderAsc(const QString &col1, const QString &col2); + virtual QxSqlQuery &orderAsc(const QString &col1, const QString &col2, const QString &col3); + virtual QxSqlQuery &orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4); + virtual QxSqlQuery &orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5); + virtual QxSqlQuery &orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6); + virtual QxSqlQuery &orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7); + virtual QxSqlQuery &orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8); + virtual QxSqlQuery &orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8, const QString &col9); + + virtual QxSqlQuery &orderDesc(const QStringList &columns); + virtual QxSqlQuery &orderDesc(const QString &col1); + virtual QxSqlQuery &orderDesc(const QString &col1, const QString &col2); + virtual QxSqlQuery &orderDesc(const QString &col1, const QString &col2, const QString &col3); + virtual QxSqlQuery &orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4); + virtual QxSqlQuery &orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5); + virtual QxSqlQuery &orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6); + virtual QxSqlQuery &orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7); + virtual QxSqlQuery &orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8); + virtual QxSqlQuery &orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8, const QString &col9); + + virtual QxSqlQuery &groupBy(const QStringList &columns); + virtual QxSqlQuery &groupBy(const QString &col1); + virtual QxSqlQuery &groupBy(const QString &col1, const QString &col2); + virtual QxSqlQuery &groupBy(const QString &col1, const QString &col2, const QString &col3); + virtual QxSqlQuery &groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4); + virtual QxSqlQuery &groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5); + virtual QxSqlQuery &groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6); + virtual QxSqlQuery &groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7); + virtual QxSqlQuery &groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8); + virtual QxSqlQuery &groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8, const QString &col9); + + virtual QxSqlQuery &limit(int rowsCount, int startRow = 0, bool withTies = false); + + virtual QxSqlQuery &like(const QString &val); + virtual QxSqlQuery ¬Like(const QString &val); + virtual QxSqlQuery &startsWith(const QString &val); + virtual QxSqlQuery &endsWith(const QString &val); + virtual QxSqlQuery &containsString(const QString &val); + + virtual QxSqlQuery &isEqualTo(const QVariant &val); + virtual QxSqlQuery &isNotEqualTo(const QVariant &val); + virtual QxSqlQuery &isGreaterThan(const QVariant &val); + virtual QxSqlQuery &isGreaterThanOrEqualTo(const QVariant &val); + virtual QxSqlQuery &isLessThan(const QVariant &val); + virtual QxSqlQuery &isLessThanOrEqualTo(const QVariant &val); + virtual QxSqlQuery &customOperator(const QString &sCustomOperator, const QVariant &val); + + virtual QxSqlQuery &in(const QVariantList &values); + virtual QxSqlQuery &in(const QVariant &val1); + virtual QxSqlQuery &in(const QVariant &val1, const QVariant &val2); + virtual QxSqlQuery &in(const QVariant &val1, const QVariant &val2, const QVariant &val3); + virtual QxSqlQuery &in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4); + virtual QxSqlQuery &in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5); + virtual QxSqlQuery &in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6); + virtual QxSqlQuery &in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7); + virtual QxSqlQuery &in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7, const QVariant &val8); + virtual QxSqlQuery &in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7, const QVariant &val8, const QVariant &val9); + + virtual QxSqlQuery ¬In(const QVariantList &values); + virtual QxSqlQuery ¬In(const QVariant &val1); + virtual QxSqlQuery ¬In(const QVariant &val1, const QVariant &val2); + virtual QxSqlQuery ¬In(const QVariant &val1, const QVariant &val2, const QVariant &val3); + virtual QxSqlQuery ¬In(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4); + virtual QxSqlQuery ¬In(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5); + virtual QxSqlQuery ¬In(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6); + virtual QxSqlQuery ¬In(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7); + virtual QxSqlQuery ¬In(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7, const QVariant &val8); + virtual QxSqlQuery ¬In(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7, const QVariant &val8, const QVariant &val9); + + virtual QxSqlQuery &in_Select(const QxSqlQuery &query); + virtual QxSqlQuery ¬In_Select(const QxSqlQuery &query); + virtual QxSqlQuery &isEqualTo_Select(const QxSqlQuery &query); + virtual QxSqlQuery &isNotEqualTo_Select(const QxSqlQuery &query); + + virtual QxSqlQuery &isNull(); + virtual QxSqlQuery &isNotNull(); + + virtual QxSqlQuery &isBetween(const QVariant &val1, const QVariant &val2); + virtual QxSqlQuery &isNotBetween(const QVariant &val1, const QVariant &val2); + + virtual QxSqlQuery &freeText(const QString &text, const QVariantList &values = QVariantList()); + + virtual QxSqlQuery &addJoinQuery(const QString &relationKeyOrAlias, const QxSqlQuery &joinQuery); + + private: + QxSqlQuery &addSqlExpression(const QString &column, qx::dao::detail::QxSqlExpression::type type); + QxSqlQuery &addSqlCompare(const QVariant &val, qx::dao::detail::QxSqlCompare::type type, const QString &sCustomOperator = QString()); + QxSqlQuery &addSqlSort(const QStringList &columns, qx::dao::detail::QxSqlSort::type type); + QxSqlQuery &addSqlIn(const QVariantList &values, qx::dao::detail::QxSqlIn::type type); + QxSqlQuery &addSqlIsNull(qx::dao::detail::QxSqlIsNull::type type); + QxSqlQuery &addSqlIsBetween(const QVariant &val1, const QVariant &val2, qx::dao::detail::QxSqlIsBetween::type type); + QxSqlQuery &addFreeText(const QString &text, const QVariantList &values); + QxSqlQuery &addEmbedQuery(const QxSqlQuery &query, qx::dao::detail::QxSqlEmbedQuery::type type, bool requirePreviousElement); + }; + +} // namespace qx + +typedef qx::QxSqlQuery qx_query; + +namespace qx +{ + namespace dao + { + + /*! + * \ingroup QxDao + * \brief qx::dao::call_query function can be used to call a custom SQL query or a stored procedure + * + * To get an output value parameter (must be pass as QSql::Out or QSql::InOut) returned by a stored procedure, just call the following method : QVariant qx::QxSqlQuery::boundValue(const QString & sKey) const;.
    + * To iterate over all resultset, just use the following methods : + * - long qx::QxSqlQuery::getSqlResultRowCount() const; + * - long qx::QxSqlQuery::getSqlResultColumnCount() const; + * - QVariant qx::QxSqlQuery::getSqlResultAt(long row, long column) const; + * - QVariant qx::QxSqlQuery::getSqlResultAt(long row, const QString & column) const; + * - QVector qx::QxSqlQuery::getSqlResultAllColumns() const; + * - void qx::QxSqlQuery::dumpSqlResult(); + * + * Here is an example of code using qx::dao::call_query function : + * \code + qx_query query("CALL MyStoredProc(:param1, :param2)"); + query.bind(":param1", "myValue1"); + query.bind(":param2", 5024, QSql::InOut); + QSqlError daoError = qx::dao::call_query(query); + QVariant vNewValue = query.boundValue(":param2"); + query.dumpSqlResult(); + * \endcode + */ + QX_DLL_EXPORT QSqlError call_query(qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL); + + /*! + * \ingroup QxDao + * \brief qx::dao::call_query_without_prepare function can be used to call a custom SQL query or a stored procedure : same as qx::dao::call_query() function without calling prepare() QSqlQuery class method (can be useful to execute some specific SQL queries) + */ + QX_DLL_EXPORT QSqlError call_query_without_prepare(qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL); + + namespace helper + { + + QX_DLL_EXPORT QSqlError call_query_helper(qx::QxSqlQuery &query, QSqlDatabase *pDatabase, bool bPrepare); + + } // namespace helper + } // namespace dao +} // namespace qx + +QX_REGISTER_CLASS_NAME(qx_query) + +QX_CLASS_VERSION(qx::QxSqlQuery, 0) + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, qx::QxSqlQuery) +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#define QX_SQL_QUERY_DERIVED_IMPL_COVARIANT_RETURN_TYPE_HPP(className) \ +public: \ + virtual className &distinct(); \ + \ + virtual className &where(const QString &column); \ + virtual className &where_OpenParenthesis(const QString &column); \ + virtual className &and_(const QString &column); \ + virtual className &and_OpenParenthesis(const QString &column); \ + virtual className &or_(const QString &column); \ + virtual className &or_OpenParenthesis(const QString &column); \ + \ + virtual className &openParenthesis(); \ + virtual className &closeParenthesis(); \ + \ + virtual className &orderAsc(const QStringList &columns); \ + virtual className &orderAsc(const QString &col1); \ + virtual className &orderAsc(const QString &col1, const QString &col2); \ + virtual className &orderAsc(const QString &col1, const QString &col2, const QString &col3); \ + virtual className &orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4); \ + virtual className &orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5); \ + virtual className &orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6); \ + virtual className &orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7); \ + virtual className &orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8); \ + virtual className &orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8, const QString &col9); \ + \ + virtual className &orderDesc(const QStringList &columns); \ + virtual className &orderDesc(const QString &col1); \ + virtual className &orderDesc(const QString &col1, const QString &col2); \ + virtual className &orderDesc(const QString &col1, const QString &col2, const QString &col3); \ + virtual className &orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4); \ + virtual className &orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5); \ + virtual className &orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6); \ + virtual className &orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7); \ + virtual className &orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8); \ + virtual className &orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8, const QString &col9); \ + \ + virtual className &groupBy(const QStringList &columns); \ + virtual className &groupBy(const QString &col1); \ + virtual className &groupBy(const QString &col1, const QString &col2); \ + virtual className &groupBy(const QString &col1, const QString &col2, const QString &col3); \ + virtual className &groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4); \ + virtual className &groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5); \ + virtual className &groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6); \ + virtual className &groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7); \ + virtual className &groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8); \ + virtual className &groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8, const QString &col9); \ + \ + virtual className &limit(int rowsCount, int startRow = 0, bool withTies = false); \ + \ + virtual className &like(const QString &val); \ + virtual className ¬Like(const QString &val); \ + virtual className &startsWith(const QString &val); \ + virtual className &endsWith(const QString &val); \ + virtual className &containsString(const QString &val); \ + \ + virtual className &isEqualTo(const QVariant &val); \ + virtual className &isNotEqualTo(const QVariant &val); \ + virtual className &isGreaterThan(const QVariant &val); \ + virtual className &isGreaterThanOrEqualTo(const QVariant &val); \ + virtual className &isLessThan(const QVariant &val); \ + virtual className &isLessThanOrEqualTo(const QVariant &val); \ + virtual className &customOperator(const QString &sCustomOperator, const QVariant &val); \ + \ + virtual className &in(const QVariantList &values); \ + virtual className &in(const QVariant &val1); \ + virtual className &in(const QVariant &val1, const QVariant &val2); \ + virtual className &in(const QVariant &val1, const QVariant &val2, const QVariant &val3); \ + virtual className &in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4); \ + virtual className &in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5); \ + virtual className &in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6); \ + virtual className &in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7); \ + virtual className &in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7, const QVariant &val8); \ + virtual className &in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7, const QVariant &val8, const QVariant &val9); \ + \ + virtual className ¬In(const QVariantList &values); \ + virtual className ¬In(const QVariant &val1); \ + virtual className ¬In(const QVariant &val1, const QVariant &val2); \ + virtual className ¬In(const QVariant &val1, const QVariant &val2, const QVariant &val3); \ + virtual className ¬In(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4); \ + virtual className ¬In(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5); \ + virtual className ¬In(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6); \ + virtual className ¬In(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7); \ + virtual className ¬In(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7, const QVariant &val8); \ + virtual className ¬In(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7, const QVariant &val8, const QVariant &val9); \ + \ + virtual className &in_Select(const QxSqlQuery &query); \ + virtual className ¬In_Select(const QxSqlQuery &query); \ + virtual className &isEqualTo_Select(const QxSqlQuery &query); \ + virtual className &isNotEqualTo_Select(const QxSqlQuery &query); \ + \ + virtual className &isNull(); \ + virtual className &isNotNull(); \ + \ + virtual className &isBetween(const QVariant &val1, const QVariant &val2); \ + virtual className &isNotBetween(const QVariant &val1, const QVariant &val2); \ + \ + virtual className &freeText(const QString &text, const QVariantList &values = QVariantList()); \ + \ + virtual className &addJoinQuery(const QString &relationKeyOrAlias, const QxSqlQuery &joinQuery); + +#define QX_SQL_QUERY_DERIVED_IMPL_COVARIANT_RETURN_TYPE_CPP(className) \ + \ + className &className::distinct() { return static_cast(qx::QxSqlQuery::distinct()); } \ + \ + className &className::where(const QString &column) { return static_cast(qx::QxSqlQuery::where(column)); } \ + className &className::where_OpenParenthesis(const QString &column) { return static_cast(qx::QxSqlQuery::where_OpenParenthesis(column)); } \ + className &className::and_(const QString &column) { return static_cast(qx::QxSqlQuery::and_(column)); } \ + className &className::and_OpenParenthesis(const QString &column) { return static_cast(qx::QxSqlQuery::and_OpenParenthesis(column)); } \ + className &className::or_(const QString &column) { return static_cast(qx::QxSqlQuery::or_(column)); } \ + className &className::or_OpenParenthesis(const QString &column) { return static_cast(qx::QxSqlQuery::or_OpenParenthesis(column)); } \ + \ + className &className::openParenthesis() { return static_cast(qx::QxSqlQuery::openParenthesis()); } \ + className &className::closeParenthesis() { return static_cast(qx::QxSqlQuery::closeParenthesis()); } \ + \ + className &className::orderAsc(const QStringList &columns) { return static_cast(qx::QxSqlQuery::orderAsc(columns)); } \ + className &className::orderAsc(const QString &col1) { return static_cast(qx::QxSqlQuery::orderAsc(col1)); } \ + className &className::orderAsc(const QString &col1, const QString &col2) { return static_cast(qx::QxSqlQuery::orderAsc(col1, col2)); } \ + className &className::orderAsc(const QString &col1, const QString &col2, const QString &col3) { return static_cast(qx::QxSqlQuery::orderAsc(col1, col2, col3)); } \ + className &className::orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4) { return static_cast(qx::QxSqlQuery::orderAsc(col1, col2, col3, col4)); } \ + className &className::orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5) { return static_cast(qx::QxSqlQuery::orderAsc(col1, col2, col3, col4, col5)); } \ + className &className::orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6) { return static_cast(qx::QxSqlQuery::orderAsc(col1, col2, col3, col4, col5, col6)); } \ + className &className::orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7) { return static_cast(qx::QxSqlQuery::orderAsc(col1, col2, col3, col4, col5, col6, col7)); } \ + className &className::orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8) { return static_cast(qx::QxSqlQuery::orderAsc(col1, col2, col3, col4, col5, col6, col7, col8)); } \ + className &className::orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8, const QString &col9) { return static_cast(qx::QxSqlQuery::orderAsc(col1, col2, col3, col4, col5, col6, col7, col8, col9)); } \ + \ + className &className::orderDesc(const QStringList &columns) { return static_cast(qx::QxSqlQuery::orderDesc(columns)); } \ + className &className::orderDesc(const QString &col1) { return static_cast(qx::QxSqlQuery::orderDesc(col1)); } \ + className &className::orderDesc(const QString &col1, const QString &col2) { return static_cast(qx::QxSqlQuery::orderDesc(col1, col2)); } \ + className &className::orderDesc(const QString &col1, const QString &col2, const QString &col3) { return static_cast(qx::QxSqlQuery::orderDesc(col1, col2, col3)); } \ + className &className::orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4) { return static_cast(qx::QxSqlQuery::orderDesc(col1, col2, col3, col4)); } \ + className &className::orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5) { return static_cast(qx::QxSqlQuery::orderDesc(col1, col2, col3, col4, col5)); } \ + className &className::orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6) { return static_cast(qx::QxSqlQuery::orderDesc(col1, col2, col3, col4, col5, col6)); } \ + className &className::orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7) { return static_cast(qx::QxSqlQuery::orderDesc(col1, col2, col3, col4, col5, col6, col7)); } \ + className &className::orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8) { return static_cast(qx::QxSqlQuery::orderDesc(col1, col2, col3, col4, col5, col6, col7, col8)); } \ + className &className::orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8, const QString &col9) { return static_cast(qx::QxSqlQuery::orderDesc(col1, col2, col3, col4, col5, col6, col7, col8, col9)); } \ + \ + className &className::groupBy(const QStringList &columns) { return static_cast(qx::QxSqlQuery::groupBy(columns)); } \ + className &className::groupBy(const QString &col1) { return static_cast(qx::QxSqlQuery::groupBy(col1)); } \ + className &className::groupBy(const QString &col1, const QString &col2) { return static_cast(qx::QxSqlQuery::groupBy(col1, col2)); } \ + className &className::groupBy(const QString &col1, const QString &col2, const QString &col3) { return static_cast(qx::QxSqlQuery::groupBy(col1, col2, col3)); } \ + className &className::groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4) { return static_cast(qx::QxSqlQuery::groupBy(col1, col2, col3, col4)); } \ + className &className::groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5) { return static_cast(qx::QxSqlQuery::groupBy(col1, col2, col3, col4, col5)); } \ + className &className::groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6) { return static_cast(qx::QxSqlQuery::groupBy(col1, col2, col3, col4, col5, col6)); } \ + className &className::groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7) { return static_cast(qx::QxSqlQuery::groupBy(col1, col2, col3, col4, col5, col6, col7)); } \ + className &className::groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8) { return static_cast(qx::QxSqlQuery::groupBy(col1, col2, col3, col4, col5, col6, col7, col8)); } \ + className &className::groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8, const QString &col9) { return static_cast(qx::QxSqlQuery::groupBy(col1, col2, col3, col4, col5, col6, col7, col8, col9)); } \ + \ + className &className::limit(int rowsCount, int startRow, bool withTies) { return static_cast(qx::QxSqlQuery::limit(rowsCount, startRow, withTies)); } \ + \ + className &className::like(const QString &val) { return static_cast(qx::QxSqlQuery::like(val)); } \ + className &className::notLike(const QString &val) { return static_cast(qx::QxSqlQuery::notLike(val)); } \ + className &className::startsWith(const QString &val) { return static_cast(qx::QxSqlQuery::startsWith(val)); } \ + className &className::endsWith(const QString &val) { return static_cast(qx::QxSqlQuery::endsWith(val)); } \ + className &className::containsString(const QString &val) { return static_cast(qx::QxSqlQuery::containsString(val)); } \ + \ + className &className::isEqualTo(const QVariant &val) { return static_cast(qx::QxSqlQuery::isEqualTo(val)); } \ + className &className::isNotEqualTo(const QVariant &val) { return static_cast(qx::QxSqlQuery::isNotEqualTo(val)); } \ + className &className::isGreaterThan(const QVariant &val) { return static_cast(qx::QxSqlQuery::isGreaterThan(val)); } \ + className &className::isGreaterThanOrEqualTo(const QVariant &val) { return static_cast(qx::QxSqlQuery::isGreaterThanOrEqualTo(val)); } \ + className &className::isLessThan(const QVariant &val) { return static_cast(qx::QxSqlQuery::isLessThan(val)); } \ + className &className::isLessThanOrEqualTo(const QVariant &val) { return static_cast(qx::QxSqlQuery::isLessThanOrEqualTo(val)); } \ + className &className::customOperator(const QString &sCustomOperator, const QVariant &val) { return static_cast(qx::QxSqlQuery::customOperator(sCustomOperator, val)); } \ + \ + className &className::in(const QVariantList &values) { return static_cast(qx::QxSqlQuery::in(values)); } \ + className &className::in(const QVariant &val1) { return static_cast(qx::QxSqlQuery::in(val1)); } \ + className &className::in(const QVariant &val1, const QVariant &val2) { return static_cast(qx::QxSqlQuery::in(val1, val2)); } \ + className &className::in(const QVariant &val1, const QVariant &val2, const QVariant &val3) { return static_cast(qx::QxSqlQuery::in(val1, val2, val3)); } \ + className &className::in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4) { return static_cast(qx::QxSqlQuery::in(val1, val2, val3, val4)); } \ + className &className::in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5) { return static_cast(qx::QxSqlQuery::in(val1, val2, val3, val4, val5)); } \ + className &className::in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6) { return static_cast(qx::QxSqlQuery::in(val1, val2, val3, val4, val5, val6)); } \ + className &className::in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7) { return static_cast(qx::QxSqlQuery::in(val1, val2, val3, val4, val5, val6, val7)); } \ + className &className::in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7, const QVariant &val8) { return static_cast(qx::QxSqlQuery::in(val1, val2, val3, val4, val5, val6, val7, val8)); } \ + className &className::in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7, const QVariant &val8, const QVariant &val9) { return static_cast(qx::QxSqlQuery::in(val1, val2, val3, val4, val5, val6, val7, val8, val9)); } \ + \ + className &className::notIn(const QVariantList &values) { return static_cast(qx::QxSqlQuery::notIn(values)); } \ + className &className::notIn(const QVariant &val1) { return static_cast(qx::QxSqlQuery::notIn(val1)); } \ + className &className::notIn(const QVariant &val1, const QVariant &val2) { return static_cast(qx::QxSqlQuery::notIn(val1, val2)); } \ + className &className::notIn(const QVariant &val1, const QVariant &val2, const QVariant &val3) { return static_cast(qx::QxSqlQuery::notIn(val1, val2, val3)); } \ + className &className::notIn(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4) { return static_cast(qx::QxSqlQuery::notIn(val1, val2, val3, val4)); } \ + className &className::notIn(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5) { return static_cast(qx::QxSqlQuery::notIn(val1, val2, val3, val4, val5)); } \ + className &className::notIn(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6) { return static_cast(qx::QxSqlQuery::notIn(val1, val2, val3, val4, val5, val6)); } \ + className &className::notIn(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7) { return static_cast(qx::QxSqlQuery::notIn(val1, val2, val3, val4, val5, val6, val7)); } \ + className &className::notIn(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7, const QVariant &val8) { return static_cast(qx::QxSqlQuery::notIn(val1, val2, val3, val4, val5, val6, val7, val8)); } \ + className &className::notIn(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7, const QVariant &val8, const QVariant &val9) { return static_cast(qx::QxSqlQuery::notIn(val1, val2, val3, val4, val5, val6, val7, val8, val9)); } \ + \ + className &className::in_Select(const QxSqlQuery &query) { return static_cast(qx::QxSqlQuery::in_Select(query)); } \ + className &className::notIn_Select(const QxSqlQuery &query) { return static_cast(qx::QxSqlQuery::notIn_Select(query)); } \ + className &className::isEqualTo_Select(const QxSqlQuery &query) { return static_cast(qx::QxSqlQuery::isEqualTo_Select(query)); } \ + className &className::isNotEqualTo_Select(const QxSqlQuery &query) { return static_cast(qx::QxSqlQuery::isNotEqualTo_Select(query)); } \ + \ + className &className::isNull() { return static_cast(qx::QxSqlQuery::isNull()); } \ + className &className::isNotNull() { return static_cast(qx::QxSqlQuery::isNotNull()); } \ + \ + className &className::isBetween(const QVariant &val1, const QVariant &val2) { return static_cast(qx::QxSqlQuery::isBetween(val1, val2)); } \ + className &className::isNotBetween(const QVariant &val1, const QVariant &val2) { return static_cast(qx::QxSqlQuery::isNotBetween(val1, val2)); } \ + \ + className &className::freeText(const QString &text, const QVariantList &values) { return static_cast(qx::QxSqlQuery::freeText(text, values)); } \ + \ + className &className::addJoinQuery(const QString &relationKeyOrAlias, const QxSqlQuery &joinQuery) { return static_cast(qx::QxSqlQuery::addJoinQuery(relationKeyOrAlias, joinQuery)); } + +#endif // _QX_SQL_QUERY_H_ diff --git a/include/QxDao/QxSqlQueryBuilder.h b/include/QxDao/QxSqlQueryBuilder.h new file mode 100644 index 0000000..0ee4e92 --- /dev/null +++ b/include/QxDao/QxSqlQueryBuilder.h @@ -0,0 +1,567 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_QUERY_BUILDER_H_ +#define _QX_SQL_QUERY_BUILDER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlQueryBuilder.h + * \author XDL Team + * \ingroup QxDao + * \brief Concrete SQL query builder by class with a cache mechanism to backup and restore queries already built by the program + */ + +#include +#include + +#include + +#include +#include +#include + +#define QX_SQL_ERR_NO_DATA_MEMBER_REGISTERED "'QxSqlQueryBuilder' error : 'qx::register_class()' not called or no data member registered" +#define QX_SQL_ERR_NO_ID_REGISTERED "'QxSqlQueryBuilder' error : no id registered" + +#define QX_SQL_BUILDER_INIT_FCT(oper) \ + qx::dao::detail::IxDao_Timer timer(this->getDaoHelper(), qx::dao::detail::IxDao_Helper::timer_build_sql); \ + QString joinQueryHash = (this->getDaoHelper() ? this->getDaoHelper()->qxQuery().getJoinQueryHash() : QString()); \ + QString ignoreSoftDeleteHash = (this->getDaoHelper() ? this->getDaoHelper()->getIgnoreSoftDeleteHash() : QString()); \ + QString key = QxClass::getSingleton()->getKey() + joinQueryHash + ignoreSoftDeleteHash + oper; \ + if ((joinQueryHash.isEmpty()) && (this->findSqlQuery(key))) \ + { \ + return (*this); \ + } \ + QString &sql = this->getCurrentBuildingSql(); \ + sql = ""; + +#define QX_SQL_BUILDER_INIT_FCT_WITH_RELATION(oper) \ + qx::dao::detail::IxDao_Timer timer(this->getDaoHelper(), qx::dao::detail::IxDao_Helper::timer_build_sql); \ + QString joinQueryHash = (this->getDaoHelper() ? this->getDaoHelper()->qxQuery().getJoinQueryHash() : QString()); \ + QString ignoreSoftDeleteHash = (this->getDaoHelper() ? this->getDaoHelper()->getIgnoreSoftDeleteHash() : QString()); \ + QString key = QxClass::getSingleton()->getKey() + joinQueryHash + this->getHashRelation() + ignoreSoftDeleteHash + oper; \ + if ((joinQueryHash.isEmpty()) && (this->findSqlQuery(key))) \ + { \ + this->findSqlAlias(key); \ + return (*this); \ + } \ + QString &sql = this->getCurrentBuildingSql(); \ + sql = ""; + +namespace qx +{ + + /*! + * \ingroup QxDao + * \brief qx::QxSqlQueryBuilder : concrete SQL query builder for class T with a cache mechanism to backup and restore queries already built by the program + */ + template + class QxSqlQueryBuilder : public IxSqlQueryBuilder + { + + private: + typedef typename qx::trait::remove_attr::type type_sql_tmp_1; + typedef typename qx::trait::remove_smart_ptr::type type_sql_tmp_2; + + public: + typedef typename qx::QxSqlQueryBuilder::type_sql_tmp_2 type_sql; + + public: + QxSqlQueryBuilder() : IxSqlQueryBuilder() { ; } + virtual ~QxSqlQueryBuilder() { static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); } + + virtual void init() + { + if (isInitDone()) + { + return; + } + setDataMemberX(QxClass::getSingleton()->dataMemberX()); + setSoftDelete(QxClass::getSingleton()->getSoftDelete()); + IxSqlQueryBuilder::init(); + } + }; + + /*! + * \ingroup QxDao + * \brief qx::QxSqlQueryBuilder_Count : concrete SQL query builder for class T to build a COUNT SQL query + */ + template + class QxSqlQueryBuilder_Count : public QxSqlQueryBuilder + { + + public: + typedef typename QxSqlQueryBuilder::type_sql type_sql; + + QxSqlQueryBuilder_Count() : QxSqlQueryBuilder() { ; } + virtual ~QxSqlQueryBuilder_Count() { ; } + + virtual IxSqlQueryBuilder &buildSql(const QStringList &columns = QStringList(), QxSqlRelationLinked *pRelationX = NULL) + { + Q_UNUSED(columns); + Q_UNUSED(pRelationX); + QX_SQL_BUILDER_INIT_FCT("Count") + sql = "SELECT COUNT(*) FROM " + qx::IxDataMember::getSqlFromTable(this->table()); + if (!this->softDelete().isEmpty()) + { + sql += " WHERE " + this->softDelete().buildSqlQueryToFetch(); + } + this->setSqlQuery(sql, key); + return (*this); + } + }; + + /*! + * \ingroup QxDao + * \brief qx::QxSqlQueryBuilder_Exist : concrete SQL query builder for class T to build an EXIST SQL query + */ + template + class QxSqlQueryBuilder_Exist : public QxSqlQueryBuilder + { + + public: + typedef typename QxSqlQueryBuilder::type_sql type_sql; + + QxSqlQueryBuilder_Exist() : QxSqlQueryBuilder() { ; } + virtual ~QxSqlQueryBuilder_Exist() { ; } + + virtual IxSqlQueryBuilder &buildSql(const QStringList &columns = QStringList(), QxSqlRelationLinked *pRelationX = NULL) + { + Q_UNUSED(columns); + Q_UNUSED(pRelationX); + QX_SQL_BUILDER_INIT_FCT("Exist") + if (!this->getDataId()) + { + qDebug("[QxOrm] %s", QX_SQL_ERR_NO_ID_REGISTERED); + qAssert(false); + return (*this); + } + qx::dao::detail::QxSqlQueryHelper_Exist::sql(sql, (*this)); + this->setSqlQuery(sql, key); + return (*this); + } + }; + + /*! + * \ingroup QxDao + * \brief qx::QxSqlQueryBuilder_FetchAll : concrete SQL query builder for class T to build a FETCH ALL SQL query + */ + template + class QxSqlQueryBuilder_FetchAll : public QxSqlQueryBuilder + { + + public: + typedef typename QxSqlQueryBuilder::type_sql type_sql; + + QxSqlQueryBuilder_FetchAll() : QxSqlQueryBuilder() { ; } + virtual ~QxSqlQueryBuilder_FetchAll() { ; } + + virtual IxSqlQueryBuilder &buildSql(const QStringList &columns = QStringList(), QxSqlRelationLinked *pRelationX = NULL) + { + Q_UNUSED(pRelationX); + + if ((columns.count() <= 0) || (columns.at(0) == "*")) + { + QX_SQL_BUILDER_INIT_FCT("FetchAll") + qx::dao::detail::QxSqlQueryHelper_FetchAll::sql(sql, (*this)); + this->setSqlQuery(sql, key); + } + else + { + QString sql; + if (!this->verifyColumns(columns)) + { + return (*this); + } + qx::dao::detail::QxSqlQueryHelper_FetchAll::sql(sql, (*this), columns); + this->setSqlQuery(sql); + } + + return (*this); + } + }; + + /*! + * \ingroup QxDao + * \brief qx::QxSqlQueryBuilder_FetchById : concrete SQL query builder for class T to build a FETCH BY ID SQL query + */ + template + class QxSqlQueryBuilder_FetchById : public QxSqlQueryBuilder + { + + public: + typedef typename QxSqlQueryBuilder::type_sql type_sql; + + QxSqlQueryBuilder_FetchById() : QxSqlQueryBuilder() { ; } + virtual ~QxSqlQueryBuilder_FetchById() { ; } + + virtual IxSqlQueryBuilder &buildSql(const QStringList &columns = QStringList(), QxSqlRelationLinked *pRelationX = NULL) + { + Q_UNUSED(pRelationX); + if (!this->getDataId()) + { + qDebug("[QxOrm] %s", QX_SQL_ERR_NO_ID_REGISTERED); + qAssert(false); + return (*this); + } + QxSqlQueryBuilder_FetchAll builder; + builder.clone(*this); + + if ((columns.count() <= 0) || (columns.at(0) == "*")) + { + QX_SQL_BUILDER_INIT_FCT("FetchById") + qx::dao::detail::QxSqlQueryHelper_FetchById::sql(sql, builder); + this->setSqlQuery(sql, key); + } + else + { + QString sql; + if (!this->verifyColumns(columns)) + { + return (*this); + } + qx::dao::detail::QxSqlQueryHelper_FetchById::sql(sql, builder, columns); + this->setSqlQuery(sql); + } + + return (*this); + } + }; + + /*! + * \ingroup QxDao + * \brief qx::QxSqlQueryBuilder_Insert : concrete SQL query builder for class T to build an INSERT SQL query + */ + template + class QxSqlQueryBuilder_Insert : public QxSqlQueryBuilder + { + + public: + typedef typename QxSqlQueryBuilder::type_sql type_sql; + + QxSqlQueryBuilder_Insert() : QxSqlQueryBuilder() { ; } + virtual ~QxSqlQueryBuilder_Insert() { ; } + + virtual IxSqlQueryBuilder &buildSql(const QStringList &columns = QStringList(), QxSqlRelationLinked *pRelationX = NULL) + { + Q_UNUSED(columns); + Q_UNUSED(pRelationX); + QX_SQL_BUILDER_INIT_FCT("Insert") + qx::dao::detail::QxSqlQueryHelper_Insert::sql(sql, (*this)); + this->setSqlQuery(sql, key); + return (*this); + } + }; + + /*! + * \ingroup QxDao + * \brief qx::QxSqlQueryBuilder_Update : concrete SQL query builder for class T to build an UPDATE SQL query + */ + template + class QxSqlQueryBuilder_Update : public QxSqlQueryBuilder + { + + public: + typedef typename QxSqlQueryBuilder::type_sql type_sql; + + QxSqlQueryBuilder_Update() : QxSqlQueryBuilder() { ; } + virtual ~QxSqlQueryBuilder_Update() { ; } + + virtual IxSqlQueryBuilder &buildSql(const QStringList &columns = QStringList(), QxSqlRelationLinked *pRelationX = NULL) + { + Q_UNUSED(pRelationX); + if (!this->getDataId()) + { + qDebug("[QxOrm] %s", QX_SQL_ERR_NO_ID_REGISTERED); + qAssert(false); + return (*this); + } + + if ((columns.count() <= 0) || (columns.at(0) == "*")) + { + QX_SQL_BUILDER_INIT_FCT("Update") + qx::dao::detail::QxSqlQueryHelper_Update::sql(sql, (*this)); + this->setSqlQuery(sql, key); + } + else + { + QString sql; + if (!this->verifyColumns(columns)) + { + return (*this); + } + qx::dao::detail::QxSqlQueryHelper_Update::sql(sql, (*this), columns); + this->setSqlQuery(sql); + } + + return (*this); + } + }; + + /*! + * \ingroup QxDao + * \brief qx::QxSqlQueryBuilder_DeleteAll : concrete SQL query builder for class T to build a DELETE ALL SQL query + */ + template + class QxSqlQueryBuilder_DeleteAll : public QxSqlQueryBuilder + { + + public: + typedef typename QxSqlQueryBuilder::type_sql type_sql; + + QxSqlQueryBuilder_DeleteAll() : QxSqlQueryBuilder() { ; } + virtual ~QxSqlQueryBuilder_DeleteAll() { ; } + + virtual IxSqlQueryBuilder &buildSql(const QStringList &columns = QStringList(), QxSqlRelationLinked *pRelationX = NULL) + { + Q_UNUSED(columns); + Q_UNUSED(pRelationX); + QX_SQL_BUILDER_INIT_FCT("DeleteAll") + sql = "DELETE FROM " + this->table(); + this->setSqlQuery(sql, key); + return (*this); + } + }; + + /*! + * \ingroup QxDao + * \brief qx::QxSqlQueryBuilder_SoftDeleteAll : concrete SQL query builder for class T to build a SOFT DELETE ALL SQL query + */ + template + class QxSqlQueryBuilder_SoftDeleteAll : public QxSqlQueryBuilder + { + + public: + typedef typename QxSqlQueryBuilder::type_sql type_sql; + + QxSqlQueryBuilder_SoftDeleteAll() : QxSqlQueryBuilder() { ; } + virtual ~QxSqlQueryBuilder_SoftDeleteAll() { ; } + + virtual IxSqlQueryBuilder &buildSql(const QStringList &columns = QStringList(), QxSqlRelationLinked *pRelationX = NULL) + { + Q_UNUSED(columns); + Q_UNUSED(pRelationX); + QX_SQL_BUILDER_INIT_FCT("SoftDeleteAll") + if (!this->softDelete().isEmpty()) + { + sql = "UPDATE " + this->table() + " SET " + this->softDelete().buildSqlQueryToUpdate(); + } + else + { + qAssert(false); + } + this->setSqlQuery(sql, key); + return (*this); + } + }; + + /*! + * \ingroup QxDao + * \brief qx::QxSqlQueryBuilder_DeleteById : concrete SQL query builder for class T to build a DELETE BY ID SQL query + */ + template + class QxSqlQueryBuilder_DeleteById : public QxSqlQueryBuilder + { + + public: + typedef typename QxSqlQueryBuilder::type_sql type_sql; + + QxSqlQueryBuilder_DeleteById() : QxSqlQueryBuilder() { ; } + virtual ~QxSqlQueryBuilder_DeleteById() { ; } + + virtual IxSqlQueryBuilder &buildSql(const QStringList &columns = QStringList(), QxSqlRelationLinked *pRelationX = NULL) + { + Q_UNUSED(columns); + Q_UNUSED(pRelationX); + QX_SQL_BUILDER_INIT_FCT("DeleteById") + if (!this->getDataId()) + { + qDebug("[QxOrm] %s", QX_SQL_ERR_NO_ID_REGISTERED); + qAssert(false); + return (*this); + } + qx::dao::detail::QxSqlQueryHelper_DeleteById::sql(sql, (*this), false); + this->setSqlQuery(sql, key); + return (*this); + } + }; + + /*! + * \ingroup QxDao + * \brief qx::QxSqlQueryBuilder_SoftDeleteById : concrete SQL query builder for class T to build a SOFT DELETE BY ID SQL query + */ + template + class QxSqlQueryBuilder_SoftDeleteById : public QxSqlQueryBuilder + { + + public: + typedef typename QxSqlQueryBuilder::type_sql type_sql; + + QxSqlQueryBuilder_SoftDeleteById() : QxSqlQueryBuilder() { ; } + virtual ~QxSqlQueryBuilder_SoftDeleteById() { ; } + + virtual IxSqlQueryBuilder &buildSql(const QStringList &columns = QStringList(), QxSqlRelationLinked *pRelationX = NULL) + { + Q_UNUSED(columns); + Q_UNUSED(pRelationX); + QX_SQL_BUILDER_INIT_FCT("SoftDeleteById") + if (!this->getDataId()) + { + qDebug("[QxOrm] %s", QX_SQL_ERR_NO_ID_REGISTERED); + qAssert(false); + return (*this); + } + if (this->softDelete().isEmpty()) + { + qAssert(false); + return (*this); + } + qx::dao::detail::QxSqlQueryHelper_DeleteById::sql(sql, (*this), true); + this->setSqlQuery(sql, key); + return (*this); + } + }; + + /*! + * \ingroup QxDao + * \brief qx::QxSqlQueryBuilder_CreateTable : concrete SQL query builder for class T to build a CREATE TABLE SQL query + */ + template + class QxSqlQueryBuilder_CreateTable : public QxSqlQueryBuilder + { + + public: + typedef typename QxSqlQueryBuilder::type_sql type_sql; + + QxSqlQueryBuilder_CreateTable() : QxSqlQueryBuilder() { ; } + virtual ~QxSqlQueryBuilder_CreateTable() { ; } + + virtual IxSqlQueryBuilder &buildSql(const QStringList &columns = QStringList(), QxSqlRelationLinked *pRelationX = NULL) + { + Q_UNUSED(columns); + Q_UNUSED(pRelationX); + QX_SQL_BUILDER_INIT_FCT("CreateTable") + qx::dao::detail::QxSqlQueryHelper_CreateTable::sql(sql, (*this)); + this->setSqlQuery(sql, key); + return (*this); + } + }; + + /*! + * \ingroup QxDao + * \brief qx::QxSqlQueryBuilder_Count_WithRelation : concrete SQL query builder for class T to build a COUNT WITH RELATION SQL query + */ + template + class QxSqlQueryBuilder_Count_WithRelation : public QxSqlQueryBuilder + { + + public: + typedef typename QxSqlQueryBuilder::type_sql type_sql; + + QxSqlQueryBuilder_Count_WithRelation() : QxSqlQueryBuilder() { ; } + virtual ~QxSqlQueryBuilder_Count_WithRelation() { ; } + + virtual IxSqlQueryBuilder &buildSql(const QStringList &columns = QStringList(), QxSqlRelationLinked *pRelationX = NULL) + { + Q_UNUSED(columns); + QX_SQL_BUILDER_INIT_FCT_WITH_RELATION("Count_WithRelation") + IxSqlQueryBuilder::sql_Count_WithRelation(pRelationX, sql, (*this)); + this->setSqlQuery(sql, key); + this->insertSqlAlias(key); + return (*this); + } + }; + + /*! + * \ingroup QxDao + * \brief qx::QxSqlQueryBuilder_FetchAll_WithRelation : concrete SQL query builder for class T to build a FETCH ALL WITH RELATION SQL query + */ + template + class QxSqlQueryBuilder_FetchAll_WithRelation : public QxSqlQueryBuilder + { + + public: + typedef typename QxSqlQueryBuilder::type_sql type_sql; + + QxSqlQueryBuilder_FetchAll_WithRelation() : QxSqlQueryBuilder() { ; } + virtual ~QxSqlQueryBuilder_FetchAll_WithRelation() { ; } + + virtual IxSqlQueryBuilder &buildSql(const QStringList &columns = QStringList(), QxSqlRelationLinked *pRelationX = NULL) + { + Q_UNUSED(columns); + QX_SQL_BUILDER_INIT_FCT_WITH_RELATION("FetchAll_WithRelation") + qx::dao::detail::QxSqlQueryHelper_FetchAll_WithRelation::sql(pRelationX, sql, (*this)); + this->setSqlQuery(sql, key); + this->insertSqlAlias(key); + return (*this); + } + }; + + /*! + * \ingroup QxDao + * \brief qx::QxSqlQueryBuilder_FetchById_WithRelation : concrete SQL query builder for class T to build a FETCH BY ID WITH RELATION SQL query + */ + template + class QxSqlQueryBuilder_FetchById_WithRelation : public QxSqlQueryBuilder + { + + public: + typedef typename QxSqlQueryBuilder::type_sql type_sql; + + QxSqlQueryBuilder_FetchById_WithRelation() : QxSqlQueryBuilder() { ; } + virtual ~QxSqlQueryBuilder_FetchById_WithRelation() { ; } + + virtual IxSqlQueryBuilder &buildSql(const QStringList &columns = QStringList(), QxSqlRelationLinked *pRelationX = NULL) + { + Q_UNUSED(columns); + QX_SQL_BUILDER_INIT_FCT_WITH_RELATION("FetchById_WithRelation") + if (!this->getDataId()) + { + qDebug("[QxOrm] %s", QX_SQL_ERR_NO_ID_REGISTERED); + qAssert(false); + return (*this); + } + QxSqlQueryBuilder_FetchAll_WithRelation builder; + builder.clone(*this); + qx::dao::detail::QxSqlQueryHelper_FetchById_WithRelation::sql(pRelationX, sql, builder); + this->setSqlQuery(sql, key); + this->insertSqlAlias(key); + return (*this); + } + }; + +} // namespace qx + +#endif // _QX_SQL_QUERY_BUILDER_H_ diff --git a/include/QxDao/QxSqlQueryHelper.h b/include/QxDao/QxSqlQueryHelper.h new file mode 100644 index 0000000..ea219df --- /dev/null +++ b/include/QxDao/QxSqlQueryHelper.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_QUERY_HELPER_H_ +#define _QX_SQL_QUERY_HELPER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxSqlQueryHelper_CreateTable; + template + struct QxSqlQueryHelper_DeleteById; + template + struct QxSqlQueryHelper_Exist; + template + struct QxSqlQueryHelper_FetchAll; + template + struct QxSqlQueryHelper_FetchAll_WithRelation; + template + struct QxSqlQueryHelper_FetchById; + template + struct QxSqlQueryHelper_FetchById_WithRelation; + template + struct QxSqlQueryHelper_Insert; + template + struct QxSqlQueryHelper_Update; + + } // namespace detail + } // namespace dao +} // namespace qx + +#include "../../inl/QxDao/QxSqlQueryHelper_CreateTable.inl" +#include "../../inl/QxDao/QxSqlQueryHelper_DeleteById.inl" +#include "../../inl/QxDao/QxSqlQueryHelper_Exist.inl" +#include "../../inl/QxDao/QxSqlQueryHelper_FetchAll.inl" +#include "../../inl/QxDao/QxSqlQueryHelper_FetchAll_WithRelation.inl" +#include "../../inl/QxDao/QxSqlQueryHelper_FetchById.inl" +#include "../../inl/QxDao/QxSqlQueryHelper_FetchById_WithRelation.inl" +#include "../../inl/QxDao/QxSqlQueryHelper_Insert.inl" +#include "../../inl/QxDao/QxSqlQueryHelper_Update.inl" + +#endif // _QX_SQL_QUERY_HELPER_H_ diff --git a/include/QxDao/QxSqlRelation.h b/include/QxDao/QxSqlRelation.h new file mode 100644 index 0000000..e1b68fc --- /dev/null +++ b/include/QxDao/QxSqlRelation.h @@ -0,0 +1,296 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_RELATION_H_ +#define _QX_SQL_RELATION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlRelation.h + * \author XDL Team + * \ingroup QxDao + * \brief Base class for all relationships defined between 2 classes (or between 2 tables in database) + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace qx +{ + + template + class QxClass; + + /*! + * \ingroup QxDao + * \brief qx::QxSqlRelation : base class for all relationships defined between 2 classes (or between 2 tables in database) + */ + template + class QxSqlRelation : public IxSqlRelation + { + + protected: + typedef typename qx::trait::remove_attr::type type_tmp_1; + typedef typename qx::trait::remove_smart_ptr::type type_tmp_2; + typedef type_tmp_2 type_container; + typedef qx::trait::generic_container type_generic_container; + typedef typename type_generic_container::type_item type_item; + typedef typename std::conditional::value, typename type_generic_container::type_value_qx, type_container>::type type_tmp_3; + typedef typename QxSqlRelation::type_tmp_3 type_data; + typedef Owner type_owner; + + enum + { + is_valid = (qx::trait::is_qx_registered::value && qx::trait::is_qx_registered::value) + }; + enum + { + is_data_pointer = (std::is_pointer::value || qx::trait::is_smart_ptr::value) + }; + enum + { + is_data_container = qx::trait::is_container::value + }; + enum + { + is_same_data_owner = std::is_same::value + }; + + public: + QxSqlRelation(IxDataMember *p) : IxSqlRelation(p) { this->setIsSameDataOwner(static_cast(is_same_data_owner)); } + virtual ~QxSqlRelation() { static_assert(is_valid, "is_valid"); } + + virtual void init() + { + if (!this->canInit()) + { + return; + } + this->setClass(QxClass::getSingleton(), QxClass::getSingleton()); + IxSqlRelation::init(); + } + + protected: + DataType *getDataTypePtr(QxSqlRelationParams ¶ms) const + { + qAssert(params.owner() && this->getDataMember()); + return static_cast(this->getDataMember()->getValueVoidPtr(params.owner())); + } + + type_owner &getOwner(QxSqlRelationParams ¶ms) const + { + qAssert(params.owner()); + return (*static_cast(params.owner())); + } + + type_data &getData(QxSqlRelationParams ¶ms) const + { + return getData_Helper::get(getDataTypePtr(params)); + } + + type_container &getContainer(QxSqlRelationParams ¶ms) const + { + return getContainer_Helper::get(getDataTypePtr(params)); + } + + type_item createItem() const + { + return createItem_Helper::get(); + } + + bool isNullData(QxSqlRelationParams ¶ms) const + { + return isNullData_Helper::get(getDataTypePtr(params)); + } + + bool callTriggerBeforeFetch(type_data &t, QxSqlRelationParams ¶ms) const + { + if (!params.builder().getDaoHelper()) + { + return true; + } + qx::dao::on_before_fetch((&t), params.builder().getDaoHelper()); + return params.builder().getDaoHelper()->isValid(); + } + + bool callTriggerAfterFetch(type_data &t, QxSqlRelationParams ¶ms) const + { + if (!params.builder().getDaoHelper()) + { + return true; + } + qx::dao::on_after_fetch((&t), params.builder().getDaoHelper()); + return params.builder().getDaoHelper()->isValid(); + } + + private: + template + struct getData_Helper + { + static type_data &get(DataType *t) { return (*t); } + }; + + template + struct getData_Helper + { + static type_data &get(DataType *t) + { + if (!(*t)) + { + qx::trait::construct_ptr::get(*t); + }; + return (**t); + } + }; + + template + struct getData_Helper + { + static type_data &get(DataType *t) + { + qAssert(false); + Q_UNUSED(t); + type_data *pDummy(NULL); + return (*pDummy); + } + }; + + template + struct getData_Helper + { + static type_data &get(DataType *t) + { + qAssert(false); + Q_UNUSED(t); + type_data *pDummy(NULL); + return (*pDummy); + } + }; + + template + struct getContainer_Helper + { + static type_container &get(DataType *t) + { + qAssert(false); + Q_UNUSED(t); + type_container *pDummy(NULL); + return (*pDummy); + } + }; + + template + struct getContainer_Helper + { + static type_container &get(DataType *t) + { + qAssert(false); + Q_UNUSED(t); + type_container *pDummy(NULL); + return (*pDummy); + } + }; + + template + struct getContainer_Helper + { + static type_container &get(DataType *t) { return (*t); } + }; + + template + struct getContainer_Helper + { + static type_container &get(DataType *t) + { + if (!(*t)) + { + qx::trait::construct_ptr::get(*t); + }; + return (**t); + } + }; + + template + struct createItem_Helper + { + static type_item get() + { + qAssert(false); + type_item *pDummy(NULL); + return (*pDummy); + } + }; + + template + struct createItem_Helper + { + static type_item get() { return type_generic_container::createItem(); } + }; + + template + struct isNullData_Helper + { + static bool get(DataType *t) + { + Q_UNUSED(t); + return false; + } + }; + + template + struct isNullData_Helper + { + static bool get(DataType *t) { return ((!(*t)) ? true : false); } + }; + }; + +} // namespace qx + +#include +#include +#include +#include +#include + +#endif // _QX_SQL_RELATION_H_ diff --git a/include/QxDao/QxSqlRelationLinked.h b/include/QxDao/QxSqlRelationLinked.h new file mode 100644 index 0000000..8a65a6c --- /dev/null +++ b/include/QxDao/QxSqlRelationLinked.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_RELATION_LINKED_H_ +#define _QX_SQL_RELATION_LINKED_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlRelationLinked.h + * \author XDL Team + * \ingroup QxDao + * \brief Hierarchy of relationships to build SQL query + */ + +#include + +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + class IxDao_Helper; + + } // namespace detail + } // namespace dao +} // namespace qx + +namespace qx +{ + + class IxClass; + + /*! + * \ingroup QxDao + * \brief qx::QxSqlRelationLinked : hierarchy of relationships to build SQL query + * + * Here is the structure, each real relation has a relation linked associated to build the hierarchy, like this : + * \code + (, ) + ("blog", blog_relation) + ("blog", ) + ("author", author_relation) + ("author", ) + ("list_blog", list_blog_relation) + ("list_blog", ) + (etc...) + ("comment", comment_relation) + ("comment", ) + ("blog_id", blog_id_relation) + ("blog_id", ) + (etc...) + ("category", category_relation) + ("category", ) + ("list_blog", list_blog_relation) + ("list_blog", ) + (etc...) + * \endcode + */ + class QX_DLL_EXPORT QxSqlRelationLinked + { + + public: + typedef std::shared_ptr type_ptr; + typedef std::tuple, long>, QString, bool> type_relation; + typedef qx::QxCollection type_lst_relation; + typedef QHash type_lst_relation_linked; + + private: + struct QxSqlRelationLinkedImpl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + + public: + QxSqlRelationLinked(); + QxSqlRelationLinked(bool bRoot); + virtual ~QxSqlRelationLinked(); + + static type_ptr getHierarchy(IxClass *pClass, const QStringList &sRelationX, qx_bool &bOk, qx::dao::detail::IxDao_Helper *pDaoHelper = NULL); + + void hierarchySelect(QxSqlRelationParams ¶ms); + void hierarchyFrom(QxSqlRelationParams ¶ms); + void hierarchyJoin(QxSqlRelationParams ¶ms); + void hierarchyWhereSoftDelete(QxSqlRelationParams ¶ms); + void hierarchyResolveOutput(QxSqlRelationParams ¶ms); + QSqlError hierarchyOnBeforeSave(QxSqlRelationParams ¶ms); + QSqlError hierarchyOnAfterSave(QxSqlRelationParams ¶ms); + void updateOffset(QxSqlRelationParams ¶ms); + + bool getCartesianProduct() const; + long getAllRelationCount() const; + long getRelationCount() const; + bool existRelation(const QString &sKey) const; + type_lst_relation_linked getRelationLinkedX() const; + type_lst_relation getRelationX() const; + + bool isRoot() const; + bool checkRootColumns(const QString &s) const; + long getRootColumnsCount() const; + long getRootColumnsOffset() const; + void setRootColumnsOffset(long l); + QString getRootCustomAlias() const; + + protected: + bool isValidDaoHelper(QxSqlRelationParams ¶ms) const; + }; + + typedef std::shared_ptr QxSqlRelationLinked_ptr; + +} // namespace qx + +#endif // _QX_SQL_RELATION_LINKED_H_ diff --git a/include/QxDao/QxSqlRelationParams.h b/include/QxDao/QxSqlRelationParams.h new file mode 100644 index 0000000..9964a38 --- /dev/null +++ b/include/QxDao/QxSqlRelationParams.h @@ -0,0 +1,195 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_RELATION_PARAMS_H_ +#define _QX_SQL_RELATION_PARAMS_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlRelationParams.h + * \author XDL Team + * \ingroup QxDao + * \brief Define list of parameters to transfer to relationships to manage SQL queries builded by QxOrm library + */ + +#include +#include + +#include +#include + +#include + +namespace qx +{ + + class QxSqlRelationLinked; + class IxSqlQueryBuilder; + class IxSqlRelation; + + /*! + * \ingroup QxDao + * \brief qx::QxSqlRelationParams : define list of parameters to transfer to relationships to manage SQL queries builded by QxOrm library + */ + class QX_DLL_EXPORT QxSqlRelationParams + { + + public: + typedef std::shared_ptr type_relation_linked_ptr; + typedef QHash type_lst_relation_linked; + + protected: + QVariant m_vId; //!< Current id + long m_lIndex; //!< Current SQL relation index + long m_lIndexOwner; //!< Current SQL relation owner index + long m_lOffset; //!< Current SQL query offset + QString *m_sql; //!< Current SQL query + IxSqlQueryBuilder *m_builder; //!< Current SQL query builder + QSqlQuery *m_query; //!< Current SQL query connected to database + QSqlDatabase *m_database; //!< Current SQL database connexion + void *m_pOwner; //!< Owner to current object to resolve input/output + qx::dao::sql_join::join_type m_eJoinType; //!< Current join type to build SQL query : LEFT OUTER JOIN, INNER JOIN, etc... + type_lst_relation_linked *m_pRelationX; //!< Current list of relations used by qx::QxSqlRelationLinked class + QString m_sTableAlias; //!< Current SQL table alias : useful for relationships defined in base class + qx::dao::save_mode::e_save_mode m_eSaveMode; //!< Used to improve performance, if you know that you are just inserting or updating items in database + bool m_bRecursiveMode; //!< Recursive mode to iterate over each level of relationship + QSet m_lstRecursiveItems; //!< Used by recursive process to avoid infinite loop + QPair, long> *m_pColumns; //!< List of relation columns to fetch (syntax : my_relation { column_1, column_2, etc... }), if empty then fetch all columns + QString m_sCustomAlias; //!< Custom SQL table alias instead of generating a new one automatically + QString m_sCustomAliasOwner; //!< Custom SQL table alias owner instead of generating a new one automatically + qx::QxCollection *m_pLstExecBatch; //!< List of data to send to database when QSqlQuery::execBatch() method is used + bool m_bIsDistinct; //!< SQL query of type SELECT DISTINCT + + public: + QxSqlRelationParams(); + QxSqlRelationParams(long lIndex, long lOffset, QString *sql, IxSqlQueryBuilder *builder, QSqlQuery *query, void *pOwner, const QVariant &vId = QVariant(), qx::QxCollection *pLstExecBatch = NULL); + virtual ~QxSqlRelationParams(); + + inline QVariant id() const { return m_vId; } + inline long index() const { return m_lIndex; } + inline long indexOwner() const { return m_lIndexOwner; } + inline long offset() const { return m_lOffset; } + inline QString &sql() + { + qAssert(m_sql); + return (*m_sql); + } + inline const QString &sql() const + { + qAssert(m_sql); + return (*m_sql); + } + inline QSqlQuery &query() + { + qAssert(m_query); + return (*m_query); + } + inline const QSqlQuery &query() const + { + qAssert(m_query); + return (*m_query); + } + inline QSqlDatabase &database() + { + qAssert(m_database); + return (*m_database); + } + inline const QSqlDatabase &database() const + { + qAssert(m_database); + return (*m_database); + } + inline IxSqlQueryBuilder &builder() + { + qAssert(m_builder); + return (*m_builder); + } + inline const IxSqlQueryBuilder &builder() const + { + qAssert(m_builder); + return (*m_builder); + } + inline void *owner() const { return m_pOwner; } + inline qx::dao::sql_join::join_type joinType() const { return m_eJoinType; } + inline type_lst_relation_linked *relationX() const { return m_pRelationX; } + inline QString getTableAlias() const { return m_sTableAlias; } + inline qx::dao::save_mode::e_save_mode saveMode() const { return m_eSaveMode; } + inline bool recursiveMode() const { return m_bRecursiveMode; } + inline bool existRecursiveItem(void *p) const { return m_lstRecursiveItems.contains(p); } + inline QSet getColumns() const { return (m_pColumns ? m_pColumns->first : QSet()); } + inline bool checkColumns(const QString &s) const { return (!m_pColumns || m_pColumns->first.isEmpty() || m_pColumns->first.contains(s)); } + inline long getColumnsCount() const { return (m_pColumns ? m_pColumns->first.count() : 0); } + inline long getColumnsOffset() const { return (m_pColumns ? m_pColumns->second : 0); } + inline QString getCustomAlias() const { return m_sCustomAlias; } + inline QString getCustomAliasOwner() const { return m_sCustomAliasOwner; } + inline qx::QxCollection *getLstExecBatch() const { return m_pLstExecBatch; } + inline bool isDistinct() const { return m_bIsDistinct; } + + inline void setId(const QVariant &vId) { m_vId = vId; } + inline void setIndex(long lIndex) { m_lIndex = lIndex; } + inline void setIndexOwner(long lIndex) { m_lIndexOwner = lIndex; } + inline void setOffset(long lOffset) { m_lOffset = lOffset; } + inline void setSql(QString *sql) { m_sql = sql; } + inline void setQuery(QSqlQuery *query) { m_query = query; } + inline void setDatabase(QSqlDatabase *database) { m_database = database; } + inline void setOwner(void *pOwner) { m_pOwner = pOwner; } + inline void setJoinType(qx::dao::sql_join::join_type e) { m_eJoinType = e; } + inline void setRelationX(type_lst_relation_linked *p) { m_pRelationX = p; } + inline void setTableAlias(const QString &s) { m_sTableAlias = s; } + inline void setSaveMode(qx::dao::save_mode::e_save_mode e) { m_eSaveMode = e; } + inline void setRecursiveMode(bool b) { m_bRecursiveMode = b; } + inline void insertRecursiveItem(void *p) + { + if (p) + { + m_lstRecursiveItems.insert(p); + } + } + inline void setColumns(QPair, long> *p) { m_pColumns = p; } + inline void setColumnsOffset(long l) + { + if (m_pColumns) + { + m_pColumns->second = l; + } + } + inline void setCustomAlias(const QString &s) { m_sCustomAlias = s; } + inline void setCustomAliasOwner(const QString &s) { m_sCustomAliasOwner = s; } + inline void setLstExecBatch(qx::QxCollection *p) { m_pLstExecBatch = p; } + void setBuilder(IxSqlQueryBuilder *builder); + }; + +} // namespace qx + +#endif // _QX_SQL_RELATION_PARAMS_H_ diff --git a/include/QxDao/QxSqlRelation_ManyToMany.h b/include/QxDao/QxSqlRelation_ManyToMany.h new file mode 100644 index 0000000..36aaaee --- /dev/null +++ b/include/QxDao/QxSqlRelation_ManyToMany.h @@ -0,0 +1,341 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_RELATION_MANY_TO_MANY_H_ +#define _QX_SQL_RELATION_MANY_TO_MANY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlRelation_ManyToMany.h + * \author XDL Team + * \ingroup QxDao + * \brief Manage a relationship many-to-many defined between 2 classes (or between 2 tables in database) + */ + +#include + +namespace qx +{ + + /*! + * \ingroup QxDao + * \brief qx::QxSqlRelation_ManyToMany : manage a relationship many-to-many defined between 2 classes (or between 2 tables in database) + */ + template + class QxSqlRelation_ManyToMany : public QxSqlRelation + { + + private: + typedef typename QxSqlRelation::type_owner type_owner; + typedef typename QxSqlRelation::type_data type_data; + typedef typename QxSqlRelation::type_container type_container; + typedef typename QxSqlRelation::type_generic_container type_generic_container; + typedef typename QxSqlRelation::type_item type_item; + typedef typename type_generic_container::type_iterator type_iterator; + typedef typename type_item::type_value type_value; + + enum + { + is_data_container = QxSqlRelation::is_data_container + }; + + public: + QxSqlRelation_ManyToMany(IxDataMember *p, const QString &sExtraTable, const QString &sForeignKeyOwner, const QString &sForeignKeyDataType) : QxSqlRelation(p) + { + this->setRelationType(qx::IxSqlRelation::many_to_many); + this->setExtraTable(sExtraTable); + this->setForeignKeyOwner(sForeignKeyOwner); + this->setForeignKeyDataType(sForeignKeyDataType); + this->verifyParameters(); + } + virtual ~QxSqlRelation_ManyToMany() { static_assert(is_data_container, "is_data_container"); } + + virtual QString getDescription() const { return "relation many-to-many"; } + virtual bool getCartesianProduct() const { return true; } + virtual void createTable(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazySelect(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyFrom(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void eagerFrom(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyJoin(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyWhere(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void eagerWhere(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyWhereSoftDelete(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyFetch_ResolveInput(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void eagerFetch_ResolveInput(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyFetch_ResolveOutput(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyInsert(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyInsert_Values(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyUpdate(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyInsert_ResolveInput(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyUpdate_ResolveInput(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual QSqlError onBeforeSave(QxSqlRelationParams ¶ms) const + { + Q_UNUSED(params); + return QSqlError(); + } + + virtual QVariant getIdFromQuery(bool bEager, QxSqlRelationParams ¶ms, int iOffset, int iNameIndex) const + { + return this->getIdFromQuery_ManyToMany(bEager, params, iOffset, iNameIndex); + } + + virtual void updateOffset(bool bEager, QxSqlRelationParams ¶ms) const + { + this->updateOffset_ManyToMany(bEager, params); + } + + virtual void eagerSelect(QxSqlRelationParams ¶ms) const + { + this->eagerSelect_ManyToMany(params); + } + + virtual void eagerJoin(QxSqlRelationParams ¶ms) const + { + this->eagerJoin_ManyToMany(params); + } + + virtual void eagerWhereSoftDelete(QxSqlRelationParams ¶ms) const + { + this->eagerWhereSoftDelete_ManyToMany(params); + } + + virtual void *eagerFetch_ResolveOutput(QxSqlRelationParams ¶ms) const + { + if (!this->verifyOffset(params, true)) + { + return NULL; + } + QSqlQuery &query = params.query(); + IxDataMember *p = NULL; + IxDataMember *pId = this->getDataId(); + qAssert(pId); + long lIndex = 0; + long lOffsetId = ((pId && (!params.isDistinct())) ? pId->getNameCount() : 0); + bool bValidId(false); + long lOffsetOld = params.offset(); + this->updateOffset(true, params); + long lRelation = 0; + IxSqlRelation *pRelation = NULL; + + if (!params.isDistinct()) + { + for (int i = 0; i < pId->getNameCount(); i++) + { + QVariant v = query.value(lOffsetOld + i); + bValidId = (bValidId || qx::trait::is_valid_primary_key(v)); + } + if (!bValidId) + { + return NULL; + } + } + + type_item item = this->createItem(); + type_data &item_val = item.value_qx(); + if (!this->callTriggerBeforeFetch(item_val, params)) + { + return NULL; + } + + if (!params.isDistinct()) + { + for (int i = 0; i < pId->getNameCount(); i++) + { + QVariant v = query.value(lOffsetOld + i); + qx::cvt::from_variant(v, item.key(), "", i, qx::cvt::context::e_database); + } + for (int i = 0; i < pId->getNameCount(); i++) + { + QVariant v = query.value(lOffsetOld + i); + pId->fromVariant((&item_val), v, "", i, qx::cvt::context::e_database); + } + } + else + { + for (int i = 0; i < pId->getNameCount(); i++) + { + QVariant v = this->getIdFromQuery(true, params, (lOffsetOld + i), i); + qx::cvt::from_variant(v, item.key(), "", i, qx::cvt::context::e_database); + } + } + + long lOffsetRelation = (lOffsetOld + lOffsetId); + long lCurrIndex = 0; + while ((p = this->nextData(lIndex))) + { + if (params.checkColumns(p->getKey())) + { + p->fromVariant((&item_val), query.value(lCurrIndex + lOffsetRelation), -1, qx::cvt::context::e_database); + lCurrIndex++; + } + } + + if (params.relationX()) + { + long lOffsetCurrent = (lCurrIndex + lOffsetRelation); + QString sOldCustomAliasOwner = params.getCustomAliasOwner(); + params.setCustomAliasOwner(params.getCustomAlias()); + long lIndexOwnerOld = params.indexOwner(); + params.setIndexOwner(params.index()); + void *pOwnerOld = params.owner(); + params.setOwner(&item_val); + lOffsetOld = params.offset(); + params.setOffset(lOffsetCurrent); + while ((pRelation = this->nextRelation(lRelation))) + { + if (this->addLazyRelation(params, pRelation)) + { + pRelation->lazyFetch_ResolveOutput(params); + } + } + params.setOwner(pOwnerOld); + params.setOffset(lOffsetOld); + params.setCustomAliasOwner(sOldCustomAliasOwner); + params.setIndexOwner(lIndexOwnerOld); + } + + if (!this->callTriggerAfterFetch(item_val, params)) + { + return NULL; + } + type_value *pValue = type_generic_container::insertItem(this->getContainer(params), item); + if (!type_item::is_value_pointer && pValue) + { + return pValue; + } + return (&item_val); + } + + virtual QSqlError onAfterSave(QxSqlRelationParams ¶ms) const + { + QSqlError daoError; + if (this->isNullData(params)) + { + return this->deleteFromExtraTable(params); + } + if (!params.recursiveMode()) + { + daoError = qx::dao::save(this->getContainer(params), (¶ms.database())); + } + else + { + daoError = qx::dao::save_with_relation_recursive(this->getContainer(params), params.saveMode(), (¶ms.database()), (¶ms)); + } + if (daoError.isValid()) + { + return daoError; + } + daoError = this->deleteFromExtraTable(params); + if (daoError.isValid()) + { + return daoError; + } + daoError = this->insertIntoExtraTable(params); + if (daoError.isValid()) + { + return daoError; + } + return QSqlError(); + } + + virtual QString createExtraTable() const + { + return this->createExtraTable_ManyToMany(); + } + + private: + void verifyParameters() + { + qAssert(!this->getExtraTable().isEmpty() && !this->getForeignKeyOwner().isEmpty() && !this->getForeignKeyDataType().isEmpty() && (this->getForeignKeyOwner() != this->getForeignKeyDataType())); + } + + QSqlError deleteFromExtraTable(QxSqlRelationParams ¶ms) const + { + return this->deleteFromExtraTable_ManyToMany(params); + } + + QSqlError insertIntoExtraTable(QxSqlRelationParams ¶ms) const + { + IxDataMember *pIdOwner = this->getDataIdOwner(); + qAssert(pIdOwner); + IxDataMember *pIdData = this->getDataId(); + qAssert(pIdData); + if (!pIdOwner || !pIdData) + { + return QSqlError(); + } + QStringList lstForeignKeyOwner = this->getForeignKeyOwner().split("|"); + QStringList lstForeignKeyDataType = this->getForeignKeyDataType().split("|"); + qAssert(pIdOwner->getNameCount() == lstForeignKeyOwner.count()); + qAssert(pIdData->getNameCount() == lstForeignKeyDataType.count()); + + QString sql = "INSERT INTO " + this->getExtraTable() + " ("; + sql += pIdOwner->getSqlName(", ", this->getForeignKeyOwner(), false, (¶ms.builder())); + sql += ", "; + sql += pIdData->getSqlName(", ", this->getForeignKeyDataType(), false, (¶ms.builder())); + sql += ") VALUES ("; + sql += pIdOwner->getSqlPlaceHolder("", -1, ", ", this->getForeignKeyOwner()) + ", " + pIdData->getSqlPlaceHolder("", -1, ", ", this->getForeignKeyDataType()) + ")"; + if (this->traceSqlQuery()) + { + qDebug("[QxOrm] sql query (extra-table) : %s", qPrintable(sql)); + } + + type_item item; + type_container &container = this->getContainer(params); + type_iterator itr = type_generic_container::begin(container, item); + type_iterator itr_end = type_generic_container::end(container); + QSqlQuery queryInsert(params.database()); + if (!queryInsert.prepare(sql)) + { + return queryInsert.lastError(); + } + + while (itr != itr_end) + { + pIdOwner->setSqlPlaceHolder(queryInsert, params.owner(), "", this->getForeignKeyOwner()); + pIdData->setSqlPlaceHolder(queryInsert, (&item.value_qx()), "", this->getForeignKeyDataType()); + if (!queryInsert.exec()) + { + return queryInsert.lastError(); + } + itr = type_generic_container::next(container, itr, item); + } + + return QSqlError(); + } + }; + +} // namespace qx + +#endif // _QX_SQL_RELATION_MANY_TO_MANY_H_ diff --git a/include/QxDao/QxSqlRelation_ManyToOne.h b/include/QxDao/QxSqlRelation_ManyToOne.h new file mode 100644 index 0000000..7c49292 --- /dev/null +++ b/include/QxDao/QxSqlRelation_ManyToOne.h @@ -0,0 +1,300 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_RELATION_MANY_TO_ONE_H_ +#define _QX_SQL_RELATION_MANY_TO_ONE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlRelation_ManyToOne.h + * \author XDL Team + * \ingroup QxDao + * \brief Manage a relationship many-to-one defined between 2 classes (or between 2 tables in database) + */ + +#include + +namespace qx +{ + + /*! + * \ingroup QxDao + * \brief qx::QxSqlRelation_ManyToOne : manage a relationship many-to-one defined between 2 classes (or between 2 tables in database) + */ + template + class QxSqlRelation_ManyToOne : public QxSqlRelation + { + + private: + typedef typename QxSqlRelation::type_owner type_owner; + typedef typename QxSqlRelation::type_data type_data; + + public: + QxSqlRelation_ManyToOne(IxDataMember *p) : QxSqlRelation(p) { this->setRelationType(qx::IxSqlRelation::many_to_one); } + virtual ~QxSqlRelation_ManyToOne() { ; } + + virtual QString getDescription() const { return "relation many-to-one"; } + virtual QString createExtraTable() const { return ""; } + virtual bool getCartesianProduct() const { return false; } + virtual void lazyFrom(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void eagerFrom(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyJoin(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyWhere(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void eagerWhere(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyWhereSoftDelete(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyFetch_ResolveInput(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void eagerFetch_ResolveInput(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual QSqlError onAfterSave(QxSqlRelationParams ¶ms) const + { + Q_UNUSED(params); + return QSqlError(); + } + + virtual QSqlError onBeforeSave(QxSqlRelationParams ¶ms) const + { + if (this->isNullData(params) || params.recursiveMode()) + { + return QSqlError(); + } + return qx::dao::save(this->getData(params), (¶ms.database())); + } + + virtual void lazyUpdate_ResolveInput(QxSqlRelationParams ¶ms) const + { + this->lazyInsert_ResolveInput(params); + } + + virtual QVariant getIdFromQuery(bool bEager, QxSqlRelationParams ¶ms, int iOffset, int iNameIndex) const + { + return this->getIdFromQuery_ManyToOne(bEager, params, iOffset, iNameIndex); + } + + virtual void updateOffset(bool bEager, QxSqlRelationParams ¶ms) const + { + this->updateOffset_ManyToOne(bEager, params); + } + + virtual void createTable(QxSqlRelationParams ¶ms) const + { + this->createTable_ManyToOne(params); + } + + virtual void lazySelect(QxSqlRelationParams ¶ms) const + { + this->lazySelect_ManyToOne(params); + } + + virtual void eagerSelect(QxSqlRelationParams ¶ms) const + { + this->eagerSelect_ManyToOne(params); + } + + virtual void eagerJoin(QxSqlRelationParams ¶ms) const + { + this->eagerJoin_ManyToOne(params); + } + + virtual void eagerWhereSoftDelete(QxSqlRelationParams ¶ms) const + { + this->eagerWhereSoftDelete_ManyToOne(params); + } + + virtual void lazyFetch_ResolveOutput(QxSqlRelationParams ¶ms) const + { + if (!this->verifyOffset(params, false)) + { + return; + } + QSqlQuery &query = params.query(); + typename QxSqlRelation::type_owner &currOwner = this->getOwner(params); + IxDataMember *pData = this->getDataMember(); + qAssert(pData); + if (!pData) + { + return; + } + bool bValidId(false); + for (int i = 0; i < pData->getNameCount(); i++) + { + QVariant vId = query.value(params.offset() + i); + bValidId = (bValidId || qx::trait::is_valid_primary_key(vId)); + } + if (pData && bValidId) + { + for (int i = 0; i < pData->getNameCount(); i++) + { + pData->fromVariant((&currOwner), query.value(params.offset() + i), i, qx::cvt::context::e_database); + } + } + this->updateOffset(false, params); + } + + virtual void *eagerFetch_ResolveOutput(QxSqlRelationParams ¶ms) const + { + if (!this->verifyOffset(params, false)) + { + return NULL; + } + QSqlQuery &query = params.query(); + typename QxSqlRelation::type_owner &currOwner = this->getOwner(params); + IxDataMember *pData = this->getDataMember(); + qAssert(pData); + if (!pData) + { + return NULL; + } + IxDataMember *p = NULL; + IxDataMember *pId = this->getDataId(); + qAssert(pId); + if (!pId) + { + return NULL; + } + long lIndex = 0; + long lOffsetId = ((pId && (!params.isDistinct())) ? pId->getNameCount() : 0); + long lOffsetData = ((pData && (!params.isDistinct())) ? pData->getNameCount() : 0); + long lOffsetOld = params.offset(); + this->updateOffset(true, params); + long lOffsetRelation = (lOffsetOld + lOffsetId + lOffsetData); + long lRelation = 0; + IxSqlRelation *pRelation = NULL; + bool bValidId(false), bValidIdBis(false); + + if (!params.isDistinct()) + { + for (int i = 0; i < pData->getNameCount(); i++) + { + QVariant vId = query.value(lOffsetOld + i); + bValidId = (bValidId || qx::trait::is_valid_primary_key(vId)); + } + for (int i = 0; i < pId->getNameCount(); i++) + { + QVariant vIdBis = query.value(lOffsetOld + lOffsetData + i); + bValidIdBis = (bValidIdBis || qx::trait::is_valid_primary_key(vIdBis)); + } + if (pData && bValidId) + { + for (int i = 0; i < pData->getNameCount(); i++) + { + pData->fromVariant((&currOwner), query.value(lOffsetOld + i), i, qx::cvt::context::e_database); + } + } + if (!bValidIdBis) + { + return NULL; + } + } + + type_data &currData = this->getData(params); + if (!this->callTriggerBeforeFetch(currData, params)) + { + return NULL; + } + + if (pId && (!params.isDistinct())) + { + for (int i = 0; i < pId->getNameCount(); i++) + { + pId->fromVariant((&currData), query.value(lOffsetOld + lOffsetData + i), i, qx::cvt::context::e_database); + } + } + while ((p = this->nextData(lIndex))) + { + if (params.checkColumns(p->getKey())) + { + p->fromVariant((&currData), query.value(lOffsetRelation++), -1, qx::cvt::context::e_database); + } + } + + if (params.relationX()) + { + QString sOldCustomAliasOwner = params.getCustomAliasOwner(); + params.setCustomAliasOwner(params.getCustomAlias()); + long lIndexOwnerOld = params.indexOwner(); + params.setIndexOwner(params.index()); + void *pOwnerOld = params.owner(); + params.setOwner(&currData); + lOffsetOld = params.offset(); + params.setOffset(lOffsetRelation++); + while ((pRelation = this->nextRelation(lRelation))) + { + if (this->addLazyRelation(params, pRelation)) + { + pRelation->lazyFetch_ResolveOutput(params); + } + } + params.setOwner(pOwnerOld); + params.setOffset(lOffsetOld); + params.setCustomAliasOwner(sOldCustomAliasOwner); + params.setIndexOwner(lIndexOwnerOld); + } + + if (!this->callTriggerAfterFetch(currData, params)) + { + return NULL; + } + return (&currData); + } + + virtual void lazyInsert(QxSqlRelationParams ¶ms) const + { + this->lazyInsert_ManyToOne(params); + } + + virtual void lazyInsert_Values(QxSqlRelationParams ¶ms) const + { + this->lazyInsert_Values_ManyToOne(params); + } + + virtual void lazyUpdate(QxSqlRelationParams ¶ms) const + { + this->lazyUpdate_ManyToOne(params); + } + + virtual void lazyInsert_ResolveInput(QxSqlRelationParams ¶ms) const + { + QSqlQuery &query = params.query(); + typename QxSqlRelation::type_owner &currOwner = this->getOwner(params); + IxDataMember *pData = this->getDataMember(); + qAssert(pData); + if (pData) + { + pData->setSqlPlaceHolder(query, (&currOwner), "", "", false, params.getLstExecBatch()); + } + } + }; + +} // namespace qx + +#endif // _QX_SQL_RELATION_MANY_TO_ONE_H_ diff --git a/include/QxDao/QxSqlRelation_OneToMany.h b/include/QxDao/QxSqlRelation_OneToMany.h new file mode 100644 index 0000000..dfb131d --- /dev/null +++ b/include/QxDao/QxSqlRelation_OneToMany.h @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_RELATION_ONE_TO_MANY_H_ +#define _QX_SQL_RELATION_ONE_TO_MANY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlRelation_OneToMany.h + * \author XDL Team + * \ingroup QxDao + * \brief Manage a relationship one-to-many defined between 2 classes (or between 2 tables in database) + */ + +#include + +namespace qx +{ + + /*! + * \ingroup QxDao + * \brief qx::QxSqlRelation_OneToMany : manage a relationship one-to-many defined between 2 classes (or between 2 tables in database) + */ + template + class QxSqlRelation_OneToMany : public QxSqlRelation + { + + private: + typedef typename QxSqlRelation::type_owner type_owner; + typedef typename QxSqlRelation::type_data type_data; + typedef typename QxSqlRelation::type_container type_container; + typedef typename QxSqlRelation::type_generic_container type_generic_container; + typedef typename QxSqlRelation::type_item type_item; + typedef typename type_generic_container::type_iterator type_iterator; + typedef typename type_item::type_value type_value; + + enum + { + is_data_container = QxSqlRelation::is_data_container + }; + + public: + QxSqlRelation_OneToMany(IxDataMember *p, const QString &sForeignKey) : QxSqlRelation(p) + { + this->setRelationType(qx::IxSqlRelation::one_to_many); + this->setForeignKey(sForeignKey); + qAssert(!this->getForeignKey().isEmpty()); + } + virtual ~QxSqlRelation_OneToMany() { static_assert(is_data_container, "is_data_container"); } + + virtual QString getDescription() const { return "relation one-to-many"; } + virtual QString createExtraTable() const { return ""; } + virtual bool getCartesianProduct() const { return true; } + virtual void createTable(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazySelect(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyFrom(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void eagerFrom(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyJoin(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyWhere(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void eagerWhere(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyWhereSoftDelete(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyFetch_ResolveInput(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void eagerFetch_ResolveInput(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyFetch_ResolveOutput(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyInsert(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyInsert_Values(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyUpdate(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyInsert_ResolveInput(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyUpdate_ResolveInput(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual QSqlError onBeforeSave(QxSqlRelationParams ¶ms) const + { + Q_UNUSED(params); + return QSqlError(); + } + + virtual QSqlError onAfterSave(QxSqlRelationParams ¶ms) const + { + if (this->isNullData(params)) + { + return QSqlError(); + } + this->forceParentIdToAllChildren(params); + if (!params.recursiveMode()) + { + return qx::dao::save(this->getContainer(params), (¶ms.database())); + } + else + { + return qx::dao::save_with_relation_recursive(this->getContainer(params), params.saveMode(), (¶ms.database()), (¶ms)); + } + } + + virtual QVariant getIdFromQuery(bool bEager, QxSqlRelationParams ¶ms, int iOffset, int iNameIndex) const + { + return this->getIdFromQuery_OneToMany(bEager, params, iOffset, iNameIndex); + } + + virtual void updateOffset(bool bEager, QxSqlRelationParams ¶ms) const + { + this->updateOffset_OneToMany(bEager, params); + } + + virtual void eagerSelect(QxSqlRelationParams ¶ms) const + { + this->eagerSelect_OneToMany(params); + } + + virtual void eagerJoin(QxSqlRelationParams ¶ms) const + { + this->eagerJoin_OneToMany(params); + } + + virtual void eagerWhereSoftDelete(QxSqlRelationParams ¶ms) const + { + this->eagerWhereSoftDelete_OneToMany(params); + } + + virtual void *eagerFetch_ResolveOutput(QxSqlRelationParams ¶ms) const + { + if (!this->verifyOffset(params, true)) + { + return NULL; + } + QSqlQuery &query = params.query(); + IxDataMember *p = NULL; + IxDataMember *pId = this->getDataId(); + qAssert(pId); + if (!pId) + { + return NULL; + } + IxDataMember *pForeign = this->getDataByKey(this->getForeignKey()); + qAssert(pForeign); + if (!pForeign) + { + return NULL; + } + long lIndex = 0; + long lOffsetId = ((pId && (!params.isDistinct())) ? pId->getNameCount() : 0); + long lOffsetForeign = ((pForeign && (!params.isDistinct())) ? pForeign->getNameCount() : 0); + long lOffsetOld = params.offset(); + this->updateOffset(true, params); + long lOffsetRelation = (lOffsetOld + lOffsetId + lOffsetForeign); + long lRelation = 0; + IxSqlRelation *pRelation = NULL; + bool bValidId(false), bValidForeign(false); + + if (!params.isDistinct()) + { + for (int i = 0; i < pId->getNameCount(); i++) + { + QVariant vId = query.value(lOffsetOld + i); + bValidId = (bValidId || qx::trait::is_valid_primary_key(vId)); + } + for (int i = 0; i < pForeign->getNameCount(); i++) + { + QVariant vForeign = query.value(lOffsetOld + lOffsetId + i); + bValidForeign = (bValidForeign || qx::trait::is_valid_primary_key(vForeign)); + } + if (!bValidId || !bValidForeign) + { + return NULL; + } + } + + type_item item = this->createItem(); + type_data &item_val = item.value_qx(); + if (!this->callTriggerBeforeFetch(item_val, params)) + { + return NULL; + } + + if (!params.isDistinct()) + { + for (int i = 0; i < pId->getNameCount(); i++) + { + QVariant v = query.value(lOffsetOld + i); + qx::cvt::from_variant(v, item.key(), "", i, qx::cvt::context::e_database); + } + for (int i = 0; i < pId->getNameCount(); i++) + { + QVariant v = query.value(lOffsetOld + i); + pId->fromVariant((&item_val), v, "", i, qx::cvt::context::e_database); + } + for (int i = 0; i < pForeign->getNameCount(); i++) + { + QVariant v = query.value(lOffsetOld + lOffsetId + i); + pForeign->fromVariant((&item_val), v, "", i, qx::cvt::context::e_database); + } + } + else + { + for (int i = 0; i < pId->getNameCount(); i++) + { + QVariant v = this->getIdFromQuery(true, params, (lOffsetOld + i), i); + qx::cvt::from_variant(v, item.key(), "", i, qx::cvt::context::e_database); + } + } + + while ((p = this->nextData(lIndex))) + { + if ((p != pForeign) && (params.checkColumns(p->getKey()))) + { + p->fromVariant((&item_val), query.value(lOffsetRelation++), -1, qx::cvt::context::e_database); + } + } + + if (params.relationX()) + { + QString sOldCustomAliasOwner = params.getCustomAliasOwner(); + params.setCustomAliasOwner(params.getCustomAlias()); + long lIndexOwnerOld = params.indexOwner(); + params.setIndexOwner(params.index()); + void *pOwnerOld = params.owner(); + params.setOwner(&item_val); + lOffsetOld = params.offset(); + params.setOffset(lOffsetRelation++); + while ((pRelation = this->nextRelation(lRelation))) + { + if (this->addLazyRelation(params, pRelation)) + { + pRelation->lazyFetch_ResolveOutput(params); + } + } + params.setOwner(pOwnerOld); + params.setOffset(lOffsetOld); + params.setCustomAliasOwner(sOldCustomAliasOwner); + params.setIndexOwner(lIndexOwnerOld); + } + + if (!this->callTriggerAfterFetch(item_val, params)) + { + return NULL; + } + type_value *pValue = type_generic_container::insertItem(this->getContainer(params), item); + if (!type_item::is_value_pointer && pValue) + { + return pValue; + } + return (&item_val); + } + + private: + void forceParentIdToAllChildren(QxSqlRelationParams ¶ms) const + { + bool bForce = qx::QxSqlDatabase::getSingleton()->getForceParentIdToAllChildren(); + if (!bForce || !params.owner()) + { + return; + } + IxDataMember *pIdOwner = this->getDataIdOwner(); + if (!pIdOwner) + { + return; + } + IxDataMember *pForeign = this->getDataByKey(this->getForeignKey()); + if (!pForeign) + { + return; + } + if (pIdOwner->getNameCount() != pForeign->getNameCount()) + { + return; + } + + QList vIdOwner; + for (int i = 0; i < pIdOwner->getNameCount(); i++) + { + vIdOwner.append(pIdOwner->toVariant(params.owner(), i, qx::cvt::context::e_database)); + } + + type_item item; + type_container &container = this->getContainer(params); + type_iterator itr = type_generic_container::begin(container, item); + type_iterator itr_end = type_generic_container::end(container); + + while (itr != itr_end) + { + type_data &item_val = item.value_qx(); + for (int i = 0; i < vIdOwner.count(); i++) + { + pForeign->fromVariant((&item_val), vIdOwner.at(i), "", i, qx::cvt::context::e_database); + } + itr = type_generic_container::next(container, itr, item); + } + } + }; + +} // namespace qx + +#endif // _QX_SQL_RELATION_ONE_TO_MANY_H_ diff --git a/include/QxDao/QxSqlRelation_OneToOne.h b/include/QxDao/QxSqlRelation_OneToOne.h new file mode 100644 index 0000000..831f2ae --- /dev/null +++ b/include/QxDao/QxSqlRelation_OneToOne.h @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_RELATION_ONE_TO_ONE_H_ +#define _QX_SQL_RELATION_ONE_TO_ONE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlRelation_OneToOne.h + * \author XDL Team + * \ingroup QxDao + * \brief Manage a relationship one-to-one defined between 2 classes (or between 2 tables in database) + */ + +#include + +namespace qx +{ + + /*! + * \ingroup QxDao + * \brief qx::QxSqlRelation_OneToOne : manage a relationship one-to-one defined between 2 classes (or between 2 tables in database) + */ + template + class QxSqlRelation_OneToOne : public QxSqlRelation + { + + private: + typedef typename QxSqlRelation::type_owner type_owner; + typedef typename QxSqlRelation::type_data type_data; + + public: + QxSqlRelation_OneToOne(IxDataMember *p) : QxSqlRelation(p) { this->setRelationType(qx::IxSqlRelation::one_to_one); } + virtual ~QxSqlRelation_OneToOne() { ; } + + virtual QString getDescription() const { return "relation one-to-one"; } + virtual QString createExtraTable() const { return ""; } + virtual bool getCartesianProduct() const { return false; } + virtual void createTable(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazySelect(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyFrom(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void eagerFrom(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyJoin(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyWhere(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void eagerWhere(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyWhereSoftDelete(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyFetch_ResolveInput(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void eagerFetch_ResolveInput(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyFetch_ResolveOutput(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyInsert(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyInsert_Values(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyUpdate(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyInsert_ResolveInput(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual void lazyUpdate_ResolveInput(QxSqlRelationParams ¶ms) const { Q_UNUSED(params); } + virtual QSqlError onBeforeSave(QxSqlRelationParams ¶ms) const + { + Q_UNUSED(params); + return QSqlError(); + } + + virtual QSqlError onAfterSave(QxSqlRelationParams ¶ms) const + { + if (this->isNullData(params)) + { + return QSqlError(); + } + if (!params.recursiveMode()) + { + return qx::dao::save(this->getData(params), (¶ms.database())); + } + else + { + return qx::dao::save_with_relation_recursive(this->getData(params), params.saveMode(), (¶ms.database()), (¶ms)); + } + } + + virtual QVariant getIdFromQuery(bool bEager, QxSqlRelationParams ¶ms, int iOffset, int iNameIndex) const + { + return this->getIdFromQuery_OneToOne(bEager, params, iOffset, iNameIndex); + } + + virtual void updateOffset(bool bEager, QxSqlRelationParams ¶ms) const + { + this->updateOffset_OneToOne(bEager, params); + } + + virtual void eagerSelect(QxSqlRelationParams ¶ms) const + { + this->eagerSelect_OneToOne(params); + } + + virtual void eagerJoin(QxSqlRelationParams ¶ms) const + { + this->eagerJoin_OneToOne(params); + } + + virtual void eagerWhereSoftDelete(QxSqlRelationParams ¶ms) const + { + this->eagerWhereSoftDelete_OneToOne(params); + } + + virtual void *eagerFetch_ResolveOutput(QxSqlRelationParams ¶ms) const + { + if (!this->verifyOffset(params, true)) + { + return NULL; + } + QSqlQuery &query = params.query(); + IxDataMember *p = NULL; + IxDataMember *pId = this->getDataId(); + qAssert(pId); + if (!pId) + { + return NULL; + } + long lIndex = 0; + long lOffsetId = ((pId && (!params.isDistinct())) ? pId->getNameCount() : 0); + bool bValidId(false); + long lOffsetOld = params.offset(); + this->updateOffset(true, params); + long lRelation = 0; + IxSqlRelation *pRelation = NULL; + + if (!params.isDistinct()) + { + for (int i = 0; i < pId->getNameCount(); i++) + { + QVariant v = query.value(lOffsetOld + i); + bValidId = (bValidId || qx::trait::is_valid_primary_key(v)); + } + if (!bValidId) + { + return NULL; + } + } + + type_data &currData = this->getData(params); + if (!this->callTriggerBeforeFetch(currData, params)) + { + return NULL; + } + + if (!params.isDistinct()) + { + for (int i = 0; i < pId->getNameCount(); i++) + { + QVariant v = query.value(lOffsetOld + i); + pId->fromVariant((&currData), v, i, qx::cvt::context::e_database); + } + } + + long lOffsetRelation = (lOffsetOld + lOffsetId); + long lCurrIndex = 0; + while ((p = this->nextData(lIndex))) + { + if (params.checkColumns(p->getKey())) + { + p->fromVariant((&currData), query.value(lCurrIndex + lOffsetRelation), -1, qx::cvt::context::e_database); + lCurrIndex++; + } + } + + if (params.relationX()) + { + long lOffsetCurrent = (lCurrIndex + lOffsetRelation); + QString sOldCustomAliasOwner = params.getCustomAliasOwner(); + params.setCustomAliasOwner(params.getCustomAlias()); + long lIndexOwnerOld = params.indexOwner(); + params.setIndexOwner(params.index()); + void *pOwnerOld = params.owner(); + params.setOwner(&currData); + lOffsetOld = params.offset(); + params.setOffset(lOffsetCurrent); + while ((pRelation = this->nextRelation(lRelation))) + { + if (this->addLazyRelation(params, pRelation)) + { + pRelation->lazyFetch_ResolveOutput(params); + } + } + params.setOwner(pOwnerOld); + params.setOffset(lOffsetOld); + params.setCustomAliasOwner(sOldCustomAliasOwner); + params.setIndexOwner(lIndexOwnerOld); + } + + if (!this->callTriggerAfterFetch(currData, params)) + { + return NULL; + } + return (&currData); + } + }; + +} // namespace qx + +#endif // _QX_SQL_RELATION_ONE_TO_ONE_H_ diff --git a/include/QxDao/QxSqlRelation_RawData.h b/include/QxDao/QxSqlRelation_RawData.h new file mode 100644 index 0000000..d5df3c0 --- /dev/null +++ b/include/QxDao/QxSqlRelation_RawData.h @@ -0,0 +1,30 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ diff --git a/include/QxDao/QxSqlSaveMode.h b/include/QxDao/QxSqlSaveMode.h new file mode 100644 index 0000000..d2c2748 --- /dev/null +++ b/include/QxDao/QxSqlSaveMode.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SQL_SAVE_MODE_H_ +#define _QX_SQL_SAVE_MODE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSqlSaveMode.h + * \author XDL Team + * \ingroup QxDao + * \brief To improve performance, if you know that you are just inserting or updating items in database + */ + +namespace qx +{ + namespace dao + { + + /*! + * \ingroup QxDao + * \brief qx::dao::save_mode : to improve performance, if you know that you are just inserting or updating items in database + */ + struct save_mode + { + + enum e_save_mode + { + e_none, + e_check_insert_or_update, + e_insert_only, + e_update_only + }; + }; + + } // namespace dao +} // namespace qx + +#endif // _QX_SQL_SAVE_MODE_H_ diff --git a/include/QxDao/QxTimeNeutral.h b/include/QxDao/QxTimeNeutral.h new file mode 100644 index 0000000..f44c53e --- /dev/null +++ b/include/QxDao/QxTimeNeutral.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_TIME_NEUTRAL_H_ +#define _QX_TIME_NEUTRAL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxTimeNeutral.h + * \author XDL Team + * \ingroup QxDao + * \brief Helper class to store a time value into database under neutral format (HHMMSS) => cross database compatibility + */ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#include +#include +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#include +#include + +#include + +#include + +namespace qx +{ + class QxTimeNeutral; +} // namespace qx + +QX_DLL_EXPORT QDataStream &operator<<(QDataStream &stream, const qx::QxTimeNeutral &t) QX_USED; +QX_DLL_EXPORT QDataStream &operator>>(QDataStream &stream, qx::QxTimeNeutral &t) QX_USED; + +namespace qx +{ + + /*! + * \ingroup QxDao + * \brief qx::QxTimeNeutral : helper class to store a time value into database under neutral format (HHMMSS) => cross database compatibility + */ + class QxTimeNeutral + { + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + friend class boost::serialization::access; +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + friend QX_DLL_EXPORT QDataStream & ::operator<<(QDataStream &stream, const qx::QxTimeNeutral &t); + friend QX_DLL_EXPORT QDataStream & ::operator>>(QDataStream &stream, qx::QxTimeNeutral &t); + + private: + QTime m_time; //!< Data value under QTime format from Qt library + QString m_neutral; //!< Data value under neutral format 'hhmmss' + + public: + QxTimeNeutral() { ; } + explicit QxTimeNeutral(const QTime &time) : m_time(time) { update(); } + explicit QxTimeNeutral(const QString &neutral) : m_neutral(neutral) { update(); } + virtual ~QxTimeNeutral() { ; } + + inline QTime toTime() const { return m_time; } + inline QString toNeutral() const { return m_neutral; } + inline bool isValid() const { return m_time.isValid(); } + + inline void setTime(const QTime &time) + { + m_neutral = ""; + m_time = time; + update(); + } + inline void setNeutral(const QString &neutral) + { + m_time = QTime(); + m_neutral = neutral; + update(); + } + + static QxTimeNeutral fromTime(const QTime &time) { return QxTimeNeutral(time); } + static QxTimeNeutral fromNeutral(const QString &neutral) { return QxTimeNeutral(neutral); } + + private: + static inline const char *format() { return "hhmmss"; } + + void update() + { + if (m_neutral.isEmpty() && !m_time.isValid()) + { + return; + } + else if (m_time.isValid()) + { + m_neutral = m_time.toString(format()); + } + else + { + qAssert(m_neutral.size() == QString(format()).size()); + m_time = QTime::fromString(m_neutral, format()); + qAssert(m_time.isValid()); + } + } + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + template + void serialize(Archive &ar, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("time_neutral", m_neutral); + if (Archive::is_loading::value) + { + m_time = QTime(); + update(); + } + } +#endif // _QX_ENABLE_BOOST_SERIALIZATION + }; + +} // namespace qx + +QX_REGISTER_CLASS_NAME(qx::QxTimeNeutral) + +#endif // _QX_TIME_NEUTRAL_H_ diff --git a/include/QxDaoRepository.h b/include/QxDaoRepository.h new file mode 100644 index 0000000..9a3f628 --- /dev/null +++ b/include/QxDaoRepository.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_DAO_REPOSITORY_H_ +#define _QX_DAO_REPOSITORY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxDaoRepository.h + * \author XDL Team + * \ingroup QxDao + * \brief Include all headers required to use repository pattern + */ + +#include + +#include +#include +#include + +#endif // _QX_DAO_REPOSITORY_H_ diff --git a/include/QxDataMember/IxDataMember.h b/include/QxDataMember/IxDataMember.h new file mode 100644 index 0000000..66a9ccb --- /dev/null +++ b/include/QxDataMember/IxDataMember.h @@ -0,0 +1,430 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_DATA_MEMBER_H_ +#define _IX_DATA_MEMBER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxDataMember.h + * \author XDL Team + * \ingroup QxDataMember + * \brief Common interface for all class properties registered into QxOrm context + */ + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) +#endif // _MSC_VER + +#include +#include + +#ifndef _QX_NO_JSON +#include +#endif // _QX_NO_JSON + +#include +#include +#include + +#include + +#include + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +#define QX_IX_DATA_MEMBER_PURE_VIRTUAL_ARCHIVE(ArchiveInput, ArchiveOutput) \ + virtual void toArchive(const void *pOwner, ArchiveOutput &ar) const = 0; \ + virtual void fromArchive(void *pOwner, ArchiveInput &ar) = 0; + +namespace qx +{ + + class IxDataMemberX; + class IxSqlRelation; + class IxSqlQueryBuilder; + struct IxDataMemberSqlCallbackParams; + + namespace dao + { + namespace detail + { + + class IxDao_Helper; + + } // namespace detail + } // namespace dao + + /*! + * \ingroup QxDataMember + * \brief qx::IxDataMember : common interface for all class properties registered into QxOrm context + */ + class QX_DLL_EXPORT IxDataMember : public qx::QxPropertyBag + { + + template + friend class QxDataMember; + + public: + typedef std::function type_fct_sql_callback; + + private: + struct IxDataMemberImpl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + + static QMutex m_mutex; //!< Mutex => qx::IxDataMember is thread-safe + + public: + IxDataMember(const QString &sKey, long lVersion, bool bSerialize, bool bDao, IxDataMember *pImpl); + virtual ~IxDataMember() = 0; + + QString getKey() const; + QString getName() const; + int getNameCount() const; + QString getNameParent() const; + const char *getNamePtr() const; + QString getDescription() const; + QString getFormat() const; + long getVersion() const; + bool getSerialize() const; + bool getDao() const; + QVariant getDefaultValue() const; + QVariant getMinValue() const; + QVariant getMaxValue() const; + int getPrecision() const; + int getMinLength() const; + int getMaxLength() const; + bool getRequired() const; + bool getReadOnly() const; + bool getAutoIncrement() const; + bool getNotNull() const; + bool getIsPrimaryKey() const; + bool getIsIndex() const; + bool getIsUnique() const; + IxDataMemberX *getParent() const; + IxSqlRelation *getSqlRelation() const; + bool hasSqlRelation() const; + bool getAccessDataPointer() const; + virtual QString getType() const; + QString getTypeParent() const; + IxDataMember *getPImpl() const; + + void setName(const QString &s); + void setNameParent(const QString &s); + void setDescription(const QString &s); + void setFormat(const QString &s); + void setSqlType(const QString &s); + void setSqlAlias(const QString &s); + void setVersion(long l); + void setSerialize(bool b); + void setDao(bool b); + void setDefaultValue(const QVariant &v); + void setPrecision(int i); + void setRequired(bool b); + void setReadOnly(bool b); + void setAutoIncrement(bool b); + void setIsPrimaryKey(bool b); + void setIsIndex(bool b); + void setIsUnique(bool b); + void setParent(IxDataMemberX *p); + void setSqlRelation(IxSqlRelation *p); + void setAccessDataPointer(bool b); + + void setMinValue(long lMinValue, const QString &sMessage = QString()); + void setMinValue(double dMinValue, const QString &sMessage = QString()); + void setMaxValue(long lMaxValue, const QString &sMessage = QString()); + void setMaxValue(double dMaxValue, const QString &sMessage = QString()); + void setMinLength(int iMinLength, const QString &sMessage = QString()); + void setMaxLength(int iMaxLength, const QString &sMessage = QString()); + void setNotNull(bool bNotNull, const QString &sMessage = QString()); + + bool isThereRelationPartOfPrimaryKey(int iIndexNamePK, IxSqlRelation *&pRelation, int &iIndexNameFK) const; + bool isPartOfPrimaryKey(int iIndexNameFK, IxDataMember *&pPrimaryKey, int &iIndexNamePK) const; + void setRelationPartOfPrimaryKey(int iIndexNamePK, IxSqlRelation *pRelation, int iIndexNameFK); + void setPartOfPrimaryKey(int iIndexNameFK, IxDataMember *pPrimaryKey, int iIndexNamePK); + + QString getName(int iIndex, const QString &sOtherName = QString()) const; + QString getSqlAlias(const QString &sTable = QString(), bool bClauseWhere = false, int iIndexName = 0, qx::IxSqlQueryBuilder *pSqlQueryBuilder = NULL) const; + QString getSqlType(int iIndexName = -1) const; + QString getSqlTypeAndParams(int iIndexName = -1) const; + QString getSqlPlaceHolder(const QString &sAppend = QString(), int iIndexName = 0, const QString &sSep = QString(", "), const QString &sOtherName = QString(), bool bCheckFKPartOfPK = false) const; + void setSqlPlaceHolder(QSqlQuery &query, void *pOwner, const QString &sAppend = QString(), const QString &sOtherName = QString(), bool bCheckFKPartOfPK = false, qx::QxCollection *pLstExecBatch = NULL) const; + QString getSqlAliasEqualToPlaceHolder(const QString &sTable = QString(), bool bClauseWhere = false, const QString &sAppend = QString(), const QString &sSep = QString(" AND "), bool bCheckFKPartOfPK = false, qx::IxSqlQueryBuilder *pSqlQueryBuilder = NULL) const; + QString getSqlNameEqualToPlaceHolder(const QString &sAppend = QString(), const QString &sSep = QString(" AND "), bool bCheckFKPartOfPK = false, qx::IxSqlQueryBuilder *pSqlQueryBuilder = NULL) const; + QString getSqlTablePointNameAsAlias(const QString &sTable, const QString &sSep = QString(", "), const QString &sSuffixAlias = QString(), bool bCheckFKPartOfPK = false, const QString &sCustomAlias = QString(), qx::IxSqlQueryBuilder *pSqlQueryBuilder = NULL) const; + QString getSqlName(const QString &sSep = QString(", "), const QString &sOtherName = QString(), bool bCheckFKPartOfPK = false, qx::IxSqlQueryBuilder *pSqlQueryBuilder = NULL) const; + QString getSqlNameAndTypeAndParams(const QString &sSep = QString(", "), const QString &sOtherName = QString(), bool bCheckFKPartOfPK = false) const; + + void customGetSqlName(type_fct_sql_callback fct); + void customGetSqlTablePointNameAsAlias(type_fct_sql_callback fct); + void customGetSqlNameEqualToPlaceHolder(type_fct_sql_callback fct); + void customGetSqlAliasEqualToPlaceHolder(type_fct_sql_callback fct); + void customGetSqlAlias(type_fct_sql_callback fct); + + static QString getSqlFromTable(const QString &sTable, const QString &sCustomAlias = QString()); + static QString getSqlTableName(const QString &sTable); + static QString getSqlColumnName(const QString &sColumn); + static QString getSqlTableNameAlias(const QString &sTable); + static QString getSqlColumnNameAlias(const QString &sColumn); + + virtual bool isEqual(const void *pOwner1, const void *pOwner2) const = 0; + virtual QVariant toVariant(const void *pOwner, const QString &sFormat, int iIndexName = -1, qx::cvt::context::ctx_type ctx = qx::cvt::context::e_no_context) const = 0; + virtual qx_bool fromVariant(void *pOwner, const QVariant &v, const QString &sFormat, int iIndexName = -1, qx::cvt::context::ctx_type ctx = qx::cvt::context::e_no_context) = 0; + + QVariant toVariant(const void *pOwner, int iIndexName = -1, qx::cvt::context::ctx_type ctx = qx::cvt::context::e_no_context) const; + qx_bool fromVariant(void *pOwner, const QVariant &v, int iIndexName = -1, qx::cvt::context::ctx_type ctx = qx::cvt::context::e_no_context); + +#ifndef _QX_NO_JSON + virtual QJsonValue toJson(const void *pOwner, const QString &sFormat) const = 0; + virtual qx_bool fromJson(void *pOwner, const QJsonValue &j, const QString &sFormat) = 0; + + QJsonValue toJson(const void *pOwner) const; + qx_bool fromJson(void *pOwner, const QJsonValue &j); +#endif // _QX_NO_JSON + + protected: + virtual qx::any getDataPtr(const void *pOwner) const = 0; + virtual qx::any getDataPtr(void *pOwner) = 0; + virtual void *getDataVoidPtr(const void *pOwner) const = 0; + virtual void *getDataVoidPtr(void *pOwner) = 0; + + public: + qx::any getValueAnyPtr(const void *pOwner) const { return this->getDataPtr(pOwner); } + qx::any getValueAnyPtr(void *pOwner) { return this->getDataPtr(pOwner); } + void *getValueVoidPtr(const void *pOwner) const { return this->getDataVoidPtr(pOwner); } + void *getValueVoidPtr(void *pOwner) { return this->getDataVoidPtr(pOwner); } + + template + T *getValuePtr(void *pOwner, bool *bOk = NULL) + { + if (bOk) + { + (*bOk) = false; + } + if (!getAccessDataPointer()) + { + qDebug("[QxOrm] qx::IxDataMember::getValuePtr() : '%s'", "cannot access data-member pointer"); + return NULL; + } + qx::any a = this->getDataPtr(pOwner); + try + { + T *t = qx::any_cast(a); + if (bOk) + { + (*bOk) = (t != NULL); + }; + return t; + } + catch (const qx::bad_any_cast &err) + { + Q_UNUSED(err); + qDebug("[QxOrm] qx::IxDataMember::getValuePtr() : '%s'", "bad any cast exception"); + return NULL; + } + catch (...) + { + qDebug("[QxOrm] qx::IxDataMember::getValuePtr() : '%s'", "unknown cast exception"); + return NULL; + } + } + + template + T getValue(void *pOwner, bool *bOk = NULL) + { + if (!getAccessDataPointer()) + { + return qxCannotAccessDataPointer::getValue(this, pOwner, bOk); + } + T *t = this->getValuePtr(pOwner, bOk); + return (t ? (*t) : T()); + } + + template + bool setValue(void *pOwner, const T &val) + { + if (!getAccessDataPointer()) + { + return qxCannotAccessDataPointer::setValue(this, pOwner, val); + } + T *t = this->getValuePtr(pOwner); + if (t) + { + (*t) = val; + } + return (t != NULL); + } + + private: + template + struct qxCannotAccessDataPointer + { + static T getValue(IxDataMember *pData, void *pOwner, bool *bOk) + { + Q_UNUSED(pData); + Q_UNUSED(pOwner); + qDebug("[QxOrm] qx::IxDataMember::qxCannotAccessDataPointer::getValue() : '%s'", "type T not supported"); + if (bOk) + { + (*bOk) = false; + }; + return T(); + } + static bool setValue(IxDataMember *pData, void *pOwner, const T &val) + { + Q_UNUSED(pData); + Q_UNUSED(pOwner); + Q_UNUSED(val); + qDebug("[QxOrm] qx::IxDataMember::qxCannotAccessDataPointer::setValue() : '%s'", "type T not supported"); + return false; + } + }; + + template + struct qxCannotAccessDataPointer + { + static QVariant getValue(IxDataMember *pData, void *pOwner, bool *bOk) + { + if (bOk) + { + (*bOk) = (pData != NULL); + }; + return (pData ? pData->toVariant(pOwner, "") : QVariant()); + } + static bool setValue(IxDataMember *pData, void *pOwner, const QVariant &val) + { + return (pData ? pData->fromVariant(pOwner, val, "").getValue() : false); + } + }; + + template + struct qxCannotAccessDataPointer + { + static QString getValue(IxDataMember *pData, void *pOwner, bool *bOk) + { + if (bOk) + { + (*bOk) = (pData != NULL); + }; + return (pData ? pData->toVariant(pOwner, "").toString() : QString()); + } + static bool setValue(IxDataMember *pData, void *pOwner, const QString &val) + { + QVariant tmp(val); + return (pData ? pData->fromVariant(pOwner, tmp, "").getValue() : false); + } + }; + + public: +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#if _QX_SERIALIZE_POLYMORPHIC + QX_IX_DATA_MEMBER_PURE_VIRTUAL_ARCHIVE(boost::archive::polymorphic_iarchive, boost::archive::polymorphic_oarchive) +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY + QX_IX_DATA_MEMBER_PURE_VIRTUAL_ARCHIVE(boost::archive::binary_iarchive, boost::archive::binary_oarchive) +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT + QX_IX_DATA_MEMBER_PURE_VIRTUAL_ARCHIVE(boost::archive::text_iarchive, boost::archive::text_oarchive) +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML + QX_IX_DATA_MEMBER_PURE_VIRTUAL_ARCHIVE(boost::archive::xml_iarchive, boost::archive::xml_oarchive) +#endif // _QX_SERIALIZE_XML + +#if _QX_SERIALIZE_PORTABLE_BINARY + QX_IX_DATA_MEMBER_PURE_VIRTUAL_ARCHIVE(eos::portable_iarchive, eos::portable_oarchive) +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#if _QX_SERIALIZE_WIDE_BINARY + QX_IX_DATA_MEMBER_PURE_VIRTUAL_ARCHIVE(boost::archive::binary_wiarchive, boost::archive::binary_woarchive) +#endif // _QX_SERIALIZE_WIDE_BINARY + +#if _QX_SERIALIZE_WIDE_TEXT + QX_IX_DATA_MEMBER_PURE_VIRTUAL_ARCHIVE(boost::archive::text_wiarchive, boost::archive::text_woarchive) +#endif // _QX_SERIALIZE_WIDE_TEXT + +#if _QX_SERIALIZE_WIDE_XML + QX_IX_DATA_MEMBER_PURE_VIRTUAL_ARCHIVE(boost::archive::xml_wiarchive, boost::archive::xml_woarchive) +#endif // _QX_SERIALIZE_WIDE_XML + +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + private: +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + template + void serialize(Archive &ar, const unsigned int version); +#endif // _QX_ENABLE_BOOST_SERIALIZATION + }; + + typedef std::shared_ptr IxDataMember_ptr; + + /*! + * \ingroup QxDataMember + * \brief qx::IxDataMemberSqlCallbackParams : list of parameters used by custom callback functions to override SQL queries generated by QxOrm library + */ + struct IxDataMemberSqlCallbackParams + { + + const IxDataMember *pDataMember; //!< The data member instance which calls custom callback function + QString &sSQL; //!< Default value is the SQL generated by QxOrm library for this data member, can be changed by the custom callback function + QString sTable; //!< SQL table name (not always provided) + QString sSep; //!< SQL separator (not always provided) + QString sCustomAlias; //!< SQL custom alias (not always provided) + QString sSuffixAlias; //!< SQL suffix alias (not always provided) + QString sAppend; //!< String to append to SQL query (not always provided) + QString sOtherName; //!< SQL other name for this data member (not always provided) + bool bClauseWhere; //!< Define if we are building SQL in the clause WHERE or not (not always provided) + bool bCheckFKPartOfPK; //!< Check if foreign key is part of primary key (not always provided) + int iIndexName; //!< Index name for composite primary key (not always provided) + qx::dao::detail::IxDao_Helper *pDaoHelper; //!< DAO helper instance + qx::IxSqlQueryBuilder *pSqlQueryBuilder; //!< SQL query builder instance + + IxDataMemberSqlCallbackParams(const IxDataMember *p, QString &sql); + ~IxDataMemberSqlCallbackParams(); + }; + +} // namespace qx + +QX_DLL_EXPORT_INLINE_FCT bool operator<(const qx::IxDataMember &i1, const qx::IxDataMember &i2); +QX_DLL_EXPORT_INLINE_FCT bool operator>(const qx::IxDataMember &i1, const qx::IxDataMember &i2); + +#endif // _IX_DATA_MEMBER_H_ diff --git a/include/QxDataMember/IxDataMemberX.h b/include/QxDataMember/IxDataMemberX.h new file mode 100644 index 0000000..de548fd --- /dev/null +++ b/include/QxDataMember/IxDataMemberX.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_DATA_MEMBER_X_H_ +#define _IX_DATA_MEMBER_X_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxDataMemberX.h + * \author XDL Team + * \ingroup QxDataMember + * \brief Common interface for a list of IxDataMember class properties registered into QxOrm context (for example, list of data member of a class) + */ + +#include + +#include + +#include + +namespace qx +{ + + class IxClass; + + /*! + * \ingroup QxDataMember + * \brief qx::IxDataMemberX : common interface for a list of IxDataMember class properties registered into QxOrm context (for example, list of data member of a class) + */ + class QX_DLL_EXPORT IxDataMemberX + { + + private: + struct IxDataMemberXImpl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + + protected: + IxDataMemberX(); + virtual ~IxDataMemberX(); + + public: + IxClass *getClass() const; + void setClass(IxClass *p); + + QString getName() const; + const char *getNamePtr() const; + QString getDescription() const; + long getVersion() const; + qx::dao::strategy::inheritance getDaoStrategy() const; + + long count() const; + long size() const; + bool exist(const QString &sKey) const; + IxDataMember *get(long l) const; + IxDataMember *get(const QString &s) const; + IxDataMember *getId() const; + + virtual long count_WithDaoStrategy() const = 0; + virtual bool exist_WithDaoStrategy(const QString &sKey) const = 0; + virtual IxDataMember *get_WithDaoStrategy(long lIndex) const = 0; + virtual IxDataMember *get_WithDaoStrategy(const QString &sKey) const = 0; + virtual IxDataMember *getId_WithDaoStrategy() const = 0; + + protected: + void setId(IxDataMember *p); + QxCollection &getListDataMemberRef(); + const QxCollection &getListDataMemberRef() const; + QxCollection &getListPImplRef(); + const QxCollection &getListPImplRef() const; + }; + + typedef std::shared_ptr IxDataMemberX_ptr; + +} // namespace qx + +#endif // _IX_DATA_MEMBER_X_H_ diff --git a/include/QxDataMember/QxDataMember.h b/include/QxDataMember/QxDataMember.h new file mode 100644 index 0000000..636a24b --- /dev/null +++ b/include/QxDataMember/QxDataMember.h @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_DATA_MEMBER_H_ +#define _QX_DATA_MEMBER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxDataMember.h + * \author XDL Team + * \ingroup QxDataMember + * \brief Concrete class property registered into QxOrm context + */ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#include +#include +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#include +#include + +#define QX_DATA_MEMBER_IMPL_VIRTUAL_ARCHIVE(ArchiveInput, ArchiveOutput) \ + virtual void toArchive(const void *pOwner, ArchiveOutput &ar) const { QxDataMember::toArchive(ar, getNamePtr(), getData(pOwner)); } \ + virtual void fromArchive(void *pOwner, ArchiveInput &ar) { QxDataMember::fromArchive(ar, getNamePtr(), getData(pOwner)); } + +namespace qx +{ + + /*! + * \ingroup QxDataMember + * \brief qx::QxDataMember : concrete property of type DataType registered into QxOrm context for the class Owner + */ + template + class QxDataMember : public IxDataMember + { + + protected: + typedef DataType Owner::*type_data_member_ptr; + + type_data_member_ptr m_pData; //!< Data member under format "& Owner::DataMember" + IxDataMember *m_pImpl_; //!< If not NULL then this data member is owned by a private implementation idiom instance + + public: + QxDataMember(type_data_member_ptr pData, const QString &sKey, long lVersion, bool bSerialize, bool bDao, IxDataMember *pImpl = NULL) : IxDataMember(sKey, lVersion, bSerialize, bDao, pImpl), m_pData(pData), m_pImpl_(pImpl) { this->setAccessDataPointer(true); } + virtual ~QxDataMember() { ; } + + inline DataType *getData(void *pOwner) const + { + void *pOwner_ = (m_pImpl_ ? m_pImpl_->getDataVoidPtr(pOwner) : pOwner); + return (pOwner_ ? (&((static_cast(pOwner_))->*m_pData)) : NULL); + } + inline const DataType *getData(const void *pOwner) const + { + const void *pOwner_ = (m_pImpl_ ? m_pImpl_->getDataVoidPtr(pOwner) : pOwner); + return (pOwner_ ? (&((static_cast(pOwner_))->*m_pData)) : NULL); + } + + virtual QVariant toVariant(const void *pOwner, const QString &sFormat, int iIndexName = -1, qx::cvt::context::ctx_type ctx = qx::cvt::context::e_no_context) const { return qx::cvt::to_variant((*getData(pOwner)), sFormat, iIndexName, ctx); } + virtual qx_bool fromVariant(void *pOwner, const QVariant &v, const QString &sFormat, int iIndexName = -1, qx::cvt::context::ctx_type ctx = qx::cvt::context::e_no_context) { return qx::cvt::from_variant(v, (*getData(pOwner)), sFormat, iIndexName, ctx); } + virtual QString getType() const + { + QMutexLocker locker(&IxDataMember::m_mutex); + return QString(qx::trait::get_class_name::get()); + } + +#ifndef _QX_NO_JSON + virtual QJsonValue toJson(const void *pOwner, const QString &sFormat) const { return qx::cvt::to_json((*getData(pOwner)), sFormat); } + virtual qx_bool fromJson(void *pOwner, const QJsonValue &j, const QString &sFormat) { return qx::cvt::from_json(j, (*getData(pOwner)), sFormat); } +#endif // _QX_NO_JSON + + virtual bool isEqual(const void *pOwner1, const void *pOwner2) const + { + if ((pOwner1 == NULL) || (pOwner2 == NULL)) + { + return false; + } + if (pOwner1 == pOwner2) + { + return true; + } + return qxCompareDataMember::value, 0>::isEqual((*this), pOwner1, pOwner2); + } + + protected: + virtual qx::any getDataPtr(const void *pOwner) const { return qx::any(getData(pOwner)); } + virtual qx::any getDataPtr(void *pOwner) { return qx::any(getData(pOwner)); } + virtual void *getDataVoidPtr(const void *pOwner) const { return static_cast(const_cast(getData(pOwner))); } + virtual void *getDataVoidPtr(void *pOwner) { return static_cast(getData(pOwner)); } + + public: +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#if _QX_SERIALIZE_POLYMORPHIC + QX_DATA_MEMBER_IMPL_VIRTUAL_ARCHIVE(boost::archive::polymorphic_iarchive, boost::archive::polymorphic_oarchive) +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY + QX_DATA_MEMBER_IMPL_VIRTUAL_ARCHIVE(boost::archive::binary_iarchive, boost::archive::binary_oarchive) +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT + QX_DATA_MEMBER_IMPL_VIRTUAL_ARCHIVE(boost::archive::text_iarchive, boost::archive::text_oarchive) +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML + QX_DATA_MEMBER_IMPL_VIRTUAL_ARCHIVE(boost::archive::xml_iarchive, boost::archive::xml_oarchive) +#endif // _QX_SERIALIZE_XML + +#if _QX_SERIALIZE_PORTABLE_BINARY + QX_DATA_MEMBER_IMPL_VIRTUAL_ARCHIVE(eos::portable_iarchive, eos::portable_oarchive) +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#if _QX_SERIALIZE_WIDE_BINARY + QX_DATA_MEMBER_IMPL_VIRTUAL_ARCHIVE(boost::archive::binary_wiarchive, boost::archive::binary_woarchive) +#endif // _QX_SERIALIZE_WIDE_BINARY + +#if _QX_SERIALIZE_WIDE_TEXT + QX_DATA_MEMBER_IMPL_VIRTUAL_ARCHIVE(boost::archive::text_wiarchive, boost::archive::text_woarchive) +#endif // _QX_SERIALIZE_WIDE_TEXT + +#if _QX_SERIALIZE_WIDE_XML + QX_DATA_MEMBER_IMPL_VIRTUAL_ARCHIVE(boost::archive::xml_wiarchive, boost::archive::xml_woarchive) +#endif // _QX_SERIALIZE_WIDE_XML + +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + private: +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + + template + static inline void toArchive(Archive &ar, const char *sName, const DataType *pData) + { + ar << boost::serialization::make_nvp(sName, (*pData)); + } + + template + static inline void fromArchive(Archive &ar, const char *sName, DataType *pData) + { + ar >> boost::serialization::make_nvp(sName, (*pData)); + } + +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + private: + template + struct qxCompareDataMember + { + static inline bool isEqual(const QxDataMember &dataMember, const void *pOwner1, const void *pOwner2) + { + return (dataMember.toVariant(pOwner1, "") == dataMember.toVariant(pOwner2, "")); + } + }; + + template + struct qxCompareDataMember + { + static inline bool isEqual(const QxDataMember &dataMember, const void *pOwner1, const void *pOwner2) + { + return ((*dataMember.getData(pOwner1)) == (*dataMember.getData(pOwner2))); + } + }; + }; + +} // namespace qx + +#include "../../inl/QxDataMember/QxDataMember.inl" + +#endif // _QX_DATA_MEMBER_H_ diff --git a/include/QxDataMember/QxDataMemberX.h b/include/QxDataMember/QxDataMemberX.h new file mode 100644 index 0000000..6774ccf --- /dev/null +++ b/include/QxDataMember/QxDataMemberX.h @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_DATA_MEMBER_X_H_ +#define _QX_DATA_MEMBER_X_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxDataMemberX.h + * \author XDL Team + * \ingroup QxDataMember + * \brief Concrete list of class properties registered into QxOrm context + */ + +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include + +namespace qx +{ + + /*! + * \ingroup QxDataMember + * \brief qx::QxDataMemberX : concrete list of properties registered into QxOrm context for the class T + */ + template + class QxDataMemberX : public IxDataMemberX, public QxSingleton> + { + + friend class QxSingleton>; + + public: + typedef typename qx::trait::get_primary_key::type type_primary_key; + typedef typename qx::trait::get_base_class::type type_base_class; + + protected: + QxDataMemberX() : IxDataMemberX(), QxSingleton>(QString("qx::QxDataMemberX_") + qx::trait::get_class_name::get_xml_tag()) { ; } + virtual ~QxDataMemberX() { ; } + + public: + virtual long count_WithDaoStrategy() const { return count_WithDaoStrategy_Helper(); } + virtual bool exist_WithDaoStrategy(const QString &sKey) const { return exist_WithDaoStrategy_Helper(sKey); } + virtual IxDataMember *get_WithDaoStrategy(long lIndex) const { return get_WithDaoStrategy_Helper(lIndex); } + virtual IxDataMember *get_WithDaoStrategy(const QString &sKey) const { return get_WithDaoStrategy_Helper(sKey); } + virtual IxDataMember *getId_WithDaoStrategy() const { return getId_WithDaoStrategy_Helper(); } + + IxDataMember *id(type_primary_key T::*pDataMemberId, const QString &sKey, long lVersion = 0); + IxDataMember *id(const QString &sKey, long lVersion); + IxDataMember *add(const QString &sKey, long lVersion); + + template + IxDataMember *add(V U::*pData, const QString &sKey, long lVersion = 0, bool bSerialize = true, bool bDao = true); + template + IxSqlRelation *relationOneToOne(V U::*pData, const QString &sKey, long lVersion = 0); + template + IxSqlRelation *relationManyToOne(V U::*pData, const QString &sKey, long lVersion = 0); + template + IxSqlRelation *relationOneToMany(V U::*pData, const QString &sKey, const QString &sForeignKey, long lVersion = 0); + template + IxSqlRelation *relationManyToMany(V U::*pData, const QString &sKey, const QString &sExtraTable, const QString &sForeignKeyOwner, const QString &sForeignKeyDataType, long lVersion = 0); + + template + IxDataMember *pimpl(V U::*pData, const QString &sKey); + template + IxDataMember *id(type_primary_key U::*pDataMemberId, const QString &sKey, long lVersion, IxDataMember *pImpl); + template + IxDataMember *add(V U::*pData, const QString &sKey, long lVersion, bool bSerialize, bool bDao, IxDataMember *pImpl); + template + IxSqlRelation *relationOneToOne(V U::*pData, const QString &sKey, long lVersion, IxDataMember *pImpl); + template + IxSqlRelation *relationManyToOne(V U::*pData, const QString &sKey, long lVersion, IxDataMember *pImpl); + template + IxSqlRelation *relationOneToMany(V U::*pData, const QString &sKey, const QString &sForeignKey, long lVersion, IxDataMember *pImpl); + template + IxSqlRelation *relationManyToMany(V U::*pData, const QString &sKey, const QString &sExtraTable, const QString &sForeignKeyOwner, const QString &sForeignKeyDataType, long lVersion, IxDataMember *pImpl); + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + template + inline void toArchive(const T *pOwner, Archive &ar, const unsigned int file_version) const; + template + inline void fromArchive(T *pOwner, Archive &ar, const unsigned int file_version); +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + private: + IxDataMember *initId(IxDataMember *pId, long lVersion); + IxDataMember *initData(IxDataMember *pData, long lVersion); + IxDataMember *initPImpl(IxDataMember *pImpl); + + inline IxDataMemberX *getBaseClass_Helper() const { return QxDataMemberX::getSingleton(); } + + long count_WithDaoStrategy_Helper() const + { + if (getDaoStrategy() == qx::dao::strategy::single_table_inheritance) + { + return ((getBaseClass_Helper()->getDaoStrategy() != getDaoStrategy()) ? count() : getBaseClass_Helper()->count_WithDaoStrategy()); + } + else if (getDaoStrategy() == qx::dao::strategy::class_table_inheritance) + { + return (count() + ((!getId() && getId_WithDaoStrategy()) ? 1 : 0)); + } + else if (getDaoStrategy() == qx::dao::strategy::concrete_table_inheritance) + { + return (count() + getBaseClass_Helper()->count_WithDaoStrategy()); + } + qAssert(false); + return 0; + } + + bool exist_WithDaoStrategy_Helper(const QString &sKey) const + { + if (getDaoStrategy() == qx::dao::strategy::single_table_inheritance) + { + return ((getBaseClass_Helper()->getDaoStrategy() != getDaoStrategy()) ? exist(sKey) : getBaseClass_Helper()->exist_WithDaoStrategy(sKey)); + } + else if (getDaoStrategy() == qx::dao::strategy::class_table_inheritance) + { + return (exist(sKey) || (getId_WithDaoStrategy() ? (getId_WithDaoStrategy()->getKey() == sKey) : false)); + } + else if (getDaoStrategy() == qx::dao::strategy::concrete_table_inheritance) + { + return (exist(sKey) || getBaseClass_Helper()->exist_WithDaoStrategy(sKey)); + } + qAssert(false); + return false; + } + + IxDataMember *get_WithDaoStrategy_Helper(long lIndex) const + { + if (getDaoStrategy() == qx::dao::strategy::single_table_inheritance) + { + return ((getBaseClass_Helper()->getDaoStrategy() != getDaoStrategy()) ? get(lIndex) : getBaseClass_Helper()->get_WithDaoStrategy(lIndex)); + } + else if (getDaoStrategy() == qx::dao::strategy::class_table_inheritance) + { + return ((!getId() && (lIndex == count())) ? getId_WithDaoStrategy() : get(lIndex)); + } + else if (getDaoStrategy() == qx::dao::strategy::concrete_table_inheritance) + { + return (((lIndex >= 0) && (lIndex < count())) ? get(lIndex) : getBaseClass_Helper()->get_WithDaoStrategy(lIndex - count())); + } + qAssert(false); + return NULL; + } + + IxDataMember *get_WithDaoStrategy_Helper(const QString &sKey) const + { + if (getDaoStrategy() == qx::dao::strategy::single_table_inheritance) + { + return ((getBaseClass_Helper()->getDaoStrategy() != getDaoStrategy()) ? get(sKey) : getBaseClass_Helper()->get_WithDaoStrategy(sKey)); + } + else if (getDaoStrategy() == qx::dao::strategy::class_table_inheritance) + { + return ((getId_WithDaoStrategy() && (getId_WithDaoStrategy()->getKey() == sKey)) ? getId_WithDaoStrategy() : get(sKey)); + } + else if (getDaoStrategy() == qx::dao::strategy::concrete_table_inheritance) + { + return (exist(sKey) ? get(sKey) : getBaseClass_Helper()->get_WithDaoStrategy(sKey)); + } + qAssert(false); + return NULL; + } + + IxDataMember *getId_WithDaoStrategy_Helper() const + { + if (getDaoStrategy() == qx::dao::strategy::single_table_inheritance) + { + return ((getBaseClass_Helper()->getDaoStrategy() != getDaoStrategy()) ? getId() : getBaseClass_Helper()->getId_WithDaoStrategy()); + } + else if (getDaoStrategy() == qx::dao::strategy::class_table_inheritance) + { + return (getId() ? getId() : getBaseClass_Helper()->getId_WithDaoStrategy()); + } + else if (getDaoStrategy() == qx::dao::strategy::concrete_table_inheritance) + { + return (getId() ? getId() : getBaseClass_Helper()->getId_WithDaoStrategy()); + } + qAssert(false); + return NULL; + } + }; + +} // namespace qx + +#include "../../inl/QxDataMember/QxDataMemberX.inl" + +#endif // _QX_DATA_MEMBER_X_H_ diff --git a/include/QxDataMember/QxDataMember_PImpl.h b/include/QxDataMember/QxDataMember_PImpl.h new file mode 100644 index 0000000..15f0c90 --- /dev/null +++ b/include/QxDataMember/QxDataMember_PImpl.h @@ -0,0 +1,376 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_DATA_MEMBER_PIMPL_H_ +#define _QX_DATA_MEMBER_PIMPL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxDataMember_PImpl.h + * \author XDL Team + * \ingroup QxDataMember + * \brief Concrete class property registered into QxOrm context (using private implementation idiom) + */ + +#include + +#define QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(ArchiveInput, ArchiveOutput) \ + virtual void toArchive(const void *pOwner, ArchiveOutput &ar) const Q_DECL_OVERRIDE \ + { \ + Q_UNUSED(pOwner); \ + Q_UNUSED(ar); \ + } \ + virtual void fromArchive(void *pOwner, ArchiveInput &ar) Q_DECL_OVERRIDE \ + { \ + Q_UNUSED(pOwner); \ + Q_UNUSED(ar); \ + } + +namespace qx +{ + + /*! + * \ingroup QxDataMember + * \brief qx::QxDataMember_PImpl : concrete property of type DataType registered into QxOrm context for the class Owner (using private implementation idiom) + */ + template + class QxDataMember_PImpl : public IxDataMember + { + + protected: + typedef DataType Owner::*type_data_member_ptr; + + type_data_member_ptr m_pData; //!< Data member under format "& Owner::DataMember" + + public: + QxDataMember_PImpl(type_data_member_ptr pData, const QString &sKey) : IxDataMember(sKey, 0, false, false, NULL), m_pData(pData) + { + static_assert(std::is_pointer::value, "std::is_pointer::value"); + this->setAccessDataPointer(true); + } + virtual ~QxDataMember_PImpl() { ; } + + virtual bool isEqual(const void *pOwner1, const void *pOwner2) const Q_DECL_OVERRIDE + { + Q_UNUSED(pOwner1); + Q_UNUSED(pOwner2); + return false; + } + virtual QVariant toVariant(const void *pOwner, const QString &sFormat, int iIndexName = -1, qx::cvt::context::ctx_type ctx = qx::cvt::context::e_no_context) const Q_DECL_OVERRIDE + { + Q_UNUSED(pOwner); + Q_UNUSED(sFormat); + Q_UNUSED(iIndexName); + Q_UNUSED(ctx); + return QVariant(); + } + virtual qx_bool fromVariant(void *pOwner, const QVariant &v, const QString &sFormat, int iIndexName = -1, qx::cvt::context::ctx_type ctx = qx::cvt::context::e_no_context) Q_DECL_OVERRIDE + { + Q_UNUSED(pOwner); + Q_UNUSED(v); + Q_UNUSED(sFormat); + Q_UNUSED(iIndexName); + Q_UNUSED(ctx); + return qx_bool(true); + } + virtual QString getType() const Q_DECL_OVERRIDE { return QString(); } + +#ifndef _QX_NO_JSON + virtual QJsonValue toJson(const void *pOwner, const QString &sFormat) const Q_DECL_OVERRIDE + { + Q_UNUSED(pOwner); + Q_UNUSED(sFormat); + return QJsonValue(); + } + virtual qx_bool fromJson(void *pOwner, const QJsonValue &j, const QString &sFormat) Q_DECL_OVERRIDE + { + Q_UNUSED(pOwner); + Q_UNUSED(j); + Q_UNUSED(sFormat); + return qx_bool(true); + } +#endif // _QX_NO_JSON + + protected: + inline DataType *getData(void *pOwner) const { return (&((static_cast(pOwner))->*m_pData)); } + inline const DataType *getData(const void *pOwner) const { return (&((static_cast(pOwner))->*m_pData)); } + + virtual qx::any getDataPtr(const void *pOwner) const Q_DECL_FINAL { return (pOwner ? qx::any(*getData(pOwner)) : qx::any()); } + virtual qx::any getDataPtr(void *pOwner) Q_DECL_FINAL { return (pOwner ? qx::any(*getData(pOwner)) : qx::any()); } + virtual void *getDataVoidPtr(const void *pOwner) const Q_DECL_FINAL { return (pOwner ? static_cast(const_cast(*getData(pOwner))) : NULL); } + virtual void *getDataVoidPtr(void *pOwner) Q_DECL_FINAL { return (pOwner ? static_cast(*getData(pOwner)) : NULL); } + + public: +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#if _QX_SERIALIZE_POLYMORPHIC + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::polymorphic_iarchive, boost::archive::polymorphic_oarchive) +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::binary_iarchive, boost::archive::binary_oarchive) +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::text_iarchive, boost::archive::text_oarchive) +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::xml_iarchive, boost::archive::xml_oarchive) +#endif // _QX_SERIALIZE_XML + +#if _QX_SERIALIZE_PORTABLE_BINARY + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(eos::portable_iarchive, eos::portable_oarchive) +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#if _QX_SERIALIZE_WIDE_BINARY + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::binary_wiarchive, boost::archive::binary_woarchive) +#endif // _QX_SERIALIZE_WIDE_BINARY + +#if _QX_SERIALIZE_WIDE_TEXT + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::text_wiarchive, boost::archive::text_woarchive) +#endif // _QX_SERIALIZE_WIDE_TEXT + +#if _QX_SERIALIZE_WIDE_XML + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::xml_wiarchive, boost::archive::xml_woarchive) +#endif // _QX_SERIALIZE_WIDE_XML + +#endif // _QX_ENABLE_BOOST_SERIALIZATION + }; + + template + class QxDataMember_PImpl, Owner> : public IxDataMember + { + + protected: + typedef std::unique_ptr Owner::*type_data_member_ptr; + + type_data_member_ptr m_pData; //!< Data member under format "& Owner::DataMember" + + public: + QxDataMember_PImpl(type_data_member_ptr pData, const QString &sKey) : IxDataMember(sKey, 0, false, false, NULL), m_pData(pData) { this->setAccessDataPointer(true); } + virtual ~QxDataMember_PImpl() { ; } + + virtual bool isEqual(const void *pOwner1, const void *pOwner2) const Q_DECL_OVERRIDE + { + Q_UNUSED(pOwner1); + Q_UNUSED(pOwner2); + return false; + } + virtual QVariant toVariant(const void *pOwner, const QString &sFormat, int iIndexName = -1, qx::cvt::context::ctx_type ctx = qx::cvt::context::e_no_context) const Q_DECL_OVERRIDE + { + Q_UNUSED(pOwner); + Q_UNUSED(sFormat); + Q_UNUSED(iIndexName); + Q_UNUSED(ctx); + return QVariant(); + } + virtual qx_bool fromVariant(void *pOwner, const QVariant &v, const QString &sFormat, int iIndexName = -1, qx::cvt::context::ctx_type ctx = qx::cvt::context::e_no_context) Q_DECL_OVERRIDE + { + Q_UNUSED(pOwner); + Q_UNUSED(v); + Q_UNUSED(sFormat); + Q_UNUSED(iIndexName); + Q_UNUSED(ctx); + return qx_bool(true); + } + virtual QString getType() const Q_DECL_OVERRIDE { return QString(); } + +#ifndef _QX_NO_JSON + virtual QJsonValue toJson(const void *pOwner, const QString &sFormat) const Q_DECL_OVERRIDE + { + Q_UNUSED(pOwner); + Q_UNUSED(sFormat); + return QJsonValue(); + } + virtual qx_bool fromJson(void *pOwner, const QJsonValue &j, const QString &sFormat) Q_DECL_OVERRIDE + { + Q_UNUSED(pOwner); + Q_UNUSED(j); + Q_UNUSED(sFormat); + return qx_bool(true); + } +#endif // _QX_NO_JSON + + protected: + inline std::unique_ptr *getData(void *pOwner) const { return (&((static_cast(pOwner))->*m_pData)); } + inline const std::unique_ptr *getData(const void *pOwner) const { return (&((static_cast(pOwner))->*m_pData)); } + + virtual qx::any getDataPtr(const void *pOwner) const Q_DECL_FINAL { return (pOwner ? qx::any(getData(pOwner)->get()) : qx::any()); } + virtual qx::any getDataPtr(void *pOwner) Q_DECL_FINAL { return (pOwner ? qx::any(getData(pOwner)->get()) : qx::any()); } + virtual void *getDataVoidPtr(const void *pOwner) const Q_DECL_FINAL { return (pOwner ? static_cast(const_cast(getData(pOwner)->get())) : NULL); } + virtual void *getDataVoidPtr(void *pOwner) Q_DECL_FINAL { return (pOwner ? static_cast(getData(pOwner)->get()) : NULL); } + + public: +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#if _QX_SERIALIZE_POLYMORPHIC + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::polymorphic_iarchive, boost::archive::polymorphic_oarchive) +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::binary_iarchive, boost::archive::binary_oarchive) +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::text_iarchive, boost::archive::text_oarchive) +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::xml_iarchive, boost::archive::xml_oarchive) +#endif // _QX_SERIALIZE_XML + +#if _QX_SERIALIZE_PORTABLE_BINARY + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(eos::portable_iarchive, eos::portable_oarchive) +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#if _QX_SERIALIZE_WIDE_BINARY + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::binary_wiarchive, boost::archive::binary_woarchive) +#endif // _QX_SERIALIZE_WIDE_BINARY + +#if _QX_SERIALIZE_WIDE_TEXT + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::text_wiarchive, boost::archive::text_woarchive) +#endif // _QX_SERIALIZE_WIDE_TEXT + +#if _QX_SERIALIZE_WIDE_XML + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::xml_wiarchive, boost::archive::xml_woarchive) +#endif // _QX_SERIALIZE_WIDE_XML + +#endif // _QX_ENABLE_BOOST_SERIALIZATION + }; + + template + class QxDataMember_PImpl, Owner> : public IxDataMember + { + + protected: + typedef std::shared_ptr Owner::*type_data_member_ptr; + + type_data_member_ptr m_pData; //!< Data member under format "& Owner::DataMember" + + public: + QxDataMember_PImpl(type_data_member_ptr pData, const QString &sKey) : IxDataMember(sKey, 0, false, false, NULL), m_pData(pData) { this->setAccessDataPointer(true); } + virtual ~QxDataMember_PImpl() { ; } + + virtual bool isEqual(const void *pOwner1, const void *pOwner2) const Q_DECL_OVERRIDE + { + Q_UNUSED(pOwner1); + Q_UNUSED(pOwner2); + return false; + } + virtual QVariant toVariant(const void *pOwner, const QString &sFormat, int iIndexName = -1, qx::cvt::context::ctx_type ctx = qx::cvt::context::e_no_context) const Q_DECL_OVERRIDE + { + Q_UNUSED(pOwner); + Q_UNUSED(sFormat); + Q_UNUSED(iIndexName); + Q_UNUSED(ctx); + return QVariant(); + } + virtual qx_bool fromVariant(void *pOwner, const QVariant &v, const QString &sFormat, int iIndexName = -1, qx::cvt::context::ctx_type ctx = qx::cvt::context::e_no_context) Q_DECL_OVERRIDE + { + Q_UNUSED(pOwner); + Q_UNUSED(v); + Q_UNUSED(sFormat); + Q_UNUSED(iIndexName); + Q_UNUSED(ctx); + return qx_bool(true); + } + virtual QString getType() const Q_DECL_OVERRIDE { return QString(); } + +#ifndef _QX_NO_JSON + virtual QJsonValue toJson(const void *pOwner, const QString &sFormat) const Q_DECL_OVERRIDE + { + Q_UNUSED(pOwner); + Q_UNUSED(sFormat); + return QJsonValue(); + } + virtual qx_bool fromJson(void *pOwner, const QJsonValue &j, const QString &sFormat) Q_DECL_OVERRIDE + { + Q_UNUSED(pOwner); + Q_UNUSED(j); + Q_UNUSED(sFormat); + return qx_bool(true); + } +#endif // _QX_NO_JSON + + protected: + inline std::shared_ptr *getData(void *pOwner) const { return (&((static_cast(pOwner))->*m_pData)); } + inline const std::shared_ptr *getData(const void *pOwner) const { return (&((static_cast(pOwner))->*m_pData)); } + + virtual qx::any getDataPtr(const void *pOwner) const Q_DECL_FINAL { return (pOwner ? qx::any(getData(pOwner)->get()) : qx::any()); } + virtual qx::any getDataPtr(void *pOwner) Q_DECL_FINAL { return (pOwner ? qx::any(getData(pOwner)->get()) : qx::any()); } + virtual void *getDataVoidPtr(const void *pOwner) const Q_DECL_FINAL { return (pOwner ? static_cast(const_cast(getData(pOwner)->get())) : NULL); } + virtual void *getDataVoidPtr(void *pOwner) Q_DECL_FINAL { return (pOwner ? static_cast(getData(pOwner)->get()) : NULL); } + + public: +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#if _QX_SERIALIZE_POLYMORPHIC + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::polymorphic_iarchive, boost::archive::polymorphic_oarchive) +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::binary_iarchive, boost::archive::binary_oarchive) +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::text_iarchive, boost::archive::text_oarchive) +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::xml_iarchive, boost::archive::xml_oarchive) +#endif // _QX_SERIALIZE_XML + +#if _QX_SERIALIZE_PORTABLE_BINARY + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(eos::portable_iarchive, eos::portable_oarchive) +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#if _QX_SERIALIZE_WIDE_BINARY + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::binary_wiarchive, boost::archive::binary_woarchive) +#endif // _QX_SERIALIZE_WIDE_BINARY + +#if _QX_SERIALIZE_WIDE_TEXT + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::text_wiarchive, boost::archive::text_woarchive) +#endif // _QX_SERIALIZE_WIDE_TEXT + +#if _QX_SERIALIZE_WIDE_XML + QX_DATA_MEMBER_PIMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::xml_wiarchive, boost::archive::xml_woarchive) +#endif // _QX_SERIALIZE_WIDE_XML + +#endif // _QX_ENABLE_BOOST_SERIALIZATION + }; + +} // namespace qx + +#endif // _QX_DATA_MEMBER_PIMPL_H_ diff --git a/include/QxDataMember/QxDataMember_QObject.h b/include/QxDataMember/QxDataMember_QObject.h new file mode 100644 index 0000000..e6137e1 --- /dev/null +++ b/include/QxDataMember/QxDataMember_QObject.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_DATA_MEMBER_QOBJECT_H_ +#define _QX_DATA_MEMBER_QOBJECT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxDataMember_QObject.h + * \author XDL Team + * \ingroup QxDataMember + * \brief Connect Qt introspection engine (based on QObject class, with QMetaObject type) to QxOrm library introspection engine + */ + +#include +#include + +#include + +#include +#include + +#define QX_DATA_MEMBER_QOBJECT_IMPL_VIRTUAL_ARCHIVE_HPP(ArchiveInput, ArchiveOutput) \ + virtual void toArchive(const void *pOwner, ArchiveOutput &ar) const; \ + virtual void fromArchive(void *pOwner, ArchiveInput &ar); + +namespace qx +{ + + /*! + * \ingroup QxDataMember + * \brief qx::QxDataMember_QObject : connect Qt introspection engine (based on QObject class, with QMetaObject type) to QxOrm library introspection engine + */ + class QX_DLL_EXPORT QxDataMember_QObject : public IxDataMember + { + + protected: + const QMetaObject *m_metaObject; //!< Meta-object from introspection engine of Qt library (& MyQObject::staticMetaObject) + QMetaProperty m_metaProperty; //!< Meta-property from introspection engine of Qt library + + public: + QxDataMember_QObject(const QMetaObject *pMetaObject, const QString &sKey); + virtual ~QxDataMember_QObject() { ; } + + virtual bool isEqual(const void *pOwner1, const void *pOwner2) const; + virtual QVariant toVariant(const void *pOwner, const QString &sFormat, int iIndexName = -1, qx::cvt::context::ctx_type ctx = qx::cvt::context::e_no_context) const; + virtual qx_bool fromVariant(void *pOwner, const QVariant &v, const QString &sFormat, int iIndexName = -1, qx::cvt::context::ctx_type ctx = qx::cvt::context::e_no_context); + virtual QString getType() const; + +#ifndef _QX_NO_JSON + virtual QJsonValue toJson(const void *pOwner, const QString &sFormat) const; + virtual qx_bool fromJson(void *pOwner, const QJsonValue &j, const QString &sFormat); +#endif // _QX_NO_JSON + + public: +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#if _QX_SERIALIZE_POLYMORPHIC + QX_DATA_MEMBER_QOBJECT_IMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::polymorphic_iarchive, boost::archive::polymorphic_oarchive) +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY + QX_DATA_MEMBER_QOBJECT_IMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::binary_iarchive, boost::archive::binary_oarchive) +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT + QX_DATA_MEMBER_QOBJECT_IMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::text_iarchive, boost::archive::text_oarchive) +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML + QX_DATA_MEMBER_QOBJECT_IMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::xml_iarchive, boost::archive::xml_oarchive) +#endif // _QX_SERIALIZE_XML + +#if _QX_SERIALIZE_PORTABLE_BINARY + QX_DATA_MEMBER_QOBJECT_IMPL_VIRTUAL_ARCHIVE_HPP(eos::portable_iarchive, eos::portable_oarchive) +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#if _QX_SERIALIZE_WIDE_BINARY + QX_DATA_MEMBER_QOBJECT_IMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::binary_wiarchive, boost::archive::binary_woarchive) +#endif // _QX_SERIALIZE_WIDE_BINARY + +#if _QX_SERIALIZE_WIDE_TEXT + QX_DATA_MEMBER_QOBJECT_IMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::text_wiarchive, boost::archive::text_woarchive) +#endif // _QX_SERIALIZE_WIDE_TEXT + +#if _QX_SERIALIZE_WIDE_XML + QX_DATA_MEMBER_QOBJECT_IMPL_VIRTUAL_ARCHIVE_HPP(boost::archive::xml_wiarchive, boost::archive::xml_woarchive) +#endif // _QX_SERIALIZE_WIDE_XML + +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + protected: + virtual qx::any getDataPtr(const void *pOwner) const; + virtual qx::any getDataPtr(void *pOwner); + virtual void *getDataVoidPtr(const void *pOwner) const; + virtual void *getDataVoidPtr(void *pOwner); + }; + +} // namespace qx + +#endif // _QX_DATA_MEMBER_QOBJECT_H_ diff --git a/include/QxExtras/QxBoostOptionalOnly.h b/include/QxExtras/QxBoostOptionalOnly.h new file mode 100644 index 0000000..b23f84b --- /dev/null +++ b/include/QxExtras/QxBoostOptionalOnly.h @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_ENABLE_BOOST +#ifndef _QX_BOOST_OPTIONAL_ONLY_H_ +#define _QX_BOOST_OPTIONAL_ONLY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxBoostOptionalOnly.h + * \author XDL Team + * \ingroup QxExtras + * \brief Provides support for boost::optional to manage NULL values even if _QX_ENABLE_BOOST compilation option is not defined : so this header file enables just boost::optional feature and should be included just after header file (ideally in a precompiled header) + */ + +#include +#include + +#include + +#define _QX_ENABLE_BOOST +#include +#undef _QX_ENABLE_BOOST + +#include + +QX_REGISTER_CLASS_NAME_TEMPLATE_1(boost::optional) + +namespace qx +{ + namespace trait + { + + template + struct get_sql_type> + { + static inline const char *get() { return qx::trait::get_sql_type::get(); } + }; + + } // namespace trait +} // namespace qx + +namespace qx +{ + namespace cvt + { + namespace detail + { + +#ifndef _QX_NO_JSON + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::optional &t, const QString &format) + { + if (t) + { + return qx::cvt::to_json((*t), format); + }; + return QJsonValue(); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::optional &t, const QString &format) + { + if (j.isNull()) + { + t = boost::none; + return qx_bool(true); + } + else if (!t) + { + t = T(); + } + return qx::cvt::from_json(j, (*t), format); + } + }; + +#endif // _QX_NO_JSON + + template + struct QxConvert_ToString> + { + static inline QString toString(const boost::optional &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (t) + { + return qx::cvt::to_string((*t), format, index, ctx); + }; + return QString(); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, boost::optional &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (!t) + { + t = T(); + }; + return qx::cvt::from_string(s, (*t), format, index, ctx); + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const boost::optional &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (t) + { + return qx::cvt::to_variant((*t), format, index, ctx); + }; + return qx::trait::construct_null_qvariant::get(); + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, boost::optional &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (v.isNull()) + { + t = boost::none; + return qx_bool(true); + } + else if (!t) + { + t = T(); + } + return qx::cvt::from_variant(v, (*t), format, index, ctx); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_BOOST_OPTIONAL_ONLY_H_ +#endif // _QX_ENABLE_BOOST diff --git a/include/QxExtras/QxStdOptional.h b/include/QxExtras/QxStdOptional.h new file mode 100644 index 0000000..a3a9a52 --- /dev/null +++ b/include/QxExtras/QxStdOptional.h @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_STD_OPTIONAL_H_ +#define _QX_STD_OPTIONAL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxStdOptional.h + * \author XDL Team + * \ingroup QxExtras + * \brief Support std::optional class (requires a C++17 compiler) to manage NULL database value, this header should be included just after header file (ideally in a precompiled header) + */ + +#include + +#include + +#include + +#include + +template +QDataStream &operator<<(QDataStream &stream, const std::optional &t) +{ + qint8 iHasData = (t ? 1 : 0); + stream << iHasData; + if (t) + { + stream << (*t); + } + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::optional &t) +{ + qint8 iHasData = 0; + stream >> iHasData; + if (iHasData) + { + t = T(); + stream >> (*t); + } + else + { + t = std::optional(); + } + return stream; +} + +QX_REGISTER_CLASS_NAME_TEMPLATE_1(std::optional) + +namespace qx +{ + namespace trait + { + + template + struct get_sql_type> + { + static inline const char *get() { return qx::trait::get_sql_type::get(); } + }; + + } // namespace trait +} // namespace qx + +namespace qx +{ + namespace cvt + { + namespace detail + { + +#ifndef _QX_NO_JSON + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::optional &t, const QString &format) + { + if (t) + { + return qx::cvt::to_json((*t), format); + }; + return QJsonValue(); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::optional &t, const QString &format) + { + if (j.isNull()) + { + t = std::optional(); + return qx_bool(true); + } + else if (!t) + { + t = T(); + } + return qx::cvt::from_json(j, (*t), format); + } + }; + +#endif // _QX_NO_JSON + + template + struct QxConvert_ToString> + { + static inline QString toString(const std::optional &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (t) + { + return qx::cvt::to_string((*t), format, index, ctx); + }; + return QString(); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, std::optional &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (!t) + { + t = T(); + }; + return qx::cvt::from_string(s, (*t), format, index, ctx); + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const std::optional &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (t) + { + return qx::cvt::to_variant((*t), format, index, ctx); + }; + return qx::trait::construct_null_qvariant::get(); + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, std::optional &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (v.isNull()) + { + t = std::optional(); + return qx_bool(true); + } + else if (!t) + { + t = T(); + } + return qx::cvt::from_variant(v, (*t), format, index, ctx); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_STD_OPTIONAL_H_ diff --git a/include/QxFactory/IxFactory.h b/include/QxFactory/IxFactory.h new file mode 100644 index 0000000..3cf7e49 --- /dev/null +++ b/include/QxFactory/IxFactory.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_FACTORY_H_ +#define _IX_FACTORY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxFactory.h + * \author XDL Team + * \ingroup QxFactory + * \brief Common interface for all classes that can be created dynamically using the class name + */ + +#ifndef _QX_NO_RTTI +#include +#endif // _QX_NO_RTTI + +#include + +namespace qx +{ + + /*! + * \ingroup QxFactory + * \brief qx::IxFactory : common interface for all classes that can be created dynamically using the class name + */ + class QX_DLL_EXPORT IxFactory + { + + protected: + QString m_sKeyFactory; //!< Factory key used by the collection QxFactoryX + + public: + IxFactory(const QString &sKey); + virtual ~IxFactory(); + + virtual qx::any createObject(bool bRawPointer = false) const = 0; + virtual void *createObjectNudePtr() const = 0; + +#ifndef _QX_NO_RTTI + virtual const std::type_info &typeInfo() const = 0; +#endif // _QX_NO_RTTI + + private: + IxFactory(const IxFactory &other) { Q_UNUSED(other); } + IxFactory &operator=(const IxFactory &other) + { + Q_UNUSED(other); + return (*this); + } + }; + +} // namespace qx + +#endif // _IX_FACTORY_H_ diff --git a/include/QxFactory/QxFactory.h b/include/QxFactory/QxFactory.h new file mode 100644 index 0000000..6da03b2 --- /dev/null +++ b/include/QxFactory/QxFactory.h @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_FACTORY_H_ +#define _QX_FACTORY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxFactory.h + * \author XDL Team + * \ingroup QxFactory + * \brief Concrete factory class to create object dynamically using the class name + */ + +#include + +#include + +#include +#include +#include + +#define QX_STR_CANNOT_INSTANTIATE_ABSTRACT_CLASS "[QxOrm] qx::QxFactory ---> cannot instantiate abstract class '%s'" + +#if _QX_AUTO_REGISTER_REPOSITORY +#define QX_AUTO_REGISTER_REPOSITORY(className, sKey) qx::register_repository(sKey); +#else // _QX_AUTO_REGISTER_REPOSITORY +#define QX_AUTO_REGISTER_REPOSITORY(className, sKey) /* Nothing */ +#endif // _QX_AUTO_REGISTER_REPOSITORY + +namespace qx +{ + + class IxPersistable; + template + class QxClass; + +#ifdef _QX_ENABLE_QT_NETWORK + namespace service + { + class IxService; + class IxParameter; + } // namespace service +#endif // _QX_ENABLE_QT_NETWORK + +#if _QX_AUTO_REGISTER_REPOSITORY + template + inline void register_repository(const QString &sKey); +#endif // _QX_AUTO_REGISTER_REPOSITORY + + /*! + * \ingroup QxFactory + * \brief qx::QxFactory : concrete factory class to create object of type T dynamically using the class name + */ + template + class QxFactory : public IxFactory + { + + public: + QxFactory(const QString &sKey) : IxFactory(sKey) { QX_AUTO_REGISTER_REPOSITORY(T, sKey); } + virtual ~QxFactory() { ; } + +#ifdef _QX_ENABLE_QT_NETWORK + virtual qx::any createObject(bool bRawPointer = false) const + { + QxClass::getSingleton(); + return qxCreateInstance::value, std::is_base_of::value, std::is_base_of::value, std::is_base_of::value, std::is_base_of::value, 0>::create(bRawPointer); + } + + virtual void *createObjectNudePtr() const + { + QxClass::getSingleton(); + return qxCreateInstance::value, std::is_base_of::value, std::is_base_of::value, std::is_base_of::value, std::is_base_of::value, 0>::createNudePtr(); + } +#else // _QX_ENABLE_QT_NETWORK + virtual qx::any createObject(bool bRawPointer = false) const + { + QxClass::getSingleton(); + return qxCreateInstance::value, std::is_base_of::value, false, false, std::is_base_of::value, 0>::create(bRawPointer); + } + + virtual void *createObjectNudePtr() const + { + QxClass::getSingleton(); + return qxCreateInstance::value, std::is_base_of::value, false, false, std::is_base_of::value, 0>::createNudePtr(); + } +#endif // _QX_ENABLE_QT_NETWORK + +#ifndef _QX_NO_RTTI + virtual const std::type_info &typeInfo() const + { + return typeid(T); + } +#endif // _QX_NO_RTTI + + private: + template + struct qxCreateInstance + { + static inline qx::any create(bool bRawPointer) + { + if (bRawPointer) + { + T *p = new T(); + return qx::any(p); + }; + std::shared_ptr ptr = std::make_shared(); + return qx::any(ptr); + } + static inline void *createNudePtr() { return static_cast(new T()); } + }; + + template + struct qxCreateInstance + { + static inline qx::any create(bool bRawPointer) + { + Q_UNUSED(bRawPointer); + qDebug(QX_STR_CANNOT_INSTANTIATE_ABSTRACT_CLASS, qx::trait::get_class_name::get()); + return qx::any(); + } + static inline void *createNudePtr() + { + qDebug(QX_STR_CANNOT_INSTANTIATE_ABSTRACT_CLASS, qx::trait::get_class_name::get()); + return NULL; + } + }; + + template + struct qxCreateInstance + { + static inline qx::any create(bool bRawPointer) + { + if (bRawPointer) + { + T *p = new T(); + return qx::any(p); + }; + std::shared_ptr ptr = std::make_shared(); + return qx::any(ptr); + } + static inline void *createNudePtr() { return static_cast(new T()); } + }; + +#ifdef _QX_ENABLE_QT_NETWORK + template + struct qxCreateInstance + { + static inline qx::any create(bool bRawPointer) + { + if (bRawPointer) + { + T *p = new T(); + return qx::any(p); + }; + std::shared_ptr ptr = std::make_shared(); + return qx::any(ptr); + } + static inline void *createNudePtr() { return static_cast(new T()); } + }; + + template + struct qxCreateInstance + { + static inline qx::any create(bool bRawPointer) + { + if (bRawPointer) + { + T *p = new T(); + return qx::any(p); + }; + std::shared_ptr ptr = std::make_shared(); + return qx::any(ptr); + } + static inline void *createNudePtr() { return static_cast(new T()); } + }; +#endif // _QX_ENABLE_QT_NETWORK + + template + struct qxCreateInstance + { + static inline qx::any create(bool bRawPointer) + { + if (bRawPointer) + { + T *p = new T(); + return qx::any(p); + }; + std::shared_ptr ptr = std::make_shared(); + return qx::any(ptr); + } + static inline void *createNudePtr() { return static_cast(new T()); } + }; + }; + +} // namespace qx + +#include "../../inl/QxFactory/QxFactory.inl" + +#endif // _QX_FACTORY_H_ diff --git a/include/QxFactory/QxFactoryX.h b/include/QxFactory/QxFactoryX.h new file mode 100644 index 0000000..9e2308d --- /dev/null +++ b/include/QxFactory/QxFactoryX.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_FACTORY_X_H_ +#define _QX_FACTORY_X_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxFactoryX.h + * \author XDL Team + * \ingroup QxFactory + * \brief List of all classes registered with QxOrm library factory pattern to create object instance dynamically using the class name + */ + +#include +#include + +#include + +#include + +namespace qx +{ + + inline qx::any create(const QString &sKey, bool bRawPointer = false); + template + inline T *create_nude_ptr(const QString &sKey); + inline void *create_void_ptr(const QString &sKey); + + class QxClassX; + + /*! + * \ingroup QxFactory + * \brief qx::QxFactoryX : list of all classes registered with QxOrm library factory pattern to create object instance dynamically using the class name + */ + class QX_DLL_EXPORT QxFactoryX : public QxSingleton + { + + friend class QxClassX; + friend class IxFactory; + friend class QxSingleton; + friend inline qx::any create(const QString &sKey, bool bRawPointer); + template + friend inline T *create_nude_ptr(const QString &sKey); + friend inline void *create_void_ptr(const QString &sKey); + + protected: + QHash m_mapFactoryX; //!< Collection of all 'IxFactory' pointer + QMutex m_oMutexFactoryX; //!< Mutex -> 'QxFactoryX' is thread-safe + + private: + QxFactoryX() : QxSingleton("qx::QxFactoryX") { ; } + virtual ~QxFactoryX() { ; } + + QHash *getAllFactory() { return (&m_mapFactoryX); } + + void registerFactory(const QString &sKey, IxFactory *pFactory); + void unregisterFactory(const QString &sKey); + + qx::any createObject(const QString &sKey, bool bRawPointer = false) const; + void *createObjectNudePtr(const QString &sKey) const; + +#ifndef _QX_NO_RTTI + const std::type_info &typeInfo(const QString &sKey) const; +#endif // _QX_NO_RTTI + + static inline qx::any createInstance(const QString &sKey, bool bRawPointer = false) { return QxFactoryX::getSingleton()->createObject(sKey, bRawPointer); } + static inline void *createInstanceNudePtr(const QString &sKey) { return QxFactoryX::getSingleton()->createObjectNudePtr(sKey); } + +#ifndef _QX_NO_RTTI + static inline const std::type_info &getTypeInfo(const QString &sKey) { return QxFactoryX::getSingleton()->typeInfo(sKey); } +#endif // _QX_NO_RTTI + }; + + /*! + * \ingroup QxFactory + * \brief Return a smart-pointer new instance of object (std::shared_ptr) associated by key sKey using qx::any type (for example : qx::create("drug") return a new instance of smart-pointer drug class into qx::any type) + */ + inline qx::any create(const QString &sKey, bool bRawPointer /* = false */) + { + return qx::QxFactoryX::createInstance(sKey, bRawPointer); + } + + /*! + * \ingroup QxFactory + * \brief Return a nude pointer (be careful with memory leak) of type T associated by key sKey, or return NULL if sKey is not registered into factory engine + */ + template + inline T *create_nude_ptr(const QString &sKey) +#ifdef _QX_NO_RTTI + { + return static_cast(qx::QxFactoryX::createInstanceNudePtr(sKey)); + } +#else // _QX_NO_RTTI + { + return dynamic_cast(static_cast(qx::QxFactoryX::createInstanceNudePtr(sKey))); + } +#endif // _QX_NO_RTTI + + /*! + * \ingroup QxFactory + * \brief Return a void * pointer (be careful with memory leak) associated by key sKey, or return NULL if sKey is not registered into factory engine + */ + inline void *create_void_ptr(const QString &sKey) + { + return qx::QxFactoryX::createInstanceNudePtr(sKey); + } + +} // namespace qx + +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::QxFactoryX) + +#endif // _QX_FACTORY_X_H_ diff --git a/include/QxFunction/IxFunction.h b/include/QxFunction/IxFunction.h new file mode 100644 index 0000000..ff5d75f --- /dev/null +++ b/include/QxFunction/IxFunction.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_FUNCTION_H_ +#define _IX_FUNCTION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxFunction.h + * \author XDL Team + * \ingroup QxFunction + * \brief Common interface for all functions registered into QxOrm context (used by introspection engine) + */ + +#include +#include +#include + +#include + +#include +#include + +#include + +namespace qx +{ + + /*! + * \ingroup QxFunction + * \brief qx::IxFunction : common interface for all functions registered into QxOrm context (used by introspection engine) + */ + class IxFunction : public qx::QxPropertyBag + { + + protected: + QString m_sKey; //!< Function key + QString m_sSeparator; //!< Separator character(s) for 'QString' parameters type + QString m_sDescription; //!< Function description + + public: + typedef std::vector type_any_params; + + IxFunction() : qx::QxPropertyBag(), m_sSeparator("|") { ; } + virtual ~IxFunction() { ; } + + QString getKey() const { return m_sKey; } + QString getSeparator() const { return m_sSeparator; } + QString getDescription() const { return m_sDescription; } + + void setKey(const QString &s) { m_sKey = s; } + void setSeparator(const QString &s) { m_sSeparator = s; } + void setDescription(const QString &s) { m_sDescription = s; } + + virtual int getParamCount() const = 0; + + virtual qx_bool invoke(const QString ¶ms = QString(), qx::any *ret = NULL) const = 0; + virtual qx_bool invoke(const type_any_params ¶ms, qx::any *ret = NULL) const = 0; + virtual qx_bool invoke(void *pOwner, const QString ¶ms = QString(), qx::any *ret = NULL) const = 0; + virtual qx_bool invoke(void *pOwner, const type_any_params ¶ms, qx::any *ret = NULL) const = 0; + + virtual qx_bool isValidFct() const = 0; + virtual qx_bool isValidParams(const QString ¶ms) const = 0; + virtual qx_bool isValidParams(const type_any_params ¶ms) const = 0; + + template + qx_bool isValidOwner(void *pOwner, T *dummy) const + { + Q_UNUSED(dummy); + typedef std::is_same qx_verify_owner_tmp; + static_assert(!qx_verify_owner_tmp::value, "! qx_verify_owner_tmp::value"); + if (!pOwner) + { + return qx_bool(false, 0, QX_FUNCTION_ERR_NULL_OWNER); + } +#ifndef _QX_NO_RTTI + if (!dynamic_cast(static_cast(pOwner))) + { + return qx_bool(false, 0, QX_FUNCTION_ERR_INVALID_OWNER); + } +#endif // _QX_NO_RTTI + return true; + } + + template + qx_bool isValid(const T ¶ms) const + { + qx_bool bValid = isValidFct(); + if (!bValid) + { + return bValid; + }; + bValid = isValidParams(params); + if (!bValid) + { + return bValid; + }; + return true; + } + + template + qx_bool isValid(void *pOwner, const T ¶ms, U *dummy) const + { + Q_UNUSED(dummy); + qx_bool bValid = isValidFct(); + if (!bValid) + { + return bValid; + }; + bValid = isValidParams(params); + if (!bValid) + { + return bValid; + }; + bValid = isValidOwner(pOwner, NULL); + if (!bValid) + { + return bValid; + }; + return true; + } + }; + + typedef std::shared_ptr IxFunction_ptr; + typedef QxCollection IxFunctionX; + typedef std::shared_ptr IxFunctionX_ptr; + +} // namespace qx + +#endif // _IX_FUNCTION_H_ diff --git a/include/QxFunction/QxFunctionError.h b/include/QxFunction/QxFunctionError.h new file mode 100644 index 0000000..65e3a0f --- /dev/null +++ b/include/QxFunction/QxFunctionError.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_FUNCTION_ERROR_H_ +#define _QX_FUNCTION_ERROR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxFunctionError.h + * \author XDL Team + * \ingroup QxFunction + * \brief Define all messages when an error occured using QxFunction module of QxOrm library + */ + +#define QX_FUNCTION_ERR_NUMBER_PARAMS "Incorrect parameters count" +#define QX_FUNCTION_ERR_INVALID_PARAM "Invalid parameter at position 'XXX'" +#define QX_FUNCTION_ERR_INVALID_FCT "Invalid function" +#define QX_FUNCTION_ERR_EMPTY_FCT "Empty function" +#define QX_FUNCTION_ERR_INVALID_MEMBER_FCT "Invalid member function" +#define QX_FUNCTION_ERR_EMPTY_MEMBER_FCT "Empty member function" +#define QX_FUNCTION_ERR_INVALID_OWNER "Invalid owner" +#define QX_FUNCTION_ERR_NULL_OWNER "NULL owner" +#define QX_FUNCTION_ERR_INVALID_INVOKE_CALL "Invalid 'invoke()' call" +#define QX_FUNCTION_ERR_UNKNOWN_ERROR "Unknown error calling function" + +#endif // _QX_FUNCTION_ERROR_H_ diff --git a/include/QxFunction/QxFunctionInclude.h b/include/QxFunction/QxFunctionInclude.h new file mode 100644 index 0000000..d5cd7b0 --- /dev/null +++ b/include/QxFunction/QxFunctionInclude.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_FUNCTION_INCLUDE_H_ +#define _QX_FUNCTION_INCLUDE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // _QX_FUNCTION_INCLUDE_H_ diff --git a/include/QxFunction/QxFunctionMacro.h b/include/QxFunction/QxFunctionMacro.h new file mode 100644 index 0000000..016df03 --- /dev/null +++ b/include/QxFunction/QxFunctionMacro.h @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_FUNCTION_MACRO_H_ +#define _QX_FUNCTION_MACRO_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#define QX_FUNCTION_CLASS_FCT(className) \ +public: \ + type_fct m_fct; \ + className(type_fct fct) : IxFunction(), m_fct(fct) { ; }; \ + virtual ~className() { ; }; \ + virtual qx_bool invoke(const QString ¶ms = QString(), qx::any *ret = NULL) const \ + { \ + return QxInvokerFct::value>::invoke(params, ret, this); \ + } \ + virtual qx_bool invoke(const type_any_params ¶ms, qx::any *ret = NULL) const \ + { \ + return QxInvokerFct::value>::invoke(params, ret, this); \ + } \ + virtual qx_bool invoke(void *pOwner, const QString ¶ms = QString(), qx::any *ret = NULL) const \ + { \ + Q_UNUSED(pOwner); \ + Q_UNUSED(params); \ + Q_UNUSED(ret); \ + qAssert(false); \ + return qx_bool(false, 0, QX_FUNCTION_ERR_INVALID_INVOKE_CALL); \ + } \ + virtual qx_bool invoke(void *pOwner, const type_any_params ¶ms, qx::any *ret = NULL) const \ + { \ + Q_UNUSED(pOwner); \ + Q_UNUSED(params); \ + Q_UNUSED(ret); \ + qAssert(false); \ + return qx_bool(false, 0, QX_FUNCTION_ERR_INVALID_INVOKE_CALL); \ + } \ + virtual qx_bool isValidFct() const \ + { \ + return ((!m_fct) ? qx_bool(false, 0, QX_FUNCTION_ERR_EMPTY_FCT) : qx_bool(true)); \ + } + +#define QX_FUNCTION_CLASS_MEMBER_FCT(className) \ +public: \ + type_fct m_fct; \ + className(type_fct fct) : IxFunction(), m_fct(fct) { ; }; \ + virtual ~className() { ; }; \ + virtual qx_bool invoke(void *pOwner, const QString ¶ms = QString(), qx::any *ret = NULL) const \ + { \ + return QxInvokerFct::value>::invoke(pOwner, params, ret, this); \ + } \ + virtual qx_bool invoke(void *pOwner, const type_any_params ¶ms, qx::any *ret = NULL) const \ + { \ + return QxInvokerFct::value>::invoke(pOwner, params, ret, this); \ + } \ + virtual qx_bool invoke(const QString ¶ms = QString(), qx::any *ret = NULL) const \ + { \ + Q_UNUSED(params); \ + Q_UNUSED(ret); \ + qAssert(false); \ + return qx_bool(false, 0, QX_FUNCTION_ERR_INVALID_INVOKE_CALL); \ + } \ + virtual qx_bool invoke(const type_any_params ¶ms, qx::any *ret = NULL) const \ + { \ + Q_UNUSED(params); \ + Q_UNUSED(ret); \ + qAssert(false); \ + return qx_bool(false, 0, QX_FUNCTION_ERR_INVALID_INVOKE_CALL); \ + } \ + virtual qx_bool isValidFct() const \ + { \ + return ((!m_fct) ? qx_bool(false, 0, QX_FUNCTION_ERR_EMPTY_MEMBER_FCT) : qx_bool(true)); \ + } + +#define QX_FUNCTION_CATCH_AND_RETURN_INVOKE() \ + catch (const std::exception &e) { bValid = qx_bool(false, 0, e.what()); } \ + catch (...) { bValid = qx_bool(false, 0, QX_FUNCTION_ERR_UNKNOWN_ERROR); } \ + if (!bValid) \ + { \ + QString sMsgDebug = bValid.getDesc(); \ + qDebug("[QxOrm] %s", qPrintable(sMsgDebug)); \ + qAssert(false); \ + } \ + return bValid; + +#define QX_FUNCTION_INVOKE_START_WITH_OWNER() \ + if (ret) \ + { \ + (*ret) = qx::any(); \ + } \ + qx_bool bValid = pThis->isValid(pOwner, params, NULL); \ + if (!bValid) \ + { \ + QString sMsgDebug = bValid.getDesc(); \ + qDebug("[QxOrm] %s", qPrintable(sMsgDebug)); \ + qAssert(false); \ + return bValid; \ + } + +#define QX_FUNCTION_INVOKE_START_WITHOUT_OWNER() \ + if (ret) \ + { \ + (*ret) = qx::any(); \ + } \ + qx_bool bValid = pThis->isValid(params); \ + if (!bValid) \ + { \ + QString sMsgDebug = bValid.getDesc(); \ + qDebug("[QxOrm] %s", qPrintable(sMsgDebug)); \ + qAssert(false); \ + return bValid; \ + } + +#define QX_FUNCTION_FETCH_PARAM(TYPE, VALUE, FCT) \ + typename std::remove_const::type VALUE; \ + { \ + qx_bool bTmp = qx::function::detail::FCT(params, VALUE, pThis); \ + if (!bTmp) \ + { \ + QString sMsgDebug = bTmp.getDesc(); \ + qDebug("[QxOrm] %s", qPrintable(sMsgDebug)); \ + qAssert(false); \ + return bTmp; \ + } \ + } + +#define QX_FUNCTION_GET_PARAM_TYPE_ANY(PARAMCOUNT) \ + Q_UNUSED(qx_fct); \ + if (params.size() < PARAMCOUNT) \ + { \ + return qx_bool(false, 0, QX_FUNCTION_ERR_NUMBER_PARAMS); \ + } \ + qx_bool bValid = true; \ + try \ + { \ + p = qx::any_cast

    (params[PARAMCOUNT - 1]); \ + } \ + catch (...) \ + { \ + bValid = qx_bool(false, 0, QString(QX_FUNCTION_ERR_INVALID_PARAM).replace("XXX", QString::number(PARAMCOUNT))); \ + } \ + return bValid; + +#define QX_FUNCTION_GET_PARAM_TYPE_STRING(PARAMCOUNT) \ + if (!qx_fct) \ + { \ + return qx_bool(false, 0, QX_FUNCTION_ERR_UNKNOWN_ERROR); \ + } \ + QStringList lst = params.split(qx_fct->getSeparator()); \ + if (lst.size() < PARAMCOUNT) \ + { \ + return qx_bool(false, 0, QX_FUNCTION_ERR_NUMBER_PARAMS); \ + } \ + qx_bool bValid = true; \ + try \ + { \ + bValid = qx::cvt::from_string(lst.at(PARAMCOUNT - 1), p); \ + } \ + catch (...) \ + { \ + bValid = qx_bool(false, 0, QString(QX_FUNCTION_ERR_INVALID_PARAM).replace("XXX", QString::number(PARAMCOUNT))); \ + } \ + return bValid; + +#define QX_FUNCTION_GET_PARAM_TYPE_STRING_TO_QSTRING(PARAMCOUNT) \ + if (!qx_fct) \ + { \ + return qx_bool(false, 0, QX_FUNCTION_ERR_UNKNOWN_ERROR); \ + } \ + QStringList lst = params.split(qx_fct->getSeparator()); \ + if (lst.size() < PARAMCOUNT) \ + { \ + return qx_bool(false, 0, QX_FUNCTION_ERR_NUMBER_PARAMS); \ + } \ + p = lst.at(PARAMCOUNT - 1); \ + return true; + +#endif // _QX_FUNCTION_MACRO_H_ diff --git a/include/QxFunction/QxFunction_0.h b/include/QxFunction/QxFunction_0.h new file mode 100644 index 0000000..5420c18 --- /dev/null +++ b/include/QxFunction/QxFunction_0.h @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_FUNCTION_0_H_ +#define _QX_FUNCTION_0_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxFunction_0.h + * \author XDL Team + * \ingroup QxFunction + * \brief Concrete function class registered into QxOrm context without parameter + */ + +#include +#include + +namespace qx +{ + + /*! + * \ingroup QxFunction + * \brief qx::QxFunction_0 : concrete function registered into QxOrm context defined into class Owner, returning an object of type R and without parameter + */ + template + class QxFunction_0 : public IxFunction + { + + public: + typedef std::function type_fct; + QX_FUNCTION_CLASS_MEMBER_FCT(QxFunction_0); + + virtual int getParamCount() const { return 0; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_0 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + try + { + pThis->m_fct(static_cast(pOwner)); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_0 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + try + { + R retTmp = pThis->m_fct(static_cast(pOwner)); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + template + class QxFunction_0 : public IxFunction + { + + public: + typedef std::function type_fct; + QX_FUNCTION_CLASS_FCT(QxFunction_0); + + virtual int getParamCount() const { return 0; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_0 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + try + { + pThis->m_fct(); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_0 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + try + { + R retTmp = pThis->m_fct(); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + namespace function + { + + template + IxFunction_ptr bind_fct_0(const typename QxFunction_0::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(qx_verify_owner_tmp::value, "qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + template + IxFunction_ptr bind_member_fct_0(const typename QxFunction_0::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(!qx_verify_owner_tmp::value, "! qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + } // namespace function +} // namespace qx + +#endif // _QX_FUNCTION_0_H_ diff --git a/include/QxFunction/QxFunction_1.h b/include/QxFunction/QxFunction_1.h new file mode 100644 index 0000000..d048921 --- /dev/null +++ b/include/QxFunction/QxFunction_1.h @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_FUNCTION_1_H_ +#define _QX_FUNCTION_1_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxFunction_1.h + * \author XDL Team + * \ingroup QxFunction + * \brief Concrete function class registered into QxOrm context with 1 parameter + */ + +#include +#include + +namespace qx +{ + + /*! + * \ingroup QxFunction + * \brief qx::QxFunction_1 : concrete function registered into QxOrm context defined into class Owner, returning an object of type R and with 1 parameter P1 + */ + template + class QxFunction_1 : public IxFunction + { + + public: + typedef std::function type_fct; + typedef typename qx::trait::remove_attr::type type_P1; + QX_FUNCTION_CLASS_MEMBER_FCT(QxFunction_1); + + virtual int getParamCount() const { return 1; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_1 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + try + { + pThis->m_fct(static_cast(pOwner), p1); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_1 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + try + { + R retTmp = pThis->m_fct(static_cast(pOwner), p1); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + template + class QxFunction_1 : public IxFunction + { + + public: + typedef std::function type_fct; + typedef typename qx::trait::remove_attr::type type_P1; + QX_FUNCTION_CLASS_FCT(QxFunction_1); + + virtual int getParamCount() const { return 1; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_1 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + try + { + pThis->m_fct(p1); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_1 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + try + { + R retTmp = pThis->m_fct(p1); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + namespace function + { + + template + IxFunction_ptr bind_fct_1(const typename QxFunction_1::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(qx_verify_owner_tmp::value, "qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + template + IxFunction_ptr bind_member_fct_1(const typename QxFunction_1::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(!qx_verify_owner_tmp::value, "! qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + } // namespace function +} // namespace qx + +#endif // _QX_FUNCTION_1_H_ diff --git a/include/QxFunction/QxFunction_2.h b/include/QxFunction/QxFunction_2.h new file mode 100644 index 0000000..294e560 --- /dev/null +++ b/include/QxFunction/QxFunction_2.h @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_FUNCTION_2_H_ +#define _QX_FUNCTION_2_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxFunction_2.h + * \author XDL Team + * \ingroup QxFunction + * \brief Concrete function class registered into QxOrm context with 2 parameters + */ + +#include +#include + +namespace qx +{ + + /*! + * \ingroup QxFunction + * \brief qx::QxFunction_2 : concrete function registered into QxOrm context defined into class Owner, returning an object of type R and with 2 parameters P1, P2 + */ + template + class QxFunction_2 : public IxFunction + { + + public: + typedef std::function type_fct; + typedef typename qx::trait::remove_attr::type type_P1; + typedef typename qx::trait::remove_attr::type type_P2; + QX_FUNCTION_CLASS_MEMBER_FCT(QxFunction_2); + + virtual int getParamCount() const { return 2; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_2 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + try + { + pThis->m_fct(static_cast(pOwner), p1, p2); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_2 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + try + { + R retTmp = pThis->m_fct(static_cast(pOwner), p1, p2); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + template + class QxFunction_2 : public IxFunction + { + + public: + typedef std::function type_fct; + typedef typename qx::trait::remove_attr::type type_P1; + typedef typename qx::trait::remove_attr::type type_P2; + QX_FUNCTION_CLASS_FCT(QxFunction_2); + + virtual int getParamCount() const { return 2; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_2 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + try + { + pThis->m_fct(p1, p2); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_2 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + try + { + R retTmp = pThis->m_fct(p1, p2); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + namespace function + { + + template + IxFunction_ptr bind_fct_2(const typename QxFunction_2::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(qx_verify_owner_tmp::value, "qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + template + IxFunction_ptr bind_member_fct_2(const typename QxFunction_2::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(!qx_verify_owner_tmp::value, "! qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + } // namespace function +} // namespace qx + +#endif // _QX_FUNCTION_2_H_ diff --git a/include/QxFunction/QxFunction_3.h b/include/QxFunction/QxFunction_3.h new file mode 100644 index 0000000..354fa58 --- /dev/null +++ b/include/QxFunction/QxFunction_3.h @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_FUNCTION_3_H_ +#define _QX_FUNCTION_3_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxFunction_3.h + * \author XDL Team + * \ingroup QxFunction + * \brief Concrete function class registered into QxOrm context with 3 parameters + */ + +#include +#include + +namespace qx +{ + + /*! + * \ingroup QxFunction + * \brief qx::QxFunction_3 : concrete function registered into QxOrm context defined into class Owner, returning an object of type R and with 3 parameters P1, P2, P3 + */ + template + class QxFunction_3 : public IxFunction + { + + public: + typedef std::function type_fct; + typedef typename qx::trait::remove_attr::type type_P1; + typedef typename qx::trait::remove_attr::type type_P2; + typedef typename qx::trait::remove_attr::type type_P3; + QX_FUNCTION_CLASS_MEMBER_FCT(QxFunction_3); + + virtual int getParamCount() const { return 3; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_3 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + try + { + pThis->m_fct(static_cast(pOwner), p1, p2, p3); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_3 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + try + { + R retTmp = pThis->m_fct(static_cast(pOwner), p1, p2, p3); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + template + class QxFunction_3 : public IxFunction + { + + public: + typedef std::function type_fct; + typedef typename qx::trait::remove_attr::type type_P1; + typedef typename qx::trait::remove_attr::type type_P2; + typedef typename qx::trait::remove_attr::type type_P3; + QX_FUNCTION_CLASS_FCT(QxFunction_3); + + virtual int getParamCount() const { return 3; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_3 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + try + { + pThis->m_fct(p1, p2, p3); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_3 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + try + { + R retTmp = pThis->m_fct(p1, p2, p3); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + namespace function + { + + template + IxFunction_ptr bind_fct_3(const typename QxFunction_3::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(qx_verify_owner_tmp::value, "qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + template + IxFunction_ptr bind_member_fct_3(const typename QxFunction_3::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(!qx_verify_owner_tmp::value, "! qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + } // namespace function +} // namespace qx + +#endif // _QX_FUNCTION_3_H_ diff --git a/include/QxFunction/QxFunction_4.h b/include/QxFunction/QxFunction_4.h new file mode 100644 index 0000000..be4d0c1 --- /dev/null +++ b/include/QxFunction/QxFunction_4.h @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_FUNCTION_4_H_ +#define _QX_FUNCTION_4_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxFunction_4.h + * \author XDL Team + * \ingroup QxFunction + * \brief Concrete function class registered into QxOrm context with 4 parameters + */ + +#include +#include + +namespace qx +{ + + /*! + * \ingroup QxFunction + * \brief qx::QxFunction_4 : concrete function registered into QxOrm context defined into class Owner, returning an object of type R and with 4 parameters P1, P2, P3, P4 + */ + template + class QxFunction_4 : public IxFunction + { + + public: + typedef std::function type_fct; + typedef typename qx::trait::remove_attr::type type_P1; + typedef typename qx::trait::remove_attr::type type_P2; + typedef typename qx::trait::remove_attr::type type_P3; + typedef typename qx::trait::remove_attr::type type_P4; + QX_FUNCTION_CLASS_MEMBER_FCT(QxFunction_4); + + virtual int getParamCount() const { return 4; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_4 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + try + { + pThis->m_fct(static_cast(pOwner), p1, p2, p3, p4); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_4 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + try + { + R retTmp = pThis->m_fct(static_cast(pOwner), p1, p2, p3, p4); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + template + class QxFunction_4 : public IxFunction + { + + public: + typedef std::function type_fct; + typedef typename qx::trait::remove_attr::type type_P1; + typedef typename qx::trait::remove_attr::type type_P2; + typedef typename qx::trait::remove_attr::type type_P3; + typedef typename qx::trait::remove_attr::type type_P4; + QX_FUNCTION_CLASS_FCT(QxFunction_4); + + virtual int getParamCount() const { return 4; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_4 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + try + { + pThis->m_fct(p1, p2, p3, p4); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_4 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + try + { + R retTmp = pThis->m_fct(p1, p2, p3, p4); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + namespace function + { + + template + IxFunction_ptr bind_fct_4(const typename QxFunction_4::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(qx_verify_owner_tmp::value, "qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + template + IxFunction_ptr bind_member_fct_4(const typename QxFunction_4::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(!qx_verify_owner_tmp::value, "! qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + } // namespace function +} // namespace qx + +#endif // _QX_FUNCTION_4_H_ diff --git a/include/QxFunction/QxFunction_5.h b/include/QxFunction/QxFunction_5.h new file mode 100644 index 0000000..59c5a55 --- /dev/null +++ b/include/QxFunction/QxFunction_5.h @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_FUNCTION_5_H_ +#define _QX_FUNCTION_5_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxFunction_5.h + * \author XDL Team + * \ingroup QxFunction + * \brief Concrete function class registered into QxOrm context with 5 parameters + */ + +#include +#include + +namespace qx +{ + + /*! + * \ingroup QxFunction + * \brief qx::QxFunction_5 : concrete function registered into QxOrm context defined into class Owner, returning an object of type R and with 5 parameters P1, P2, P3, P4, P5 + */ + template + class QxFunction_5 : public IxFunction + { + + public: + typedef std::function type_fct; + typedef typename qx::trait::remove_attr::type type_P1; + typedef typename qx::trait::remove_attr::type type_P2; + typedef typename qx::trait::remove_attr::type type_P3; + typedef typename qx::trait::remove_attr::type type_P4; + typedef typename qx::trait::remove_attr::type type_P5; + QX_FUNCTION_CLASS_MEMBER_FCT(QxFunction_5); + + virtual int getParamCount() const { return 5; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_5 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + try + { + pThis->m_fct(static_cast(pOwner), p1, p2, p3, p4, p5); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_5 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + try + { + R retTmp = pThis->m_fct(static_cast(pOwner), p1, p2, p3, p4, p5); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + template + class QxFunction_5 : public IxFunction + { + + public: + typedef std::function type_fct; + typedef typename qx::trait::remove_attr::type type_P1; + typedef typename qx::trait::remove_attr::type type_P2; + typedef typename qx::trait::remove_attr::type type_P3; + typedef typename qx::trait::remove_attr::type type_P4; + typedef typename qx::trait::remove_attr::type type_P5; + QX_FUNCTION_CLASS_FCT(QxFunction_5); + + virtual int getParamCount() const { return 5; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_5 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + try + { + pThis->m_fct(p1, p2, p3, p4, p5); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_5 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + try + { + R retTmp = pThis->m_fct(p1, p2, p3, p4, p5); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + namespace function + { + + template + IxFunction_ptr bind_fct_5(const typename QxFunction_5::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(qx_verify_owner_tmp::value, "qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + template + IxFunction_ptr bind_member_fct_5(const typename QxFunction_5::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(!qx_verify_owner_tmp::value, "! qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + } // namespace function +} // namespace qx + +#endif // _QX_FUNCTION_5_H_ diff --git a/include/QxFunction/QxFunction_6.h b/include/QxFunction/QxFunction_6.h new file mode 100644 index 0000000..d442edb --- /dev/null +++ b/include/QxFunction/QxFunction_6.h @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_FUNCTION_6_H_ +#define _QX_FUNCTION_6_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxFunction_6.h + * \author XDL Team + * \ingroup QxFunction + * \brief Concrete function class registered into QxOrm context with 6 parameters + */ + +#include +#include + +namespace qx +{ + + /*! + * \ingroup QxFunction + * \brief qx::QxFunction_6 : concrete function registered into QxOrm context defined into class Owner, returning an object of type R and with 6 parameters P1, P2, P3, P4, P5, P6 + */ + template + class QxFunction_6 : public IxFunction + { + + public: + typedef std::function type_fct; + typedef typename qx::trait::remove_attr::type type_P1; + typedef typename qx::trait::remove_attr::type type_P2; + typedef typename qx::trait::remove_attr::type type_P3; + typedef typename qx::trait::remove_attr::type type_P4; + typedef typename qx::trait::remove_attr::type type_P5; + typedef typename qx::trait::remove_attr::type type_P6; + QX_FUNCTION_CLASS_MEMBER_FCT(QxFunction_6); + + virtual int getParamCount() const { return 6; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_6 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + QX_FUNCTION_FETCH_PARAM(type_P6, p6, get_param_6); + try + { + pThis->m_fct(static_cast(pOwner), p1, p2, p3, p4, p5, p6); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_6 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + QX_FUNCTION_FETCH_PARAM(type_P6, p6, get_param_6); + try + { + R retTmp = pThis->m_fct(static_cast(pOwner), p1, p2, p3, p4, p5, p6); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + template + class QxFunction_6 : public IxFunction + { + + public: + typedef std::function type_fct; + typedef typename qx::trait::remove_attr::type type_P1; + typedef typename qx::trait::remove_attr::type type_P2; + typedef typename qx::trait::remove_attr::type type_P3; + typedef typename qx::trait::remove_attr::type type_P4; + typedef typename qx::trait::remove_attr::type type_P5; + typedef typename qx::trait::remove_attr::type type_P6; + QX_FUNCTION_CLASS_FCT(QxFunction_6); + + virtual int getParamCount() const { return 6; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_6 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + QX_FUNCTION_FETCH_PARAM(type_P6, p6, get_param_6); + try + { + pThis->m_fct(p1, p2, p3, p4, p5, p6); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_6 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + QX_FUNCTION_FETCH_PARAM(type_P6, p6, get_param_6); + try + { + R retTmp = pThis->m_fct(p1, p2, p3, p4, p5, p6); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + namespace function + { + + template + IxFunction_ptr bind_fct_6(const typename QxFunction_6::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(qx_verify_owner_tmp::value, "qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + template + IxFunction_ptr bind_member_fct_6(const typename QxFunction_6::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(!qx_verify_owner_tmp::value, "! qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + } // namespace function +} // namespace qx + +#endif // _QX_FUNCTION_6_H_ diff --git a/include/QxFunction/QxFunction_7.h b/include/QxFunction/QxFunction_7.h new file mode 100644 index 0000000..7760504 --- /dev/null +++ b/include/QxFunction/QxFunction_7.h @@ -0,0 +1,230 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_FUNCTION_7_H_ +#define _QX_FUNCTION_7_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxFunction_7.h + * \author XDL Team + * \ingroup QxFunction + * \brief Concrete function class registered into QxOrm context with 7 parameters + */ + +#include +#include + +namespace qx +{ + + /*! + * \ingroup QxFunction + * \brief qx::QxFunction_7 : concrete function registered into QxOrm context defined into class Owner, returning an object of type R and with 7 parameters P1, P2, P3, P4, P5, P6, P7 + */ + template + class QxFunction_7 : public IxFunction + { + + public: + typedef std::function type_fct; + typedef typename qx::trait::remove_attr::type type_P1; + typedef typename qx::trait::remove_attr::type type_P2; + typedef typename qx::trait::remove_attr::type type_P3; + typedef typename qx::trait::remove_attr::type type_P4; + typedef typename qx::trait::remove_attr::type type_P5; + typedef typename qx::trait::remove_attr::type type_P6; + typedef typename qx::trait::remove_attr::type type_P7; + QX_FUNCTION_CLASS_MEMBER_FCT(QxFunction_7); + + virtual int getParamCount() const { return 7; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_7 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + QX_FUNCTION_FETCH_PARAM(type_P6, p6, get_param_6); + QX_FUNCTION_FETCH_PARAM(type_P7, p7, get_param_7); + try + { + pThis->m_fct(static_cast(pOwner), p1, p2, p3, p4, p5, p6, p7); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_7 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + QX_FUNCTION_FETCH_PARAM(type_P6, p6, get_param_6); + QX_FUNCTION_FETCH_PARAM(type_P7, p7, get_param_7); + try + { + R retTmp = pThis->m_fct(static_cast(pOwner), p1, p2, p3, p4, p5, p6, p7); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + template + class QxFunction_7 : public IxFunction + { + + public: + typedef std::function type_fct; + typedef typename qx::trait::remove_attr::type type_P1; + typedef typename qx::trait::remove_attr::type type_P2; + typedef typename qx::trait::remove_attr::type type_P3; + typedef typename qx::trait::remove_attr::type type_P4; + typedef typename qx::trait::remove_attr::type type_P5; + typedef typename qx::trait::remove_attr::type type_P6; + typedef typename qx::trait::remove_attr::type type_P7; + QX_FUNCTION_CLASS_FCT(QxFunction_7); + + virtual int getParamCount() const { return 7; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_7 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + QX_FUNCTION_FETCH_PARAM(type_P6, p6, get_param_6); + QX_FUNCTION_FETCH_PARAM(type_P7, p7, get_param_7); + try + { + pThis->m_fct(p1, p2, p3, p4, p5, p6, p7); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_7 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + QX_FUNCTION_FETCH_PARAM(type_P6, p6, get_param_6); + QX_FUNCTION_FETCH_PARAM(type_P7, p7, get_param_7); + try + { + R retTmp = pThis->m_fct(p1, p2, p3, p4, p5, p6, p7); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + namespace function + { + + template + IxFunction_ptr bind_fct_7(const typename QxFunction_7::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(qx_verify_owner_tmp::value, "qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + template + IxFunction_ptr bind_member_fct_7(const typename QxFunction_7::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(!qx_verify_owner_tmp::value, "! qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + } // namespace function +} // namespace qx + +#endif // _QX_FUNCTION_7_H_ diff --git a/include/QxFunction/QxFunction_8.h b/include/QxFunction/QxFunction_8.h new file mode 100644 index 0000000..6f50c49 --- /dev/null +++ b/include/QxFunction/QxFunction_8.h @@ -0,0 +1,236 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_FUNCTION_8_H_ +#define _QX_FUNCTION_8_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxFunction_8.h + * \author XDL Team + * \ingroup QxFunction + * \brief Concrete function class registered into QxOrm context with 8 parameters + */ + +#include +#include + +namespace qx +{ + + /*! + * \ingroup QxFunction + * \brief qx::QxFunction_8 : concrete function registered into QxOrm context defined into class Owner, returning an object of type R and with 8 parameters P1, P2, P3, P4, P5, P6, P7, P8 + */ + template + class QxFunction_8 : public IxFunction + { + + public: + typedef std::function type_fct; + typedef typename qx::trait::remove_attr::type type_P1; + typedef typename qx::trait::remove_attr::type type_P2; + typedef typename qx::trait::remove_attr::type type_P3; + typedef typename qx::trait::remove_attr::type type_P4; + typedef typename qx::trait::remove_attr::type type_P5; + typedef typename qx::trait::remove_attr::type type_P6; + typedef typename qx::trait::remove_attr::type type_P7; + typedef typename qx::trait::remove_attr::type type_P8; + QX_FUNCTION_CLASS_MEMBER_FCT(QxFunction_8); + + virtual int getParamCount() const { return 8; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_8 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + QX_FUNCTION_FETCH_PARAM(type_P6, p6, get_param_6); + QX_FUNCTION_FETCH_PARAM(type_P7, p7, get_param_7); + QX_FUNCTION_FETCH_PARAM(type_P8, p8, get_param_8); + try + { + pThis->m_fct(static_cast(pOwner), p1, p2, p3, p4, p5, p6, p7, p8); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_8 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + QX_FUNCTION_FETCH_PARAM(type_P6, p6, get_param_6); + QX_FUNCTION_FETCH_PARAM(type_P7, p7, get_param_7); + QX_FUNCTION_FETCH_PARAM(type_P8, p8, get_param_8); + try + { + R retTmp = pThis->m_fct(static_cast(pOwner), p1, p2, p3, p4, p5, p6, p7, p8); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + template + class QxFunction_8 : public IxFunction + { + + public: + typedef std::function type_fct; + typedef typename qx::trait::remove_attr::type type_P1; + typedef typename qx::trait::remove_attr::type type_P2; + typedef typename qx::trait::remove_attr::type type_P3; + typedef typename qx::trait::remove_attr::type type_P4; + typedef typename qx::trait::remove_attr::type type_P5; + typedef typename qx::trait::remove_attr::type type_P6; + typedef typename qx::trait::remove_attr::type type_P7; + typedef typename qx::trait::remove_attr::type type_P8; + QX_FUNCTION_CLASS_FCT(QxFunction_8); + + virtual int getParamCount() const { return 8; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_8 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + QX_FUNCTION_FETCH_PARAM(type_P6, p6, get_param_6); + QX_FUNCTION_FETCH_PARAM(type_P7, p7, get_param_7); + QX_FUNCTION_FETCH_PARAM(type_P8, p8, get_param_8); + try + { + pThis->m_fct(p1, p2, p3, p4, p5, p6, p7, p8); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_8 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + QX_FUNCTION_FETCH_PARAM(type_P6, p6, get_param_6); + QX_FUNCTION_FETCH_PARAM(type_P7, p7, get_param_7); + QX_FUNCTION_FETCH_PARAM(type_P8, p8, get_param_8); + try + { + R retTmp = pThis->m_fct(p1, p2, p3, p4, p5, p6, p7, p8); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + namespace function + { + + template + IxFunction_ptr bind_fct_8(const typename QxFunction_8::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(qx_verify_owner_tmp::value, "qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + template + IxFunction_ptr bind_member_fct_8(const typename QxFunction_8::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(!qx_verify_owner_tmp::value, "! qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + } // namespace function +} // namespace qx + +#endif // _QX_FUNCTION_8_H_ diff --git a/include/QxFunction/QxFunction_9.h b/include/QxFunction/QxFunction_9.h new file mode 100644 index 0000000..c04faa9 --- /dev/null +++ b/include/QxFunction/QxFunction_9.h @@ -0,0 +1,242 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_FUNCTION_9_H_ +#define _QX_FUNCTION_9_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxFunction_9.h + * \author XDL Team + * \ingroup QxFunction + * \brief Concrete function class registered into QxOrm context with 9 parameters + */ + +#include +#include + +namespace qx +{ + + /*! + * \ingroup QxFunction + * \brief qx::QxFunction_9 : concrete function registered into QxOrm context defined into class Owner, returning an object of type R and with 9 parameters P1, P2, P3, P4, P5, P6, P7, P8, P9 + */ + template + class QxFunction_9 : public IxFunction + { + + public: + typedef std::function type_fct; + typedef typename qx::trait::remove_attr::type type_P1; + typedef typename qx::trait::remove_attr::type type_P2; + typedef typename qx::trait::remove_attr::type type_P3; + typedef typename qx::trait::remove_attr::type type_P4; + typedef typename qx::trait::remove_attr::type type_P5; + typedef typename qx::trait::remove_attr::type type_P6; + typedef typename qx::trait::remove_attr::type type_P7; + typedef typename qx::trait::remove_attr::type type_P8; + typedef typename qx::trait::remove_attr::type type_P9; + QX_FUNCTION_CLASS_MEMBER_FCT(QxFunction_9); + + virtual int getParamCount() const { return 9; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_9 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + QX_FUNCTION_FETCH_PARAM(type_P6, p6, get_param_6); + QX_FUNCTION_FETCH_PARAM(type_P7, p7, get_param_7); + QX_FUNCTION_FETCH_PARAM(type_P8, p8, get_param_8); + QX_FUNCTION_FETCH_PARAM(type_P9, p9, get_param_9); + try + { + pThis->m_fct(static_cast(pOwner), p1, p2, p3, p4, p5, p6, p7, p8, p9); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(void *pOwner, const T ¶ms, qx::any *ret, const QxFunction_9 *pThis) + { + QX_FUNCTION_INVOKE_START_WITH_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + QX_FUNCTION_FETCH_PARAM(type_P6, p6, get_param_6); + QX_FUNCTION_FETCH_PARAM(type_P7, p7, get_param_7); + QX_FUNCTION_FETCH_PARAM(type_P8, p8, get_param_8); + QX_FUNCTION_FETCH_PARAM(type_P9, p9, get_param_9); + try + { + R retTmp = pThis->m_fct(static_cast(pOwner), p1, p2, p3, p4, p5, p6, p7, p8, p9); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + template + class QxFunction_9 : public IxFunction + { + + public: + typedef std::function type_fct; + typedef typename qx::trait::remove_attr::type type_P1; + typedef typename qx::trait::remove_attr::type type_P2; + typedef typename qx::trait::remove_attr::type type_P3; + typedef typename qx::trait::remove_attr::type type_P4; + typedef typename qx::trait::remove_attr::type type_P5; + typedef typename qx::trait::remove_attr::type type_P6; + typedef typename qx::trait::remove_attr::type type_P7; + typedef typename qx::trait::remove_attr::type type_P8; + typedef typename qx::trait::remove_attr::type type_P9; + QX_FUNCTION_CLASS_FCT(QxFunction_9); + + virtual int getParamCount() const { return 9; } + virtual qx_bool isValidParams(const QString ¶ms) const + { + Q_UNUSED(params); + return true; + } + virtual qx_bool isValidParams(const type_any_params ¶ms) const + { + Q_UNUSED(params); + return true; + } + + private: + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_9 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + QX_FUNCTION_FETCH_PARAM(type_P6, p6, get_param_6); + QX_FUNCTION_FETCH_PARAM(type_P7, p7, get_param_7); + QX_FUNCTION_FETCH_PARAM(type_P8, p8, get_param_8); + QX_FUNCTION_FETCH_PARAM(type_P9, p9, get_param_9); + try + { + pThis->m_fct(p1, p2, p3, p4, p5, p6, p7, p8, p9); + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + + template + struct QxInvokerFct + { + static inline qx_bool invoke(const T ¶ms, qx::any *ret, const QxFunction_9 *pThis) + { + QX_FUNCTION_INVOKE_START_WITHOUT_OWNER(); + QX_FUNCTION_FETCH_PARAM(type_P1, p1, get_param_1); + QX_FUNCTION_FETCH_PARAM(type_P2, p2, get_param_2); + QX_FUNCTION_FETCH_PARAM(type_P3, p3, get_param_3); + QX_FUNCTION_FETCH_PARAM(type_P4, p4, get_param_4); + QX_FUNCTION_FETCH_PARAM(type_P5, p5, get_param_5); + QX_FUNCTION_FETCH_PARAM(type_P6, p6, get_param_6); + QX_FUNCTION_FETCH_PARAM(type_P7, p7, get_param_7); + QX_FUNCTION_FETCH_PARAM(type_P8, p8, get_param_8); + QX_FUNCTION_FETCH_PARAM(type_P9, p9, get_param_9); + try + { + R retTmp = pThis->m_fct(p1, p2, p3, p4, p5, p6, p7, p8, p9); + if (ret) + { + (*ret) = qx::any(retTmp); + } + } + QX_FUNCTION_CATCH_AND_RETURN_INVOKE(); + } + }; + }; + + namespace function + { + + template + IxFunction_ptr bind_fct_9(const typename QxFunction_9::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(qx_verify_owner_tmp::value, "qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + template + IxFunction_ptr bind_member_fct_9(const typename QxFunction_9::type_fct &fct) + { + typedef std::is_same qx_verify_owner_tmp; + static_assert(!qx_verify_owner_tmp::value, "! qx_verify_owner_tmp::value"); + IxFunction_ptr ptr = std::make_shared>(fct); + return ptr; + } + + } // namespace function +} // namespace qx + +#endif // _QX_FUNCTION_9_H_ diff --git a/include/QxFunction/QxParameters.h b/include/QxFunction/QxParameters.h new file mode 100644 index 0000000..d86a72c --- /dev/null +++ b/include/QxFunction/QxParameters.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_PARAMETERS_H_ +#define _QX_PARAMETERS_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxParameters.h + * \author XDL Team + * \ingroup QxFunction + * \brief qx::function::detail::get_param_X() : provide some helper functions to retrieve parameters for all qx::IxFunction registered into QxOrm context + */ + +#include + +namespace qx +{ + namespace function + { + namespace detail + { + + template + inline qx_bool get_param_1(const T ¶ms, P &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_ANY(1); } + + template + inline qx_bool get_param_2(const T ¶ms, P &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_ANY(2); } + + template + inline qx_bool get_param_3(const T ¶ms, P &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_ANY(3); } + + template + inline qx_bool get_param_4(const T ¶ms, P &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_ANY(4); } + + template + inline qx_bool get_param_5(const T ¶ms, P &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_ANY(5); } + + template + inline qx_bool get_param_6(const T ¶ms, P &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_ANY(6); } + + template + inline qx_bool get_param_7(const T ¶ms, P &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_ANY(7); } + + template + inline qx_bool get_param_8(const T ¶ms, P &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_ANY(8); } + + template + inline qx_bool get_param_9(const T ¶ms, P &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_ANY(9); } + + template + inline qx_bool get_param_1(const QString ¶ms, P &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_STRING(1); } + + template + inline qx_bool get_param_2(const QString ¶ms, P &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_STRING(2); } + + template + inline qx_bool get_param_3(const QString ¶ms, P &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_STRING(3); } + + template + inline qx_bool get_param_4(const QString ¶ms, P &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_STRING(4); } + + template + inline qx_bool get_param_5(const QString ¶ms, P &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_STRING(5); } + + template + inline qx_bool get_param_6(const QString ¶ms, P &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_STRING(6); } + + template + inline qx_bool get_param_7(const QString ¶ms, P &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_STRING(7); } + + template + inline qx_bool get_param_8(const QString ¶ms, P &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_STRING(8); } + + template + inline qx_bool get_param_9(const QString ¶ms, P &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_STRING(9); } + + template <> + inline qx_bool get_param_1(const QString ¶ms, QString &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_STRING_TO_QSTRING(1); } + + template <> + inline qx_bool get_param_2(const QString ¶ms, QString &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_STRING_TO_QSTRING(2); } + + template <> + inline qx_bool get_param_3(const QString ¶ms, QString &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_STRING_TO_QSTRING(3); } + + template <> + inline qx_bool get_param_4(const QString ¶ms, QString &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_STRING_TO_QSTRING(4); } + + template <> + inline qx_bool get_param_5(const QString ¶ms, QString &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_STRING_TO_QSTRING(5); } + + template <> + inline qx_bool get_param_6(const QString ¶ms, QString &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_STRING_TO_QSTRING(6); } + + template <> + inline qx_bool get_param_7(const QString ¶ms, QString &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_STRING_TO_QSTRING(7); } + + template <> + inline qx_bool get_param_8(const QString ¶ms, QString &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_STRING_TO_QSTRING(8); } + + template <> + inline qx_bool get_param_9(const QString ¶ms, QString &p, const qx::IxFunction *qx_fct) { QX_FUNCTION_GET_PARAM_TYPE_STRING_TO_QSTRING(9); } + + } // namespace detail + } // namespace function +} // namespace qx + +#endif // _QX_PARAMETERS_H_ diff --git a/include/QxHttpServer/QxHttpCookie.h b/include/QxHttpServer/QxHttpCookie.h new file mode 100644 index 0000000..67c712e --- /dev/null +++ b/include/QxHttpServer/QxHttpCookie.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _QX_HTTP_COOKIE_H_ +#define _QX_HTTP_COOKIE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxHttpCookie.h + * \author XDL Team + * \ingroup QxHttpServer + * \brief HTTP cookie (https://www.qxorm.com/qxorm_en/manual.html#manual_999) + */ + +namespace qx +{ + + /*! + * \ingroup QxHttpServer + * \brief qx::QxHttpCookie : HTTP cookie (https://www.qxorm.com/qxorm_en/manual.html#manual_999) + */ + struct QX_DLL_EXPORT QxHttpCookie + { + + QByteArray name; //!< Cookie name + QByteArray value; //!< Cookie value + QByteArray domain; //!< Cookie domain : specifies those hosts to which the cookie will be sent, if not specified defaults to the host portion of the current document location (but not including subdomains) + QByteArray path; //!< Cookie path : indicates a URL path that must exist in the requested resource before sending the Cookie header + qlonglong maxAge; //!< Cookie max-age : number of seconds until the cookie expires (a zero or negative number will expire the cookie immediately) + bool secure; //!< Cookie secure : a secure cookie will only be sent to the server when a request is made using SSL and the HTTPS protocol + bool httpOnly; //!< Cookie http-only : HTTP-only cookies aren't accessible via JavaScript through the Document.cookie property, the XMLHttpRequest and Request APIs to mitigate attacks against cross-site scripting + + QxHttpCookie(); + QxHttpCookie(const QByteArray &name_, const QByteArray &value_, const QByteArray &domain_ = QByteArray(), const QByteArray &path_ = QByteArray("/"), qlonglong maxAge_ = 0, bool secure_ = false, bool httpOnly_ = false); + ~QxHttpCookie(); + + QByteArray toString() const; + static QHash parse(const QByteArray &cookies); + }; + +} // namespace qx + +#endif // _QX_HTTP_COOKIE_H_ +#endif // _QX_ENABLE_QT_NETWORK diff --git a/include/QxHttpServer/QxHttpRequest.h b/include/QxHttpServer/QxHttpRequest.h new file mode 100644 index 0000000..773a08c --- /dev/null +++ b/include/QxHttpServer/QxHttpRequest.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _QX_HTTP_REQUEST_H_ +#define _QX_HTTP_REQUEST_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxHttpRequest.h + * \author XDL Team + * \ingroup QxHttpServer + * \brief HTTP request (headers + body) + */ + +#include + +#include + +namespace qx +{ + + class QxHttpTransaction; + + /*! + * \ingroup QxHttpServer + * \brief qx::QxHttpRequest : HTTP request (headers + body) + */ + class QX_DLL_EXPORT QxHttpRequest + { + + private: + struct QxHttpRequestImpl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + + public: + QxHttpRequest(QxHttpTransaction *transaction); + virtual ~QxHttpRequest(); + + QUrl &url(); + QString &command(); + QString &version(); + QByteArray &data(); + QHash &headers(); + QByteArray header(const QByteArray &key); + QHash &cookies(); + QxHttpCookie cookie(const QByteArray &name); + QHash &dispatchParams(); + QHash ¶ms(); + QString param(const QString &key); + QString &sourceAddress(); + long &sourcePort(); + QString guid() const; + }; + + typedef std::shared_ptr QxHttpRequest_ptr; + +} // namespace qx + +typedef qx::QxHttpRequest qx_http_request; + +#endif // _QX_HTTP_REQUEST_H_ +#endif // _QX_ENABLE_QT_NETWORK diff --git a/include/QxHttpServer/QxHttpResponse.h b/include/QxHttpServer/QxHttpResponse.h new file mode 100644 index 0000000..4f4fa75 --- /dev/null +++ b/include/QxHttpServer/QxHttpResponse.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _QX_HTTP_RESPONSE_H_ +#define _QX_HTTP_RESPONSE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxHttpResponse.h + * \author XDL Team + * \ingroup QxHttpServer + * \brief HTTP response (headers + body) + */ + +#include + +#ifndef Q_MOC_RUN +#include +#endif // Q_MOC_RUN + +namespace qx +{ + + class QxHttpTransaction; + + /*! + * \ingroup QxHttpServer + * \brief qx::QxHttpResponse : HTTP response (headers + body) + */ + class QX_DLL_EXPORT QxHttpResponse + { + + private: + struct QxHttpResponseImpl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + + public: + QxHttpResponse(QxHttpTransaction *transaction); + virtual ~QxHttpResponse(); + + int &status(); + QByteArray &data(); + QByteArray statusDesc(); + QHash &headers(); + QByteArray header(const QByteArray &key); + QHash &cookies(); + QxHttpCookie cookie(const QByteArray &name); + qx_bool writeChunked(const QByteArray &data); + bool isChunked() const; + }; + + typedef std::shared_ptr QxHttpResponse_ptr; + +} // namespace qx + +typedef qx::QxHttpResponse qx_http_response; + +#endif // _QX_HTTP_RESPONSE_H_ +#endif // _QX_ENABLE_QT_NETWORK diff --git a/include/QxHttpServer/QxHttpServer.h b/include/QxHttpServer/QxHttpServer.h new file mode 100644 index 0000000..43447e7 --- /dev/null +++ b/include/QxHttpServer/QxHttpServer.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef Q_MOC_RUN +#include // Need to include this file for the 'moc' process +#endif // Q_MOC_RUN + +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _QX_HTTP_SERVER_H_ +#define _QX_HTTP_SERVER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxHttpServer.h + * \author XDL Team + * \ingroup QxHttpServer + * \brief HTTP server which manages connections in a multi-threaded environment (support SSL/TLS, persistent connection, etc...) : https://www.qxorm.com/qxorm_en/manual.html#manual_96 + */ + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +#include +#include +#include + +#ifndef Q_MOC_RUN +#include +#include +#include +#endif // Q_MOC_RUN + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +class QAbstractEventDispatcher; +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + +namespace qx +{ + + /*! + * \ingroup QxHttpServer + * \brief qx::QxHttpServer : HTTP server which manages connections in a multi-threaded environment (support SSL/TLS, persistent connection, etc...) : https://www.qxorm.com/qxorm_en/manual.html#manual_96 + */ + class QX_DLL_EXPORT QxHttpServer : public QObject + { + + Q_OBJECT + + public: + typedef std::function type_fct_custom_request_handler; + + private: + struct QxHttpServerImpl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + + public: + QxHttpServer(QObject *parent = NULL); + virtual ~QxHttpServer(); + + Q_INVOKABLE void startServer(); + Q_INVOKABLE void stopServer(); + + void setCustomRequestHandler(type_fct_custom_request_handler fct); + QxHttpServer &dispatch(const QString &command, const QString &path, type_fct_custom_request_handler fct, long position = -1); + void beforeDispatching(type_fct_custom_request_handler fct); + void afterDispatching(type_fct_custom_request_handler fct); + void clearDispatcher(); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + void setEventDispatcher(QAbstractEventDispatcher *pEventDispatcher); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + + static void buildResponseStaticFile(qx::QxHttpRequest &request, qx::QxHttpResponse &response, const QString &serverPath, qlonglong chunkedSize = 0); + static void buildResponseQxRestApi(qx::QxHttpRequest &request, qx::QxHttpResponse &response); + + Q_SIGNALS: + + void error(const QString &err, qx::QxHttpTransaction_ptr transaction); + void transactionStarted(qx::QxHttpTransaction_ptr transaction); + void transactionFinished(qx::QxHttpTransaction_ptr transaction); + void serverStatusChanged(bool bIsRunning); + + private Q_SLOTS: + + void onError(const QString &err, qx::service::QxTransaction_ptr transaction); + void onServerIsRunning(bool bIsRunning, qx::service::QxServer *pServer); + void onTransactionStarted(qx::service::QxTransaction_ptr transaction); + void onTransactionFinished(qx::service::QxTransaction_ptr transaction); + void onCustomRequestHandler(qx::service::QxTransaction_ptr transaction); + }; + + typedef std::shared_ptr QxHttpServer_ptr; + +} // namespace qx + +typedef qx::QxHttpServer qx_http_server; + +#endif // _QX_HTTP_SERVER_H_ +#endif // _QX_ENABLE_QT_NETWORK diff --git a/include/QxHttpServer/QxHttpSession.h b/include/QxHttpServer/QxHttpSession.h new file mode 100644 index 0000000..2859605 --- /dev/null +++ b/include/QxHttpServer/QxHttpSession.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _QX_HTTP_SESSION_H_ +#define _QX_HTTP_SESSION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxHttpSession.h + * \author XDL Team + * \ingroup QxHttpServer + * \brief HTTP session (https://www.qxorm.com/qxorm_en/manual.html#manual_998) + */ + +namespace qx +{ + + class QxHttpSessionManager; + + /*! + * \ingroup QxHttpServer + * \brief qx::QxHttpSession : HTTP session (https://www.qxorm.com/qxorm_en/manual.html#manual_998) + */ + class QX_DLL_EXPORT QxHttpSession + { + + friend class qx::QxHttpSessionManager; + + private: + struct QxHttpSessionImpl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + +#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) + public: // Some older compilers don't support std::shared_ptr with private custom deleter, but for Qt5 we assume that all compilers should support it +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) + + QxHttpSession(); + ~QxHttpSession(); + + public: + QByteArray id(); + QDateTime lastAccess(); + void lastAccess(const QDateTime &dt); + QVariant get(const QByteArray &key); + void set(const QByteArray &key, const QVariant &value); + QHash getAll(); + void remove(const QByteArray &key); + void clear(); + }; + + typedef std::shared_ptr QxHttpSession_ptr; + +} // namespace qx + +typedef qx::QxHttpSession qx_http_session; +typedef qx::QxHttpSession_ptr qx_http_session_ptr; + +#endif // _QX_HTTP_SESSION_H_ +#endif // _QX_ENABLE_QT_NETWORK diff --git a/include/QxHttpServer/QxHttpSessionManager.h b/include/QxHttpServer/QxHttpSessionManager.h new file mode 100644 index 0000000..7095dc8 --- /dev/null +++ b/include/QxHttpServer/QxHttpSessionManager.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef Q_MOC_RUN +#include // Need to include this file for the 'moc' process +#endif // Q_MOC_RUN + +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _QX_HTTP_SESSION_MANAGER_H_ +#define _QX_HTTP_SESSION_MANAGER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxHttpSessionManager.h + * \author XDL Team + * \ingroup QxHttpServer + * \brief HTTP session manager (https://www.qxorm.com/qxorm_en/manual.html#manual_998) + */ + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +#ifndef Q_MOC_RUN +#include +#endif // Q_MOC_RUN + +#include +#include +#include + +namespace qx +{ + + /*! + * \ingroup QxHttpServer + * \brief qx::QxHttpSessionManager : HTTP session manager (https://www.qxorm.com/qxorm_en/manual.html#manual_998) + */ + class QX_DLL_EXPORT QxHttpSessionManager : public QObject, public qx::QxSingleton + { + + Q_OBJECT + friend class qx::QxSingleton; + + private: + struct QxHttpSessionManagerImpl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + + QxHttpSessionManager(); + virtual ~QxHttpSessionManager(); + + public: + static qx::QxHttpSession_ptr getSession(qx::QxHttpRequest &request, qx::QxHttpResponse &response, const QByteArray &cookieName = QByteArray("qx_session_id"), bool autoCreateSession = true); + static qx::QxHttpSession_ptr createSession(qx::QxHttpRequest &request, qx::QxHttpResponse &response, const QByteArray &cookieName = QByteArray("qx_session_id")); + static void removeSession(qx::QxHttpRequest &request, qx::QxHttpResponse &response, const QByteArray &cookieName = QByteArray("qx_session_id")); + + private Q_SLOTS: + + void onCheckSessionTimeOut(); + + private: + static void deleteSession(qx::QxHttpSession *p); + }; + +} // namespace qx + +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::QxHttpSessionManager) + +#endif // _QX_HTTP_SESSION_MANAGER_H_ +#endif // _QX_ENABLE_QT_NETWORK diff --git a/include/QxHttpServer/QxHttpTransaction.h b/include/QxHttpServer/QxHttpTransaction.h new file mode 100644 index 0000000..81a4788 --- /dev/null +++ b/include/QxHttpServer/QxHttpTransaction.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef Q_MOC_RUN +#include // Need to include this file for the 'moc' process +#endif // Q_MOC_RUN + +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _QX_HTTP_TRANSACTION_H_ +#define _QX_HTTP_TRANSACTION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxHttpTransaction.h + * \author XDL Team + * \ingroup QxHttpServer + * \brief HTTP transaction (contains request from client and reply from server) + */ + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +#include + +#include +#include + +namespace qx +{ + + /*! + * \ingroup QxHttpServer + * \brief qx::QxHttpTransaction : HTTP transaction (contains request from client and reply from server) + */ + class QX_DLL_EXPORT QxHttpTransaction : public qx::service::QxTransaction + { + + Q_OBJECT + + private: + struct QxHttpTransactionImpl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + + public: + QxHttpTransaction(); + virtual ~QxHttpTransaction(); + + qx::QxHttpRequest &request(); + qx::QxHttpResponse &response(); + qx_bool writeChunked(const QByteArray &data); + + virtual void clear(); + virtual void executeServer(); + virtual qx_bool writeSocketServer(QTcpSocket &socket); + virtual qx_bool readSocketServer(QTcpSocket &socket); + }; + + typedef std::shared_ptr QxHttpTransaction_ptr; + +} // namespace qx + +typedef qx::QxHttpTransaction qx_http_transaction; + +#endif // _QX_HTTP_TRANSACTION_H_ +#endif // _QX_ENABLE_QT_NETWORK diff --git a/include/QxMemLeak.h b/include/QxMemLeak.h new file mode 100644 index 0000000..9264b46 --- /dev/null +++ b/include/QxMemLeak.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_MEM_LEAK_H_ +#define _QX_MEM_LEAK_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxMemLeak.h + * \author Wu Yongwei + * \ingroup QxMemLeak + * \brief QxOrm library memory leak detection (by Wu Yongwei) + * + * QxMemLeak module provides a fast detection of memory leaks in Debug mode once the execution of the program is finished (with indication of the file and the line => MFC-style from Microsoft).
    + * This module is developed by Wu Yongwei and has undergone some modifications to be integrated into QxOrm library.
    + * If another tool is already used in your projects (Valgrind for example), this functionality should not be activated.
    + * To enable/disable QxMemLeak module, all is needed is to modify the constant _QX_USE_MEM_LEAK_DETECTION defined in the QxConfig.h file.
    + * A recompilation of QxOrm library is necessary to take into account this modification. + */ + +#include + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#if _QX_INCLUDE_BOOST_SERIALIZE_EXPORT_HPP_INTO_QX_MEM_LEAK_HPP +#if _QX_USE_MODIFY_BOOST_SERIALIZATION_EXPORT_HPP +#include +#else +#include +#endif // _QX_USE_MODIFY_BOOST_SERIALIZATION_EXPORT_HPP +#endif // _QX_INCLUDE_BOOST_SERIALIZE_EXPORT_HPP_INTO_QX_MEM_LEAK_HPP +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#endif // _QX_MEM_LEAK_H_ diff --git a/include/QxMemLeak/bool_array.h b/include/QxMemLeak/bool_array.h new file mode 100644 index 0000000..52e1fdf --- /dev/null +++ b/include/QxMemLeak/bool_array.h @@ -0,0 +1,244 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2008 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/*! + * \file bool_array.h + * \ingroup QxMemLeak + * + * Header file for class bool_array (packed boolean array). + * + * \version 3.1, 2005/08/25 + * \author Wu Yongwei + * + */ + +#ifndef QT_NO_DEBUG +#ifndef _QX_MODE_RELEASE +#if _QX_USE_MEM_LEAK_DETECTION + +#ifndef _BOOL_ARRAY_H +#define _BOOL_ARRAY_H + +#ifdef _MSC_VER +#pragma once +#endif + +#include // assert +#include // exit, free, and NULL +#include // std::bad_alloc +#include // std::out_of_range +#include // for exception constructors + +namespace qx { +namespace memory { + +#ifndef _BYTE_DEFINED +#define _BYTE_DEFINED +typedef unsigned char BYTE; +#endif // !_BYTE_DEFINED + +/** + * Class to represent a packed boolean array. + * + * This was first written in April 1995, before I knew of any existing + * implementation of this kind of classes. Of course, the C++ Standard + * Template Library now demands an implementation of packed boolean + * array as `vector<bool>', but the code here should still be useful + * for the following three reasons: (1) STL support of MSVC 6 did not + * implement this specialization (nor did it have a `bit_vector'); (2) I + * incorporated some useful member functions from the STL bitset into + * this `bool_array', including `reset', `set', `flip', and `count'; + * (3) In my tests under MSVC 6 and GCC 2.95.3/3.2.3 my code is really + * FASTER than vector<bool> or the normal boolean array. + */ +class QX_DLL_EXPORT bool_array +{ + /** Class to represent a reference to an array element. */ + class QX_DLL_EXPORT _Element + { + public: + _Element(BYTE* __ptr, unsigned long __idx); + bool operator=(bool ___value); + operator bool() const; + private: + BYTE* _M_byte_ptr; + size_t _M_byte_idx; + size_t _M_bit_idx; + }; + +public: + bool_array() : _M_byte_ptr(NULL), _M_length(0) {} + explicit bool_array(unsigned long __size); + ~bool_array() { if (_M_byte_ptr != NULL) free(_M_byte_ptr); } + + bool create(unsigned long __size); + void initialize(bool ___value); + + // Using unsigned type here can increase performance! + _Element operator[](unsigned long __idx); + bool at(unsigned long __idx) const; + void reset(unsigned long __idx); + void set(unsigned long __idx); + + unsigned long size() const { return _M_length; } + unsigned long count() const; + unsigned long count(unsigned long __beg, unsigned long __end) const; + void flip(); + +private: + BYTE* _M_byte_ptr; + unsigned long _M_length; + static BYTE _S_bit_count[256]; +}; + + +/* Inline functions */ + +/** + * Constructs a reference to an array element. + * + * @param __ptr pointer to the interal boolean data + * @param __idx index of the array element to access + */ +inline bool_array::_Element::_Element(BYTE* __ptr, unsigned long __idx) +{ + _M_byte_ptr = __ptr; + _M_byte_idx = (size_t)(__idx / 8); + _M_bit_idx = (size_t)(__idx % 8); +} + +/** + * Assigns a new boolean value to an array element. + * + * @param ___value the new boolean value + * @return the assigned boolean value + */ +inline bool bool_array::_Element::operator=(bool ___value) +{ + if (___value) + *(_M_byte_ptr + _M_byte_idx) |= 1 << _M_bit_idx; + else + *(_M_byte_ptr + _M_byte_idx) &= ~(1 << _M_bit_idx); + return ___value; +} + +/** + * Reads the boolean value from an array element. + * + * @return the boolean value of the accessed array element + */ +inline bool_array::_Element::operator bool() const +{ + return *(_M_byte_ptr + _M_byte_idx) & (1 << _M_bit_idx) ? true : false; +} + +/** + * Constructs the packed boolean array with a specific size. + * + * @param __size size of the array + * @throw std::out_of_range if \a __size equals \c 0 + * @throw std::bad_alloc if memory is insufficient + */ +inline bool_array::bool_array(unsigned long __size) + : _M_byte_ptr(NULL), _M_length(0) +{ + if (__size == 0) + throw std::out_of_range("invalid bool_array size"); + + if (!create(__size)) + throw std::bad_alloc(); +} + +/** + * Creates a reference to an array element. + * + * @param __idx index of the array element to access + */ +inline bool_array::_Element bool_array::operator[](unsigned long __idx) +{ + assert(_M_byte_ptr); + assert(__idx < _M_length); + return _Element(_M_byte_ptr, __idx); +} + +/** + * Reads the boolean value of an array element via an index. + * + * @param __idx index of the array element to access + * @return the boolean value of the accessed array element + * @throw std::out_of_range when the index is too big + */ +inline bool bool_array::at(unsigned long __idx) const +{ + size_t __byte_idx, __bit_idx; + if (__idx >= _M_length) + throw std::out_of_range("invalid bool_array subscript"); + __byte_idx = (size_t)(__idx / 8); + __bit_idx = (size_t)(__idx % 8); + return *(_M_byte_ptr + __byte_idx) & (1 << __bit_idx) ? true : false; +} + +/** + * Resets an array element to \c false via an index. + * + * @param __idx index of the array element to access + * @throw std::out_of_range when the index is too big + */ +inline void bool_array::reset(unsigned long __idx) +{ + size_t __byte_idx, __bit_idx; + if (__idx >= _M_length) + throw std::out_of_range("invalid bool_array subscript"); + __byte_idx = (size_t)(__idx / 8); + __bit_idx = (size_t)(__idx % 8); + *(_M_byte_ptr + __byte_idx) &= ~(1 << __bit_idx); +} + +/** + * Sets an array element to \c true via an index. + * + * @param __idx index of the array element to access + * @throw std::out_of_range when the index is too big + */ +inline void bool_array::set(unsigned long __idx) +{ + size_t __byte_idx, __bit_idx; + if (__idx >= _M_length) + throw std::out_of_range("invalid bool_array subscript"); + __byte_idx = (size_t)(__idx / 8); + __bit_idx = (size_t)(__idx % 8); + *(_M_byte_ptr + __byte_idx) |= 1 << __bit_idx; +} + +} // namespace memory +} // namespace qx + +#endif // _BOOL_ARRAY_H +#endif // _QX_USE_MEM_LEAK_DETECTION +#endif // _QX_MODE_RELEASE +#endif // QT_NO_DEBUG diff --git a/include/QxMemLeak/class_level_lock.h b/include/QxMemLeak/class_level_lock.h new file mode 100644 index 0000000..5b93628 --- /dev/null +++ b/include/QxMemLeak/class_level_lock.h @@ -0,0 +1,139 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2008 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/*! + * \file class_level_lock.h + * \ingroup QxMemLeak + * + * In essence Loki ClassLevelLockable re-engineered to use a fast_mutex class. + * + * \version 1.13, 2007/12/30 + * \author Wu Yongwei + * + */ + +#ifndef QT_NO_DEBUG +#ifndef _QX_MODE_RELEASE +#if _QX_USE_MEM_LEAK_DETECTION + +#ifndef _CLASS_LEVEL_LOCK_H +#define _CLASS_LEVEL_LOCK_H + +#ifdef _MSC_VER +#pragma once +#endif + +#include "fast_mutex.h" + +namespace qx { +namespace memory { + +#ifdef _NOTHREADS + /** + * Helper class for class-level locking. This is the + * single-threaded implementation. + */ + template + class class_level_lock + { + public: + /** Type that provides locking/unlocking semantics. */ + class lock + { + public: + lock() {} + }; + + typedef _Host volatile_type; + }; +#else + /** + * Helper class for class-level locking. This is the multi-threaded + * implementation. The main departure from Loki ClassLevelLockable + * is that there is an additional template parameter which can make + * the lock not lock at all even in multi-threaded environments. + * See static_mem_pool.h for real usage. + */ + template + class class_level_lock + { + static fast_mutex _S_mtx; + + public: + class lock; + friend class lock; + + /** Type that provides locking/unlocking semantics. */ + class lock + { + lock(const lock&); + lock& operator=(const lock&); + public: + lock() + { + if (_RealLock) + _S_mtx.lock(); + } + ~lock() + { + if (_RealLock) + _S_mtx.unlock(); + } + }; + + typedef volatile _Host volatile_type; + }; + +#if HAVE_CLASS_TEMPLATE_PARTIAL_SPECIALIZATION + /** Partial specialization that makes null locking. */ + template + class class_level_lock<_Host, false> + { + public: + /** Type that provides locking/unlocking semantics. */ + class lock + { + public: + lock() {} + }; + + typedef _Host volatile_type; + }; +#endif // HAVE_CLASS_TEMPLATE_PARTIAL_SPECIALIZATION + + template + fast_mutex class_level_lock<_Host, _RealLock>::_S_mtx; +#endif // _NOTHREADS + +} // namespace memory +} // namespace qx + +#endif // _CLASS_LEVEL_LOCK_H +#endif // _QX_USE_MEM_LEAK_DETECTION +#endif // _QX_MODE_RELEASE +#endif // QT_NO_DEBUG diff --git a/include/QxMemLeak/cont_ptr_utils.h b/include/QxMemLeak/cont_ptr_utils.h new file mode 100644 index 0000000..790ded1 --- /dev/null +++ b/include/QxMemLeak/cont_ptr_utils.h @@ -0,0 +1,151 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2008 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/*! + * \file cont_ptr_utils.h + * \ingroup QxMemLeak + * + * Utility functors for containers of pointers (adapted from Scott + * Meyers' Effective STL). + * + * \version 1.4, 2007/09/12 + * \author Wu Yongwei + * + */ + +#ifndef QT_NO_DEBUG +#ifndef _QX_MODE_RELEASE +#if _QX_USE_MEM_LEAK_DETECTION + +#ifndef _CONT_PTR_UTILS_H +#define _CONT_PTR_UTILS_H + +#ifdef _MSC_VER +#pragma once +#endif + +namespace qx { +namespace memory { + +/** + * Functor to return objects pointed by a container of pointers. + * + * A typical usage might be like: + * @code + * vector v; + * ... + * transform(v.begin(), v.end(), + * ostream_iterator(cout, " "), + * dereference()); + * @endcode + */ +struct QX_DLL_EXPORT dereference +{ + template + const _Tp& operator()(const _Tp* __ptr) const + { + return *__ptr; + } +}; + +/** + * Functor to compare objects pointed by a container of pointers. + * + * @code + * vector v; + * ... + * sort(v.begin(), v.end(), dereference_less()); + * @endcode + * or + * @code + * set s; + * @endcode + */ +struct QX_DLL_EXPORT dereference_less +{ + template + bool operator()(_Pointer __ptr1, _Pointer __ptr2) const + { + return *__ptr1 < *__ptr2; + } +}; + +/** + * Functor to delete objects pointed by a container of pointers. + * + * A typical usage might be like: + * @code + * list l; + * ... + * for_each(l.begin(), l.end(), delete_object()); + * @endcode + */ +struct QX_DLL_EXPORT delete_object +{ + template + void operator()(_Pointer __ptr) const + { + delete __ptr; + } +}; + +/** + * Functor to output objects pointed by a container of pointers. + * + * A typical usage might be like: + * @code + * list l; + * ... + * for_each(l.begin(), l.end(), output_object(cout, " ")); + * @endcode + */ +template +struct output_object +{ + output_object(_OutputStrm& __outs, const _StringType& __sep) + : _M_outs(__outs), _M_sep(__sep) + {} + + template + void operator()(const _Tp* __ptr) const + { + _M_outs << *__ptr << _M_sep; + } + +private: + _OutputStrm& _M_outs; + _StringType _M_sep; +}; + +} // namespace memory +} // namespace qx + +#endif // _CONT_PTR_UTILS_H +#endif // _QX_USE_MEM_LEAK_DETECTION +#endif // _QX_MODE_RELEASE +#endif // QT_NO_DEBUG diff --git a/include/QxMemLeak/debug_new.h b/include/QxMemLeak/debug_new.h new file mode 100644 index 0000000..948db86 --- /dev/null +++ b/include/QxMemLeak/debug_new.h @@ -0,0 +1,220 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2008 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/*! + * \file debug_new.h + * \ingroup QxMemLeak + * + * Header file for checking leaks caused by unmatched new/delete. + * + * \version 4.4, 2007/12/31 + * \author Wu Yongwei + * + */ + +#ifndef QT_NO_DEBUG +#ifndef _QX_MODE_RELEASE +#if _QX_USE_MEM_LEAK_DETECTION + +#ifndef _DEBUG_NEW_H +#define _DEBUG_NEW_H + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include + +#include + +/** + * @def HAVE_PLACEMENT_DELETE + * + * Macro to indicate whether placement delete operators are supported on + * a certain compiler. Some compilers, like Borland C++ Compiler 5.5.1 + * and Digital Mars Compiler 8.42, do not support them, and the user + * must define this macro to \c 0 to make the program compile. Also + * note that in that case memory leakage will occur if an exception is + * thrown in the initialization (constructor) of a dynamically created + * object. + */ +#ifndef HAVE_PLACEMENT_DELETE +#define HAVE_PLACEMENT_DELETE 1 +#endif + +/** + * @def _DEBUG_NEW_REDEFINE_NEW + * + * Macro to indicate whether redefinition of \c new is wanted. If one + * wants to define one's own operator new, to call + * operator new directly, or to call placement \c new, it + * should be defined to \c 0 to alter the default behaviour. Unless, of + * course, one is willing to take the trouble to write something like: + * @code + * # ifdef new + * # define _NEW_REDEFINED + * # undef new + * # endif + * + * // Code that uses new is here + * + * # ifdef _NEW_REDEFINED + * # ifdef DEBUG_NEW + * # define new DEBUG_NEW + * # endif + * # undef _NEW_REDEFINED + * # endif + * @endcode + */ +#ifndef _DEBUG_NEW_REDEFINE_NEW +#define _DEBUG_NEW_REDEFINE_NEW 1 +#endif + +/** + * @def _DEBUG_NEW_CALLER_ADDRESS + * + * The expression to return the caller address. print_position will + * later on use this address to print the position information of memory + * operation points. + */ +#ifndef _DEBUG_NEW_CALLER_ADDRESS +#ifdef __GNUC__ +#define _DEBUG_NEW_CALLER_ADDRESS __builtin_return_address(0) +#else +#define _DEBUG_NEW_CALLER_ADDRESS NULL +#endif +#endif + +/** + * @def DEBUG_NEW + * + * Macro to catch file/line information on allocation. If + * #_DEBUG_NEW_REDEFINE_NEW is \c 0, one can use this macro directly; + * otherwise \c new will be defined to it, and one must use \c new + * instead. + */ +#define DEBUG_NEW qx::memory::__debug_new_recorder(__FILE__, __LINE__, __FUNCTION__) ->* new + +#if _DEBUG_NEW_REDEFINE_NEW +#define new DEBUG_NEW +#endif // _DEBUG_NEW_REDEFINE_NEW + +#ifdef _DEBUG_NEW_EMULATE_MALLOC +#include +#ifdef new +#define malloc(s) ((void*)(new char[s])) +#else // new +#define malloc(s) ((void*)(DEBUG_NEW char[s])) +#endif // new +#define free(p) delete[] (char*)(p) +#endif // _DEBUG_NEW_EMULATE_MALLOC + +QX_DLL_EXPORT void * operator new(size_t size, const char * file, int line); +QX_DLL_EXPORT void * operator new[](size_t size, const char * file, int line); + +#if HAVE_PLACEMENT_DELETE +QX_DLL_EXPORT void operator delete(void * pointer, const char * file, int line) throw(); +QX_DLL_EXPORT void operator delete[](void * pointer, const char * file, int line) throw(); +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1300 +// MSVC 6 requires the following declarations; or the non-placement +// new[]/delete[] will not compile. +QX_DLL_EXPORT void * operator new[](size_t) throw(std::bad_alloc); +QX_DLL_EXPORT void operator delete[](void *) throw(); +#endif + +namespace qx { +namespace memory { + +/* Prototypes */ +QX_DLL_EXPORT int check_leaks(); +QX_DLL_EXPORT int check_mem_corruption(); + +/* Control variables */ +extern bool new_autocheck_flag; // default to true: call check_leaks() on exit +extern bool new_verbose_flag; // default to false: no verbose information +extern FILE* new_output_fp; // default to stderr: output to console +extern const char* new_progname;// default to NULL; should be assigned argv[0] + +/** + * Recorder class to remember the call context. + * + * The idea comes from Greg Herlihy's post in comp.lang.c++.moderated. + */ +class QX_DLL_EXPORT __debug_new_recorder +{ + const char* _M_file; + const int _M_line; + const char* _M_fct; + void _M_process(void* pointer); +public: + /** + * Constructor to remember the call context. The information will + * be used in __debug_new_recorder::operator->*. + */ + __debug_new_recorder(const char* file, int line, const char* fct) + : _M_file(file), _M_line(line), _M_fct(fct) {} + /** + * Operator to write the context information to memory. + * operator->* is chosen because it has the right + * precedence, it is rarely used, and it looks good: so people can + * tell the special usage more quickly. + */ + template _Tp* operator->*(_Tp* pointer) { _M_process(pointer); return pointer; }; + static void free_pointer(void* pointer, void* addr, bool is_array); +private: + __debug_new_recorder(const __debug_new_recorder&); + __debug_new_recorder& operator=(const __debug_new_recorder&); +}; + +/** + * Counter class for on-exit leakage check. + * + * This technique is learnt from The C++ Programming Language by + * Bjarne Stroustup. + */ +class QX_DLL_EXPORT __debug_new_counter +{ + static int _S_count; +public: + __debug_new_counter(); + ~__debug_new_counter(); +}; + +/** Counting object for each file including debug_new.h. */ +static __debug_new_counter __debug_new_count; + +} // namespace memory +} // namespace qx + +#endif // _DEBUG_NEW_H +#endif // _QX_USE_MEM_LEAK_DETECTION +#endif // _QX_MODE_RELEASE +#endif // QT_NO_DEBUG diff --git a/include/QxMemLeak/fast_mutex.h b/include/QxMemLeak/fast_mutex.h new file mode 100644 index 0000000..57c8969 --- /dev/null +++ b/include/QxMemLeak/fast_mutex.h @@ -0,0 +1,367 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2008 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/*! + * \file fast_mutex.h + * \ingroup QxMemLeak + * + * A fast mutex implementation for POSIX and Win32. + * + * \version 1.18, 2005/05/06 + * \author Wu Yongwei + * + */ + +#ifndef QT_NO_DEBUG +#ifndef _QX_MODE_RELEASE +#if _QX_USE_MEM_LEAK_DETECTION + +#ifndef _FAST_MUTEX_H +#define _FAST_MUTEX_H + +#ifdef _MSC_VER +#pragma once +#endif + +# if !defined(_NOTHREADS) +# if !defined(_WIN32THREADS) && \ + (defined(_WIN32) && defined(_MT)) +// Automatically use _WIN32THREADS when specifying -MT/-MD in MSVC, +// or -mthreads in MinGW GCC. +# define _WIN32THREADS +# elif !defined(_PTHREADS) && \ + defined(_REENTRANT) +// Automatically use _PTHREADS when specifying -pthread in GCC. +// N.B. I do not detect on _PTHREAD_H since libstdc++-v3 under +// Linux will silently include anyway. +# define _PTHREADS +# endif +# endif + +# if !defined(_PTHREADS) && !defined(_WIN32THREADS) && !defined(_NOTHREADS) +# define _NOTHREADS +# endif + +# if defined(_NOTHREADS) +# if defined(_PTHREADS) || defined(_WIN32THREADS) +# undef _NOTHREADS +# error "Cannot define multi-threaded mode with -D_NOTHREADS" +# if defined(__MINGW32__) && defined(_WIN32THREADS) && !defined(_MT) +# error "Be sure to specify -mthreads with -D_WIN32THREADS" +# endif +# endif +# endif + +# ifndef _FAST_MUTEX_CHECK_INITIALIZATION +/** + * Macro to control whether to check for initialization status for each + * lock/unlock operation. Defining it to a non-zero value will enable + * the check, so that the construction/destruction of a static object + * using a static fast_mutex not yet constructed or already destroyed + * will work (with lock/unlock operations ignored). Defining it to zero + * will disable to check. + */ +# define _FAST_MUTEX_CHECK_INITIALIZATION 1 +# endif + +# if defined(_PTHREADS) && defined(_WIN32THREADS) +// Some C++ libraries have _PTHREADS defined even on Win32 platforms. +// Thus this hack. +# undef _PTHREADS +# endif + +#ifndef _QX_MODE_RELEASE +#ifndef QT_NO_DEBUG +# include +# include +/** Macro for fast_mutex assertions. Real version (for debug mode). */ +# define _FAST_MUTEX_ASSERT(_Expr, _Msg) \ + if (!(_Expr)) { \ + fprintf(stderr, "fast_mutex::%s\n", _Msg); \ + abort(); \ + } +#else +/** Macro for fast_mutex assertions. Fake version (for release mode). */ +# define _FAST_MUTEX_ASSERT(_Expr, _Msg) \ + ((void)0) +#endif // QT_NO_DEBUG +#endif // _QX_MODE_RELEASE + +# ifdef _PTHREADS +# include +/** + * Macro alias to `volatile' semantics. Here it is truly volatile since + * it is in a multi-threaded (POSIX threads) environment. + */ +# define __VOLATILE volatile + /** + * Class for non-reentrant fast mutexes. This is the implementation + * for POSIX threads. + */ + namespace qx { + namespace memory { + class fast_mutex + { + pthread_mutex_t _M_mtx_impl; +# if _FAST_MUTEX_CHECK_INITIALIZATION + bool _M_initialized; +# endif +#ifndef _QX_MODE_RELEASE +#ifndef QT_NO_DEBUG + bool _M_locked; +#endif // QT_NO_DEBUG +#endif // _QX_MODE_RELEASE + public: + fast_mutex() +#ifndef _QX_MODE_RELEASE +#ifndef QT_NO_DEBUG + : _M_locked(false) +#endif // QT_NO_DEBUG +#endif // _QX_MODE_RELEASE + { + ::pthread_mutex_init(&_M_mtx_impl, NULL); +# if _FAST_MUTEX_CHECK_INITIALIZATION + _M_initialized = true; +# endif + } + ~fast_mutex() + { + _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked"); +# if _FAST_MUTEX_CHECK_INITIALIZATION + _M_initialized = false; +# endif + ::pthread_mutex_destroy(&_M_mtx_impl); + } + void lock() + { +# if _FAST_MUTEX_CHECK_INITIALIZATION + if (!_M_initialized) + return; +# endif + ::pthread_mutex_lock(&_M_mtx_impl); +#ifndef _QX_MODE_RELEASE +#ifndef QT_NO_DEBUG + // The following assertion should _always_ be true for a + // real `fast' pthread_mutex. However, this assertion can + // help sometimes, when people forget to use `-lpthread' and + // glibc provides an empty implementation. Having this + // assertion is also more consistent. + _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked"); + _M_locked = true; +#endif // QT_NO_DEBUG +#endif // _QX_MODE_RELEASE + } + void unlock() + { +# if _FAST_MUTEX_CHECK_INITIALIZATION + if (!_M_initialized) + return; +# endif +#ifndef _QX_MODE_RELEASE +#ifndef QT_NO_DEBUG + _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked"); + _M_locked = false; +#endif // QT_NO_DEBUG +#endif // _QX_MODE_RELEASE + ::pthread_mutex_unlock(&_M_mtx_impl); + } + private: + fast_mutex(const fast_mutex&); + fast_mutex& operator=(const fast_mutex&); + }; + } // namespace memory + } // namespace qx +# endif // _PTHREADS + +# ifdef _WIN32THREADS +# include +/** + * Macro alias to `volatile' semantics. Here it is truly volatile since + * it is in a multi-threaded (Win32 threads) environment. + */ +# define __VOLATILE volatile + /** + * Class for non-reentrant fast mutexes. This is the implementation + * for Win32 threads. + */ + namespace qx { + namespace memory { + class fast_mutex + { + CRITICAL_SECTION _M_mtx_impl; +# if _FAST_MUTEX_CHECK_INITIALIZATION + bool _M_initialized; +# endif +#ifndef _QX_MODE_RELEASE +#ifndef QT_NO_DEBUG + bool _M_locked; +#endif // QT_NO_DEBUG +#endif // _QX_MODE_RELEASE + public: + fast_mutex() +#ifndef _QX_MODE_RELEASE +#ifndef QT_NO_DEBUG + : _M_locked(false) +#endif // QT_NO_DEBUG +#endif // _QX_MODE_RELEASE + { + ::InitializeCriticalSection(&_M_mtx_impl); +# if _FAST_MUTEX_CHECK_INITIALIZATION + _M_initialized = true; +# endif + } + ~fast_mutex() + { + _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked"); +# if _FAST_MUTEX_CHECK_INITIALIZATION + _M_initialized = false; +# endif + ::DeleteCriticalSection(&_M_mtx_impl); + } + void lock() + { +# if _FAST_MUTEX_CHECK_INITIALIZATION + if (!_M_initialized) + return; +# endif + ::EnterCriticalSection(&_M_mtx_impl); +#ifndef _QX_MODE_RELEASE +#ifndef QT_NO_DEBUG + _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked"); + _M_locked = true; +#endif // QT_NO_DEBUG +#endif // _QX_MODE_RELEASE + } + void unlock() + { +# if _FAST_MUTEX_CHECK_INITIALIZATION + if (!_M_initialized) + return; +# endif +#ifndef _QX_MODE_RELEASE +#ifndef QT_NO_DEBUG + _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked"); + _M_locked = false; +#endif // QT_NO_DEBUG +#endif // _QX_MODE_RELEASE + ::LeaveCriticalSection(&_M_mtx_impl); + } + private: + fast_mutex(const fast_mutex&); + fast_mutex& operator=(const fast_mutex&); + }; + } // namespace memory + } // namespace qx +# endif // _WIN32THREADS + +# ifdef _NOTHREADS +/** + * Macro alias to `volatile' semantics. Here it is not truly volatile + * since it is in a single-threaded environment. + */ +# define __VOLATILE + /** + * Class for non-reentrant fast mutexes. This is the null + * implementation for single-threaded environments. + */ + namespace qx { + namespace memory { + class fast_mutex + { +#ifndef _QX_MODE_RELEASE +#ifndef QT_NO_DEBUG + bool _M_locked; +#endif // QT_NO_DEBUG +#endif // _QX_MODE_RELEASE + public: + fast_mutex() +#ifndef _QX_MODE_RELEASE +#ifndef QT_NO_DEBUG + : _M_locked(false) +#endif // QT_NO_DEBUG +#endif // _QX_MODE_RELEASE + { + } + ~fast_mutex() + { + _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked"); + } + void lock() + { +#ifndef _QX_MODE_RELEASE +#ifndef QT_NO_DEBUG + _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked"); + _M_locked = true; +#endif // QT_NO_DEBUG +#endif // _QX_MODE_RELEASE + } + void unlock() + { +#ifndef _QX_MODE_RELEASE +#ifndef QT_NO_DEBUG + _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked"); + _M_locked = false; +#endif // QT_NO_DEBUG +#endif // _QX_MODE_RELEASE + } + private: + fast_mutex(const fast_mutex&); + fast_mutex& operator=(const fast_mutex&); + }; + } // namespace memory + } // namespace qx +# endif // _NOTHREADS + +namespace qx { +namespace memory { + +/** An acquistion-on-initialization lock class based on fast_mutex. */ +class QX_DLL_EXPORT fast_mutex_autolock +{ + fast_mutex& _M_mtx; +public: + explicit fast_mutex_autolock(fast_mutex& __mtx) : _M_mtx(__mtx) + { + _M_mtx.lock(); + } + ~fast_mutex_autolock() + { + _M_mtx.unlock(); + } +private: + fast_mutex_autolock(const fast_mutex_autolock&); + fast_mutex_autolock& operator=(const fast_mutex_autolock&); +}; + +} // namespace memory +} // namespace qx + +#endif // _FAST_MUTEX_H +#endif // _QX_USE_MEM_LEAK_DETECTION +#endif // _QX_MODE_RELEASE +#endif // QT_NO_DEBUG diff --git a/include/QxMemLeak/fixed_mem_pool.h b/include/QxMemLeak/fixed_mem_pool.h new file mode 100644 index 0000000..e3c0791 --- /dev/null +++ b/include/QxMemLeak/fixed_mem_pool.h @@ -0,0 +1,330 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2008 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/*! + * \file fixed_mem_pool.h + * \ingroup QxMemLeak + * + * Definition of a fixed-size memory pool template for structs/classes. + * This is a easy-to-use class template for pre-allocated memory pools. + * The client side needs to do the following things: + * - Use one of the macros #DECLARE_FIXED_MEM_POOL, + * #DECLARE_FIXED_MEM_POOL__NOTHROW, and + * #DECLARE_FIXED_MEM_POOL__THROW_NOCHECK at the end of the class + * (say, \c class \e _Cls) definitions + * - Call fixed_mem_pool<_Cls>::initialize at the beginning of the + * program + * - Optionally, specialize fixed_mem_pool<_Cls>::bad_alloc_handler to + * change the behaviour when all memory blocks are allocated + * - Optionally, call fixed_mem_pool<_Cls>::deinitialize at exit of the + * program to check for memory leaks + * - Optionally, call fixed_mem_pool<_Cls>::get_alloc_count to check + * memory usage when the program is running + * + * \version 1.14, 2005/09/19 + * \author Wu Yongwei + * + */ + +#ifndef QT_NO_DEBUG +#ifndef _QX_MODE_RELEASE +#if _QX_USE_MEM_LEAK_DETECTION + +#ifndef _FIXED_MEM_POOL_H +#define _FIXED_MEM_POOL_H + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include "class_level_lock.h" +#include "mem_pool_base.h" + +/** + * Defines the alignment of memory blocks. + */ +#ifndef MEM_POOL_ALIGNMENT +#define MEM_POOL_ALIGNMENT 4 +#endif + +namespace qx { +namespace memory { + +/** + * Class template to manipulate a fixed-size memory pool. Please notice + * that only allocate and deallocate are protected by a lock. + * + * @param _Tp class to use the fixed_mem_pool + */ +template +class fixed_mem_pool +{ +public: + typedef typename class_level_lock >::lock lock; + static void* allocate(); + static void deallocate(void*); + static bool initialize(size_t __size); + static int deinitialize(); + static int get_alloc_count(); + static bool is_initialized(); +protected: + static bool bad_alloc_handler(); +private: + static size_t _S_align(size_t __size); + static void* _S_mem_pool_ptr; + static void* _S_first_avail_ptr; + static int _S_alloc_cnt; +}; + +/** Pointer to the allocated chunk of memory. */ +template +void* fixed_mem_pool<_Tp>::_S_mem_pool_ptr = NULL; + +/** Pointer to the first available memory block. */ +template +void* fixed_mem_pool<_Tp>::_S_first_avail_ptr = NULL; + +/** Count of allocations. */ +template +int fixed_mem_pool<_Tp>::_S_alloc_cnt = 0; + +/** + * Allocates a memory block from the memory pool. + * + * @return pointer to the allocated memory block + */ +template +inline void* fixed_mem_pool<_Tp>::allocate() +{ + lock __guard; + for (;;) + { + if (void* __result = _S_first_avail_ptr) + { + _S_first_avail_ptr = *(void**)_S_first_avail_ptr; + ++_S_alloc_cnt; + return __result; + } + else + if (!bad_alloc_handler()) + return NULL; + } +} + +/** + * Deallocates a memory block and returns it to the memory pool. + * + * @param __block_ptr pointer to the memory block to return + */ +template +inline void fixed_mem_pool<_Tp>::deallocate(void* __block_ptr) +{ + if (__block_ptr == NULL) + return; + lock __guard; + assert(_S_alloc_cnt != 0); + --_S_alloc_cnt; + *(void**)__block_ptr = _S_first_avail_ptr; + _S_first_avail_ptr = __block_ptr; +} + +/** + * Initializes the memory pool. + * + * @param __size number of memory blocks to put in the memory pool + * @return \c true if successful; \c false if memory insufficient + */ +template +bool fixed_mem_pool<_Tp>::initialize(size_t __size) +{ + size_t __block_size = _S_align(sizeof(_Tp)); + assert(!is_initialized()); + assert(__size > 0 && __block_size >= sizeof(void*)); + _S_mem_pool_ptr = mem_pool_base::alloc_sys(__size * __block_size); + _S_first_avail_ptr = _S_mem_pool_ptr; + if (_S_mem_pool_ptr == NULL) + return false; + char* __block_ = (char*)_S_mem_pool_ptr; + while (--__size != 0) + { + char* __next_ = __block_ + __block_size; + *(void**)__block_ = __next_; + __block_ = __next_; + } + *(void**)__block_ = NULL; + return true; +} + +/** + * Deinitializes the memory pool. + * + * @return \c 0 if all memory blocks are returned and the memory pool + * successfully freed; or a non-zero value indicating number of + * memory blocks still in allocation + */ +template +int fixed_mem_pool<_Tp>::deinitialize() +{ + if (_S_alloc_cnt != 0) + return _S_alloc_cnt; + assert(is_initialized()); + mem_pool_base::dealloc_sys(_S_mem_pool_ptr); + _S_mem_pool_ptr = NULL; + _S_first_avail_ptr = NULL; + return 0; +} + +/** + * Gets the allocation count. + * + * @return the number of memory blocks still in allocation + */ +template +inline int fixed_mem_pool<_Tp>::get_alloc_count() +{ + return _S_alloc_cnt; +} + +/** + * Is the memory pool initialized? + * + * @return \c true if it is successfully initialized; \c false otherwise + */ +template +inline bool fixed_mem_pool<_Tp>::is_initialized() +{ + return _S_mem_pool_ptr != NULL;; +} + +/** + * Bad allocation handler. Called when there are no memory blocks + * available in the memory pool. If this function returns \c false + * (default behaviour if not explicitly specialized), it indicates that + * it can do nothing and allocate() should return \c NULL; if this + * function returns \c true, it indicates that it has freed some memory + * blocks and allocate() should try allocating again. + */ +template +bool fixed_mem_pool<_Tp>::bad_alloc_handler() +{ + return false; +} + +/** + * Aligns the memory block size. + * + * @param __size size to be aligned + * @return aligned value of \a __size + */ +template +inline size_t fixed_mem_pool<_Tp>::_S_align(size_t __size) +{ + return (__size + MEM_POOL_ALIGNMENT - 1) + / MEM_POOL_ALIGNMENT * MEM_POOL_ALIGNMENT; +} + +} // namespace memory +} // namespace qx + +/** + * Declares the normal (exceptionable) overload of operator new + * and operator delete. + * + * @param _Cls class to use the fixed_mem_pool + * @see DECLARE_FIXED_MEM_POOL__THROW_NOCHECK, which, too, + * defines an operator new that will never return + * \c NULL, but requires more discipline on the + * programmer's side. + */ +#define DECLARE_FIXED_MEM_POOL(_Cls) \ +public: \ + static void* operator new(size_t __size) \ + { \ + assert(__size == sizeof(_Cls)); \ + if (void* __ptr = fixed_mem_pool<_Cls>::allocate()) \ + return __ptr; \ + else \ + throw std::bad_alloc(); \ + } \ + static void operator delete(void* __ptr) \ + { \ + if (__ptr != NULL) \ + fixed_mem_pool<_Cls>::deallocate(__ptr); \ + } + +/** + * Declares the non-exceptionable overload of operator new and + * operator delete. + * + * @param _Cls class to use the fixed_mem_pool + */ +#define DECLARE_FIXED_MEM_POOL__NOTHROW(_Cls) \ +public: \ + static void* operator new(size_t __size) throw() \ + { \ + assert(__size == sizeof(_Cls)); \ + return fixed_mem_pool<_Cls>::allocate(); \ + } \ + static void operator delete(void* __ptr) \ + { \ + if (__ptr != NULL) \ + fixed_mem_pool<_Cls>::deallocate(__ptr); \ + } + +/** + * Declares the exceptionable, non-checking overload of operator + * new and operator delete. + * + * N.B. Using this macro \e requires users to explicitly specialize + * fixed_mem_pool::bad_alloc_handler so that it shall never return + * \c false (it may throw exceptions, say, \c std::bad_alloc, or simply + * abort). Otherwise a segmentation fault might occur (instead of + * returning a \c NULL pointer). + * + * @param _Cls class to use the fixed_mem_pool + */ +#define DECLARE_FIXED_MEM_POOL__THROW_NOCHECK(_Cls) \ +public: \ + static void* operator new(size_t __size) \ + { \ + assert(__size == sizeof(_Cls)); \ + return fixed_mem_pool<_Cls>::allocate(); \ + } \ + static void operator delete(void* __ptr) \ + { \ + if (__ptr != NULL) \ + fixed_mem_pool<_Cls>::deallocate(__ptr); \ + } + +#endif // _FIXED_MEM_POOL_H +#endif // _QX_USE_MEM_LEAK_DETECTION +#endif // _QX_MODE_RELEASE +#endif // QT_NO_DEBUG diff --git a/include/QxMemLeak/mem_leak.h b/include/QxMemLeak/mem_leak.h new file mode 100644 index 0000000..f33132a --- /dev/null +++ b/include/QxMemLeak/mem_leak.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +/*** http://wyw.dcweb.cn/leakage.htm ***/ + +#ifndef QT_NO_DEBUG +#ifndef _QX_MODE_RELEASE +#if _QX_USE_MEM_LEAK_DETECTION + +#define _DEBUG_NEW_REDEFINE_NEW 0 +#include "debug_new.h" + +#ifdef _MSC_VER +#pragma warning(disable : 4290) // C++ exception specification ignored +#endif // _MSC_VER + +#ifndef QX_MEM_LEAK_STATIC_OR_INLINE +#ifdef _MSC_VER +#define QX_MEM_LEAK_STATIC_OR_INLINE static +#else +#define QX_MEM_LEAK_STATIC_OR_INLINE inline +#endif // _MSC_VER +#endif // QX_MEM_LEAK_STATIC_OR_INLINE + +QX_MEM_LEAK_STATIC_OR_INLINE void *operator new(size_t size) throw(std::bad_alloc) { return operator new(size, (char *)_DEBUG_NEW_CALLER_ADDRESS, 0); }; +QX_MEM_LEAK_STATIC_OR_INLINE void *operator new[](size_t size) throw(std::bad_alloc) { return operator new[](size, (char *)_DEBUG_NEW_CALLER_ADDRESS, 0); }; +QX_MEM_LEAK_STATIC_OR_INLINE void operator delete(void *pointer) throw() { qx::memory::__debug_new_recorder::free_pointer(pointer, _DEBUG_NEW_CALLER_ADDRESS, false); }; +QX_MEM_LEAK_STATIC_OR_INLINE void operator delete[](void *pointer) throw() { qx::memory::__debug_new_recorder::free_pointer(pointer, _DEBUG_NEW_CALLER_ADDRESS, true); }; + +#if HAVE_PLACEMENT_DELETE +QX_MEM_LEAK_STATIC_OR_INLINE void operator delete(void *pointer, const std::nothrow_t &) throw() { operator delete(pointer, (char *)_DEBUG_NEW_CALLER_ADDRESS, 0); }; +QX_MEM_LEAK_STATIC_OR_INLINE void operator delete[](void *pointer, const std::nothrow_t &) throw() { operator delete[](pointer, (char *)_DEBUG_NEW_CALLER_ADDRESS, 0); }; +#endif // HAVE_PLACEMENT_DELETE + +#ifdef new +#undef new +#endif // new +#define new DEBUG_NEW + +#endif // _QX_USE_MEM_LEAK_DETECTION +#endif // _QX_MODE_RELEASE +#endif // QT_NO_DEBUG + +#if (!_QX_USE_MEM_LEAK_DETECTION) +#ifndef DEBUG_NEW +#define DEBUG_NEW new +#endif // DEBUG_NEW +#endif // (! _QX_USE_MEM_LEAK_DETECTION) + +#ifdef _QX_MODE_RELEASE +#ifndef DEBUG_NEW +#define DEBUG_NEW new +#endif // DEBUG_NEW +#endif // _QX_MODE_RELEASE diff --git a/include/QxMemLeak/mem_pool_base.h b/include/QxMemLeak/mem_pool_base.h new file mode 100644 index 0000000..75148ab --- /dev/null +++ b/include/QxMemLeak/mem_pool_base.h @@ -0,0 +1,77 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2008 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/*! + * \file mem_pool_base.h + * \ingroup QxMemLeak + * + * Header file for the memory pool base. + * + * \version 1.1, 2004/07/26 + * \author Wu Yongwei + * + */ + +#ifndef QT_NO_DEBUG +#ifndef _QX_MODE_RELEASE +#if _QX_USE_MEM_LEAK_DETECTION + +#ifndef _MEM_POOL_BASE_H +#define _MEM_POOL_BASE_H + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +namespace qx { +namespace memory { + +/** + * Base class for memory pools. + */ +class QX_DLL_EXPORT mem_pool_base +{ +public: + virtual ~mem_pool_base(); + virtual void recycle() = 0; + static void* alloc_sys(size_t __size); + static void dealloc_sys(void* __ptr); + + /** Structure to store the next available memory block. */ + struct _Block_list { _Block_list* _M_next; }; +}; + +} // namespace memory +} // namespace qx + +#endif // _MEM_POOL_BASE_H +#endif // _QX_USE_MEM_LEAK_DETECTION +#endif // _QX_MODE_RELEASE +#endif // QT_NO_DEBUG diff --git a/include/QxMemLeak/object_level_lock.h b/include/QxMemLeak/object_level_lock.h new file mode 100644 index 0000000..7d9a924 --- /dev/null +++ b/include/QxMemLeak/object_level_lock.h @@ -0,0 +1,156 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2008 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/*! + * \file object_level_lock.h + * \ingroup QxMemLeak + * + * In essence Loki ObjectLevelLockable re-engineered to use a fast_mutex + * class. Check also Andrei Alexandrescu's article + * "Multithreading and the C++ Type System" for the ideas behind. + * + * \version 1.4, 2004/05/09 + * \author Wu Yongwei + * + */ + +#ifndef QT_NO_DEBUG +#ifndef _QX_MODE_RELEASE +#if _QX_USE_MEM_LEAK_DETECTION + +#ifndef _OBJECT_LEVEL_LOCK_H +#define _OBJECT_LEVEL_LOCK_H + +#ifdef _MSC_VER +#pragma once +#endif + +#include "fast_mutex.h" + +namespace qx { +namespace memory { + +# ifdef _NOTHREADS + /** + * Helper class for object-level locking. This is the + * single-threaded implementation. + */ + template + class object_level_lock + { + public: + /** Type that provides locking/unlocking semantics. */ + class lock + { +#ifndef _QX_MODE_RELEASE +#ifndef QT_NO_DEBUG + const object_level_lock& _M_host; +#endif // QT_NO_DEBUG +#endif // _QX_MODE_RELEASE + lock(const lock&); + lock& operator=(const lock&); + public: + explicit lock(const object_level_lock& __host) +#ifndef _QX_MODE_RELEASE +#ifndef QT_NO_DEBUG + : _M_host(__host) +#endif // QT_NO_DEBUG +#endif // _QX_MODE_RELEASE + {} +#ifndef _QX_MODE_RELEASE +#ifndef QT_NO_DEBUG + // The purpose of this method is allow one to write code + // like "assert(guard.get_locked_object() == this)" to + // ensure that the locked object is exactly the object being + // accessed. + const object_level_lock* get_locked_object() const + { + return &_M_host; + } +#endif // QT_NO_DEBUG +#endif // _QX_MODE_RELEASE + }; + + typedef _Host volatile_type; + }; +# else + /** + * Helper class for class-level locking. This is the multi-threaded + * implementation. + */ + template + class object_level_lock + { + mutable fast_mutex _M_mtx; + + public: + class lock; + friend class lock; + + /** Type that provides locking/unlocking semantics. */ + class lock + { + const object_level_lock& _M_host; + + lock(const lock&); + lock& operator=(const lock&); + public: + explicit lock(const object_level_lock& __host) : _M_host(__host) + { + _M_host._M_mtx.lock(); + } + ~lock() + { + _M_host._M_mtx.unlock(); + } +#ifndef _QX_MODE_RELEASE +#ifndef QT_NO_DEBUG + // The purpose of this method is allow one to write code + // like "assert(guard.get_locked_object() == this)" to + // ensure that the locked object is exactly the object being + // accessed. + const object_level_lock* get_locked_object() const + { + return &_M_host; + } +#endif // QT_NO_DEBUG +#endif // _QX_MODE_RELEASE + }; + + typedef volatile _Host volatile_type; + }; +# endif // _NOTHREADS + +} // namespace memory +} // namespace qx + +#endif // _OBJECT_LEVEL_LOCK_H +#endif // _QX_USE_MEM_LEAK_DETECTION +#endif // _QX_MODE_RELEASE +#endif // QT_NO_DEBUG diff --git a/include/QxMemLeak/pctimer.h b/include/QxMemLeak/pctimer.h new file mode 100644 index 0000000..f832289 --- /dev/null +++ b/include/QxMemLeak/pctimer.h @@ -0,0 +1,112 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2008 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/*! + * \file pctimer.h + * \ingroup QxMemLeak + * + * Function to get a high-resolution timer for Win32/Cygwin/Unix. + * + * \version 1.6, 2004/08/02 + * \author Wu Yongwei + * + */ + +#ifndef QT_NO_DEBUG +#ifndef _QX_MODE_RELEASE +#if _QX_USE_MEM_LEAK_DETECTION +#ifndef _PCTIMER_H + +namespace qx { +namespace memory { + +typedef double pctimer_t; + +} // namespace memory +} // namespace qx + +#if defined(_WIN32) || defined(__CYGWIN__) + +#ifndef _WIN32 +#define _PCTIMER_NO_WIN32 +#endif /* _WIN32 */ + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif /* WIN32_LEAN_AND_MEAN */ +#include + +#ifdef _PCTIMER_NO_WIN32 +#undef _PCTIMER_NO_WIN32 +#undef _WIN32 +#endif /* _PCTIMER_NO_WIN32 */ + +namespace qx { +namespace memory { + +__inline pctimer_t pctimer(void) +{ + static LARGE_INTEGER __pcount, __pcfreq; + static int __initflag; + + if (!__initflag) + { + QueryPerformanceFrequency(&__pcfreq); + __initflag++; + } + + QueryPerformanceCounter(&__pcount); + return (double)__pcount.QuadPart / (double)__pcfreq.QuadPart; +} + +} // namespace memory +} // namespace qx + +#else /* Not Win32/Cygwin */ + +#include + +namespace qx { +namespace memory { + +__inline pctimer_t pctimer(void) +{ + struct timeval __tv; + gettimeofday(&__tv, NULL); + return (double)__tv.tv_sec + (double)__tv.tv_usec / 1000000; +} + +} // namespace memory +} // namespace qx + +#endif /* Win32/Cygwin */ + +#endif /* _PCTIMER_H */ +#endif // _QX_USE_MEM_LEAK_DETECTION +#endif // _QX_MODE_RELEASE +#endif // QT_NO_DEBUG diff --git a/include/QxMemLeak/set_assign.h b/include/QxMemLeak/set_assign.h new file mode 100644 index 0000000..a7acddb --- /dev/null +++ b/include/QxMemLeak/set_assign.h @@ -0,0 +1,162 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2008 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/*! + * \file set_assign.h + * \ingroup QxMemLeak + * + * Definition of template functions set_assign_union and set_assign_difference. + * + * \version 1.5, 2004/07/26 + * \author Wu Yongwei + * + */ + +#ifndef QT_NO_DEBUG +#ifndef _QX_MODE_RELEASE +#if _QX_USE_MEM_LEAK_DETECTION + +#ifndef _SET_ASSIGN_H +#define _SET_ASSIGN_H + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +namespace qx { +namespace memory { + +template +_Container& set_assign_union(_Container& __dest, + _InputIter __first, + _InputIter __last) +{ + typename _Container::iterator __first_dest = __dest.begin(); + typename _Container::iterator __last_dest = __dest.end(); + while (__first_dest != __last_dest && __first != __last) + { + if (*__first_dest < *__first) + ++__first_dest; + else if (*__first < *__first_dest) + { + __dest.insert(__first_dest, *__first); + ++__first; + } + else // *__first_dest == *__first + { + ++__first_dest; + ++__first; + } + } + if (__first != __last) + std::copy(__first, __last, inserter(__dest, __last_dest)); + return __dest; +} + +template +_Container& set_assign_union(_Container& __dest, + _InputIter __first, + _InputIter __last, + _Compare __comp) +{ + typename _Container::iterator __first_dest = __dest.begin(); + typename _Container::iterator __last_dest = __dest.end(); + while (__first_dest != __last_dest && __first != __last) + { + if (__comp(*__first_dest, *__first)) + ++__first_dest; + else if (__comp(*__first, *__first_dest)) + { + __dest.insert(__first_dest, *__first); + ++__first; + } + else // *__first_dest is equivalent to *__first + { + ++__first_dest; + ++__first; + } + } + if (__first != __last) + std::copy(__first, __last, inserter(__dest, __last_dest)); + return __dest; +} + +template +_Container& set_assign_difference(_Container& __dest, + _InputIter __first, + _InputIter __last) +{ + typename _Container::iterator __first_dest = __dest.begin(); + typename _Container::iterator __last_dest = __dest.end(); + while (__first_dest != __last_dest && __first != __last) + { + if (*__first_dest < *__first) + ++__first_dest; + else if (*__first < *__first_dest) + ++__first; + else // *__first_dest == *__first + { + __dest.erase(__first_dest++); + ++__first; + } + } + return __dest; +} + +template +_Container& set_assign_difference(_Container& __dest, + _InputIter __first, + _InputIter __last, + _Compare __comp) +{ + typename _Container::iterator __first_dest = __dest.begin(); + typename _Container::iterator __last_dest = __dest.end(); + while (__first_dest != __last_dest && __first != __last) + { + if (__comp(*__first_dest, *__first)) + ++__first_dest; + else if (__comp(*__first, *__first_dest)) + ++__first; + else // *__first_dest is equivalent to *__first + { + __dest.erase(__first_dest++); + ++__first; + } + } + return __dest; +} + +} // namespace memory +} // namespace qx + +#endif // _SET_ASSIGN_H +#endif // _QX_USE_MEM_LEAK_DETECTION +#endif // _QX_MODE_RELEASE +#endif // QT_NO_DEBUG diff --git a/include/QxMemLeak/static_assert.h b/include/QxMemLeak/static_assert.h new file mode 100644 index 0000000..dd7d9a2 --- /dev/null +++ b/include/QxMemLeak/static_assert.h @@ -0,0 +1,63 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2008 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/*! + * \file static_assert.h + * \ingroup QxMemLeak + * + * Template class to check validity duing compile time (adapted from Loki). + * + * \version 1.2, 2005/11/22 + * \author Wu Yongwei + * + */ + +#ifndef QT_NO_DEBUG +#ifndef _QX_MODE_RELEASE +#if _QX_USE_MEM_LEAK_DETECTION +#ifndef STATIC_ASSERT + +namespace qx { +namespace memory { + +template struct __nvwa_compile_time_error; +template <> struct __nvwa_compile_time_error {}; + +} // namespace memory +} // namespace qx + +#define STATIC_ASSERT(_Expr, _Msg) \ + { \ + __nvwa_compile_time_error<((_Expr) != 0)> ERROR_##_Msg; \ + (void)ERROR_##_Msg; \ + } + +#endif // STATIC_ASSERT +#endif // _QX_USE_MEM_LEAK_DETECTION +#endif // _QX_MODE_RELEASE +#endif // QT_NO_DEBUG diff --git a/include/QxMemLeak/static_mem_pool.h b/include/QxMemLeak/static_mem_pool.h new file mode 100644 index 0000000..7f1c832 --- /dev/null +++ b/include/QxMemLeak/static_mem_pool.h @@ -0,0 +1,395 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2008 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/*! + * \file static_mem_pool.h + * \ingroup QxMemLeak + * + * Header file for the `static' memory pool. + * + * \version 1.20, 2007/10/20 + * \author Wu Yongwei + * + */ + +#ifndef QT_NO_DEBUG +#ifndef _QX_MODE_RELEASE +#if _QX_USE_MEM_LEAK_DETECTION + +#ifndef _STATIC_MEM_POOL_H +#define _STATIC_MEM_POOL_H + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include "class_level_lock.h" +#include "mem_pool_base.h" + +/* Defines Work-around for Microsoft Visual C++ 6.0 and Borland C++ 5.5.1 */ +# if (defined(_MSC_VER) && _MSC_VER < 1300) \ + || (defined(__BORLANDC__) && __BORLANDC__ < 0x600) +# define __PRIVATE public +# else +# define __PRIVATE private +# endif + +/* Defines the macro for debugging output */ +# ifdef _STATIC_MEM_POOL_DEBUG +# include +# define _STATIC_MEM_POOL_TRACE(_Lck, _Msg) \ + { \ + if (_Lck) { \ + static_mem_pool_set::lock __guard; \ + std::cerr << "static_mem_pool: " << _Msg << std::endl; \ + } else { \ + std::cerr << "static_mem_pool: " << _Msg << std::endl; \ + } \ + } +# else +# define _STATIC_MEM_POOL_TRACE(_Lck, _Msg) \ + ((void)0) +# endif + +namespace qx { +namespace memory { + +/** + * Singleton class to maintain a set of existing instantiations of + * static_mem_pool. + */ +class QX_DLL_EXPORT static_mem_pool_set +{ +public: + typedef class_level_lock::lock lock; + static static_mem_pool_set& instance(); + void recycle(); + void add(mem_pool_base* __memory_pool_p); + +__PRIVATE: + ~static_mem_pool_set(); +private: + static_mem_pool_set(); + + typedef std::vector container_type; + container_type _M_memory_pool_set; + + /* Forbid their use */ + static_mem_pool_set(const static_mem_pool_set&); + const static_mem_pool_set& operator=(const static_mem_pool_set&); +}; + +/** + * Singleton class template to manage the allocation/deallocation of + * memory blocks of one specific size. + * + * @param _Sz size of elements in the static_mem_pool + * @param _Gid group id of a static_mem_pool: if it is negative, + * simultaneous accesses to this static_mem_pool will be + * protected from each other; otherwise no protection is + * given + */ +template +class static_mem_pool : public mem_pool_base +{ + typedef typename class_level_lock, (_Gid < 0)> + ::lock lock; +public: + /** + * Gets the instance of the static memory pool. It will create the + * instance if it does not already exist. Generally this function + * is now not needed. + * + * @return reference to the instance of the static memory pool + * @see instance_known + */ + static static_mem_pool& instance() + { + lock __guard; + if (!_S_instance_p) + { + _S_instance_p = _S_create_instance(); + } + return *_S_instance_p; + } + /** + * Gets the known instance of the static memory pool. The instance + * must already exist. Generally the static initializer of the + * template guarantees it. + * + * @return reference to the instance of the static memory pool + */ + static static_mem_pool& instance_known() + { + assert(_S_instance_p != NULL); + return *_S_instance_p; + } + /** + * Allocates memory and returns its pointer. The template will try + * to get it from the memory pool first, and request memory from the + * system if there is no free memory in the pool. + * + * @return pointer to allocated memory if successful; \c NULL + * otherwise + */ + void* allocate() + { + { + lock __guard; + if (_S_memory_block_p) + { + void* __result = _S_memory_block_p; + _S_memory_block_p = _S_memory_block_p->_M_next; + return __result; + } + } + return _S_alloc_sys(_S_align(_Sz)); + } + /** + * Deallocates memory by putting the memory block into the pool. + * + * @param __ptr pointer to memory to be deallocated + */ + void deallocate(void* __ptr) + { + assert(__ptr != NULL); + lock __guard; + _Block_list* __block_ = reinterpret_cast<_Block_list*>(__ptr); + __block_->_M_next = _S_memory_block_p; + _S_memory_block_p = __block_; + } + virtual void recycle(); + +private: + static_mem_pool() + { + _STATIC_MEM_POOL_TRACE(true, "static_mem_pool<" << _Sz << ',' + << _Gid << "> is created"); + } + ~static_mem_pool() + { +#ifndef _QX_MODE_RELEASE +#ifndef QT_NO_DEBUG + // Empty the pool to avoid false memory leakage alarms. This is + // generally not necessary for release binaries. + _Block_list* __block_ = _S_memory_block_p; + while (__block_) + { + _Block_list* __next_ = __block_->_M_next; + dealloc_sys(__block_); + __block_ = __next_; + } + _S_memory_block_p = NULL; +#endif // QT_NO_DEBUG +#endif // _QX_MODE_RELEASE + _S_instance_p = NULL; + _S_destroyed = true; + _STATIC_MEM_POOL_TRACE(false, "static_mem_pool<" << _Sz << ',' + << _Gid << "> is destroyed"); + } + static size_t _S_align(size_t __size) + { + return __size >= sizeof(_Block_list) ? __size : sizeof(_Block_list); + } + static void* _S_alloc_sys(size_t __size); + static static_mem_pool* _S_create_instance(); + + static bool _S_destroyed; + static static_mem_pool* _S_instance_p; + static mem_pool_base::_Block_list* _S_memory_block_p; + + /* Forbid their use */ + static_mem_pool(const static_mem_pool&); + const static_mem_pool& operator=(const static_mem_pool&); +}; + +template bool + static_mem_pool<_Sz, _Gid>::_S_destroyed = false; +template mem_pool_base::_Block_list* + static_mem_pool<_Sz, _Gid>::_S_memory_block_p = NULL; +template static_mem_pool<_Sz, _Gid>* + static_mem_pool<_Sz, _Gid>::_S_instance_p = _S_create_instance(); + +/** + * Recycles half of the free memory blocks in the memory pool to the + * system. It is called when a memory request to the system (in other + * instances of the static memory pool) fails. + */ +template +void static_mem_pool<_Sz, _Gid>::recycle() +{ + // Only here the global lock in static_mem_pool_set is obtained + // before the pool-specific lock. However, no race conditions are + // found so far. + lock __guard; + _Block_list* __block_ = _S_memory_block_p; + while (__block_) + { + if (_Block_list* __temp_ = __block_->_M_next) + { + _Block_list* __next_ = __temp_->_M_next; + __block_->_M_next = __next_; + dealloc_sys(__temp_); + __block_ = __next_; + } + else + { + break; + } + } + _STATIC_MEM_POOL_TRACE(false, "static_mem_pool<" << _Sz << ',' + << _Gid << "> is recycled"); +} + +template +void* static_mem_pool<_Sz, _Gid>::_S_alloc_sys(size_t __size) +{ + static_mem_pool_set::lock __guard; + void* __result = mem_pool_base::alloc_sys(__size); + if (!__result) + { + static_mem_pool_set::instance().recycle(); + __result = mem_pool_base::alloc_sys(__size); + } + return __result; +} + +template +static_mem_pool<_Sz, _Gid>* static_mem_pool<_Sz, _Gid>::_S_create_instance() +{ + if (_S_destroyed) + throw std::runtime_error("dead reference detected"); + + static_mem_pool_set::instance(); // Force its creation + static_mem_pool* __inst_p = new static_mem_pool(); + try + { + static_mem_pool_set::instance().add(__inst_p); + } + catch (...) + { + _STATIC_MEM_POOL_TRACE(true, + "Exception occurs in static_mem_pool_set::add"); + // The strange cast below is to work around a bug in GCC 2.95.3 + delete static_cast(__inst_p); + throw; + } + return __inst_p; +} + +} // namespace memory +} // namespace qx + +#define DECLARE_STATIC_MEM_POOL(_Cls) \ +public: \ + static void* operator new(size_t __size) \ + { \ + assert(__size == sizeof(_Cls)); \ + void* __ptr; \ + __ptr = static_mem_pool:: \ + instance_known().allocate(); \ + if (__ptr == NULL) \ + throw std::bad_alloc(); \ + return __ptr; \ + } \ + static void operator delete(void* __ptr) \ + { \ + if (__ptr) \ + static_mem_pool:: \ + instance_known().deallocate(__ptr); \ + } + +#define DECLARE_STATIC_MEM_POOL__NOTHROW(_Cls) \ +public: \ + static void* operator new(size_t __size) throw() \ + { \ + assert(__size == sizeof(_Cls)); \ + return static_mem_pool:: \ + instance_known().allocate(); \ + } \ + static void operator delete(void* __ptr) \ + { \ + if (__ptr) \ + static_mem_pool:: \ + instance_known().deallocate(__ptr); \ + } + +#define DECLARE_STATIC_MEM_POOL_GROUPED(_Cls, _Gid) \ +public: \ + static void* operator new(size_t __size) \ + { \ + assert(__size == sizeof(_Cls)); \ + void* __ptr; \ + __ptr = static_mem_pool:: \ + instance_known().allocate(); \ + if (__ptr == NULL) \ + throw std::bad_alloc(); \ + return __ptr; \ + } \ + static void operator delete(void* __ptr) \ + { \ + if (__ptr) \ + static_mem_pool:: \ + instance_known().deallocate(__ptr); \ + } + +#define DECLARE_STATIC_MEM_POOL_GROUPED__NOTHROW(_Cls, _Gid) \ +public: \ + static void* operator new(size_t __size) throw() \ + { \ + assert(__size == sizeof(_Cls)); \ + return static_mem_pool:: \ + instance_known().allocate(); \ + } \ + static void operator delete(void* __ptr) \ + { \ + if (__ptr) \ + static_mem_pool:: \ + instance_known().deallocate(__ptr); \ + } + +// OBSOLETE: no longer needed +#define PREPARE_STATIC_MEM_POOL(_Cls) \ + std::cerr << "PREPARE_STATIC_MEM_POOL is obsolete!\n"; + +// OBSOLETE: no longer needed +#define PREPARE_STATIC_MEM_POOL_GROUPED(_Cls, _Gid) \ + std::cerr << "PREPARE_STATIC_MEM_POOL_GROUPED is obsolete!\n"; + +#undef __PRIVATE + +#endif // _STATIC_MEM_POOL_H +#endif // _QX_USE_MEM_LEAK_DETECTION +#endif // _QX_MODE_RELEASE +#endif // QT_NO_DEBUG diff --git a/include/QxModelView.h b/include/QxModelView.h new file mode 100644 index 0000000..ce0d1ea --- /dev/null +++ b/include/QxModelView.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_MODEL_VIEW_H_ +#define _QX_MODEL_VIEW_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxModelView.h + * \author XDL Team + * \ingroup QxModelView + * \brief Include all headers required to use QxModelView module : https://www.qxorm.com/qxorm_en/manual.html#manual_90 + */ + +#include + +#include +#include +#include +#include +#include + +#endif // _QX_MODEL_VIEW_H_ diff --git a/include/QxModelView/IxModel.h b/include/QxModelView/IxModel.h new file mode 100644 index 0000000..6ff451e --- /dev/null +++ b/include/QxModelView/IxModel.h @@ -0,0 +1,374 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_MODEL_H_ +#define _IX_MODEL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxModel.h + * \author XDL Team + * \ingroup QxModelView + * \brief Interface to manage Qt model/view architecture with classes registered into QxOrm context (Qt widgets and/or QML views) + */ + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +#include + +#ifndef Q_MOC_RUN +#include +#include +#include +#include +#include +#include +#include +#include +#endif // Q_MOC_RUN + +namespace qx +{ + namespace model_view + { + namespace detail + { + + template + struct QxNestedModel; + template + struct QxNestedModel_Generic; + template + struct QxNestedModel_Container; + + } // namespace detail + } // namespace model_view +} // namespace qx + +namespace qx +{ + + /*! + * \ingroup QxModelView + * \brief qx::IxModel : interface to manage Qt model/view architecture with classes registered into QxOrm context (Qt widgets and/or QML views) + * + * QxModelView module provides an easy way to work with Qt model/view engine with all classes registered into QxOrm context : + * - Qt widgets : QTableView or QListView for example to display/modify a database table content ; + * - QML : each property defined in QxOrm context is exposed to QML engine : QxModelView module makes easier integration between QML and databases. + * + * qx::IxModel interface provides a generic way for all models linked to persistents classes registered into QxOrm context. All methods of this class prefixed by qx call functions from qx::dao namespace and then communicate with database. + * + * The qxBlogModelView sample project in ./test/ directory of QxOrm package shows how to create quickly a model and associate it to the Qt model/view engine (first with a Qt widget, then with a QML view). + * + * 1- Here is an example to display/modify data from 'author' table (go to qxBlog tutorial for 'author' class definition) in a QTableView : + * \code + // Create a model and fetch all data from database + qx::IxModel * pModel = new qx::QxModel(); + pModel->qxFetchAll(); + + // Associate the model to a QTableView and display it + QTableView tableView; + tableView.setModel(pModel); + tableView.show(); + * \endcode + * + * 2- Here is another example in QML (with Qt5, QxModelView module works fine with Qt4 too) : + * \code + // Create a model and fetch all data from database + qx::IxModel * pModel = new qx::QxModel(); + pModel->qxFetchAll(); + + // Associate the model to a QML view and display it + QQuickView qmlView; + qmlView.rootContext()->setContextProperty("myModel", pModel); + qmlView.setSource(QUrl("qrc:/documents/main.qml")); + qmlView.show(); + * \endcode + * + * And here is the 'main.qml' file content : + * \code + import QtQuick 2.1 + import QtQuick.Controls 1.0 + + Item { + width: 400 + height: 300 + Row { + height: 20 + spacing: 20 + Button { + text: "Clear" + onClicked: myModel.clear() + } + Button { + text: "Fetch All" + onClicked: myModel.qxFetchAll_() + } + Button { + text: "Save" + onClicked: myModel.qxSave_() + } + } + ListView { + y: 30 + height: 270 + model: myModel + delegate: Row { + height: 20 + spacing: 10 + Text { text: "id: " + author_id } + TextField { + text: name + onTextChanged: name = text + } + } + } + } + * \endcode + * + * As you can see in the 'main.qml' file, 'author_id' and 'name' properties of 'author' model ('myModel' variable) can be automatically read and write (because they are registered into QxOrm context). + * Moreover, qx::IxModel interface provides a list of methods for QML side (Q_INVOKABLE) to communicate with database : for example, the 'Save' button will save the model in database without having to write a C++ function. + * + * Note : a QxEntityEditor plugin generates automatically the code to manage models with relationships. Then it is possible to work with nested C++ models. + */ + class QX_DLL_EXPORT IxModel : public QAbstractItemModel + { + + Q_OBJECT + + template + friend struct qx::model_view::detail::QxNestedModel; + template + friend struct qx::model_view::detail::QxNestedModel_Generic; + template + friend struct qx::model_view::detail::QxNestedModel_Container; + + public: + enum e_auto_update_database + { + e_no_auto_update, + e_auto_update_on_field_change + }; + + typedef QHash type_relation_by_name; + typedef QList type_lst_relation_by_name; + typedef QHash> type_child_to_its_relation; + + protected: + IxClass *m_pClass; //!< Class introspection registered into QxOrm context associated to the model + IxClass *m_pModelClass; //!< If model itself is registered into QxOrm context, then you can use this property to work with introspection engine + IxDataMemberX *m_pDataMemberX; //!< List of properties defined into QxOrm context + IxDataMember *m_pDataMemberId; //!< Primary key (property id) defined into QxOrm context + IxCollection *m_pCollection; //!< Interface to store a list of items + QHash m_lstRoleNames; //!< List of model's role names to expose data to QML + QList m_lstDataMember; //!< List of data member exposed by the model + QHash m_lstDataMemberByKey; //!< List of data member key to get column index in model + QHash m_lstHeadersData; //!< List of headers data by role and data member key + QStringList m_lstColumns; //!< List of columns exposed by the model (if empty, all columns) + QSqlDatabase m_database; //!< Database connexion to execute SQL queries (if empty, default database connexion) + QSqlError m_lastError; //!< Last SQL error + IxModel *m_pParent; //!< Parent model, NULL if current model is the root model + type_lst_relation_by_name m_lstChild; //!< List of child model : QxEntityEditor uses this property to manage relationships and create complex data structure + type_child_to_its_relation m_hChild; //!< Reverse link to m_lstChild, used in setData() to save relations + e_auto_update_database m_eAutoUpdateDatabase; //!< Auto-update database on field change (detected by the setData() method) + IxDataMember *m_pDataMemberRelationToParent; //!< The data member holding relationship to its parent model (if one exists), used only by nested models + long m_lManualInsertIndex; //!< Index to insert manually items to the collection + QHash m_hCustomProperties; //!< Use this generic hash-table to define extra-properties in your custom classes which inherit from qx::IxModel interface (instead of creating new properties) ==> this will ensure that sizeof(qx::IxModel) == sizeof(YourCustomClass), this is important with nested models feature + + public: + IxModel(QObject *parent = 0); + virtual ~IxModel(); + + IxClass *getClass() const; + IxClass *getModelClass() const; + IxCollection *getCollection() const; + QSqlDatabase getDatabase() const; + QSqlError getLastError() const; + Q_INVOKABLE QString getLastErrorAsString() const; + Q_INVOKABLE QStringList getListOfColumns() const; + QHash getListOfHeaders() const; //!< Obsolete : use headerData() instead + IxDataMember *getDataMember(int column) const; + Q_INVOKABLE QString getDataMemberKey(int column) const; + Q_INVOKABLE int getRowCount() const; + Q_INVOKABLE QVariant getModelValue(int row, const QString &column) const; + Q_INVOKABLE int getColumnIndex(const QString &sColumnName) const; + Q_INVOKABLE int getAutoUpdateDatabase_() const; + e_auto_update_database getAutoUpdateDatabase() const; + Q_INVOKABLE virtual bool getShowEmptyLine() const = 0; //!< Can be useful when a model is displayed in a table (QTableView for example) to add automatically an empty row at the end of the table to insert quickly new items (for example, same style like QxEntityEditor list of properties/relationships) + Q_INVOKABLE QVariant getCustomProperty(const QString &key) const; + Q_INVOKABLE QObject *getParentModel() const; //!< Can be used to figure out whether this model has a parent model (used only by nested models) + Q_INVOKABLE void dumpModel(bool bJsonFormat = true) const; + Q_INVOKABLE QObject *cloneModel(); + + void setDatabase(const QSqlDatabase &db); + Q_INVOKABLE void setListOfColumns(const QStringList &lst); + void setListOfHeaders(const QHash &lst); //!< Obsolete : use setHeaderData() instead + Q_INVOKABLE bool setModelValue(int row, const QString &column, const QVariant &value); + void setParentModel(IxModel *pParent); + Q_INVOKABLE void setAutoUpdateDatabase_(int i); + void setAutoUpdateDatabase(e_auto_update_database e); + Q_INVOKABLE virtual void setShowEmptyLine(bool b) = 0; + Q_INVOKABLE void setCustomProperty(const QString &key, const QVariant &val); + + Q_INVOKABLE QString toJson(int row = -1) const; //!< On QML side, use JSON.parse() to create a javascript object after calling this qx::IxModel::toJson() method + Q_INVOKABLE bool fromJson(const QString &json, int row = -1); //!< On QML side, use JSON.stringify() on a javascript object before calling this qx::IxModel::fromJson() method + + Q_INVOKABLE QVariant getRelationshipValues(int row, const QString &relation, bool bLoadFromDatabase = false, const QString &sAppendRelations = QString()); //!< Depending on relationship type (1-1, 1-n, n-1, n-n) : can return a QVariantMap type or a QVariantList type + Q_INVOKABLE bool setRelationshipValues(int row, const QString &relation, const QVariant &values); //!< Depending on relationship type (1-1, 1-n, n-1, n-n) : values parameter can be a QVariantMap type or a QVariantList type + + virtual long qxCount(const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL) = 0; + virtual QSqlError qxCount(long &lCount, const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL) = 0; + virtual QSqlError qxFetchById(const QVariant &id, const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) = 0; + virtual QSqlError qxFetchAll(const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) = 0; + virtual QSqlError qxFetchByQuery(const qx::QxSqlQuery &query, const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) = 0; + virtual QSqlError qxFetchRow(int row, const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) = 0; + virtual QSqlError qxInsert(const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) = 0; + virtual QSqlError qxInsertRow(int row, const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) = 0; + virtual QSqlError qxUpdate(const qx::QxSqlQuery &query = qx::QxSqlQuery(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) = 0; + virtual QSqlError qxUpdateRow(int row, const qx::QxSqlQuery &query = qx::QxSqlQuery(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) = 0; + virtual QSqlError qxSave(const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) = 0; + virtual QSqlError qxSaveRow(int row, const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) = 0; + virtual QSqlError qxSaveRowData(int row, const QStringList &column = QStringList(), QSqlDatabase *pDatabase = NULL) = 0; + virtual QSqlError qxDeleteById(const QVariant &id, QSqlDatabase *pDatabase = NULL) = 0; + virtual QSqlError qxDeleteAll(QSqlDatabase *pDatabase = NULL) = 0; + virtual QSqlError qxDeleteByQuery(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) = 0; + virtual QSqlError qxDeleteRow(int row, QSqlDatabase *pDatabase = NULL) = 0; + virtual QSqlError qxDestroyById(const QVariant &id, QSqlDatabase *pDatabase = NULL) = 0; + virtual QSqlError qxDestroyAll(QSqlDatabase *pDatabase = NULL) = 0; + virtual QSqlError qxDestroyByQuery(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) = 0; + virtual QSqlError qxDestroyRow(int row, QSqlDatabase *pDatabase = NULL) = 0; + virtual QSqlError qxExecuteQuery(qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) = 0; + virtual qx_bool qxExist(const QVariant &id, QSqlDatabase *pDatabase = NULL) = 0; + virtual qx::QxInvalidValueX qxValidate(const QStringList &groups = QStringList()) = 0; + virtual qx::QxInvalidValueX qxValidateRow(int row, const QStringList &groups = QStringList()) = 0; + + Q_INVOKABLE int qxCount_(const QString &sQuery); + Q_INVOKABLE bool qxFetchById_(const QVariant &id, const QStringList &relation = QStringList()); + Q_INVOKABLE bool qxFetchAll_(const QStringList &relation = QStringList()); + Q_INVOKABLE bool qxFetchByQuery_(const QString &sQuery, const QStringList &relation = QStringList()); + Q_INVOKABLE bool qxFetchRow_(int row, const QStringList &relation = QStringList()); + Q_INVOKABLE bool qxInsert_(const QStringList &relation = QStringList(), bool bUseExecBatch = false); + Q_INVOKABLE bool qxInsertRow_(int row, const QStringList &relation = QStringList()); + Q_INVOKABLE bool qxUpdate_(const QString &sQuery, const QStringList &relation = QStringList(), bool bUseExecBatch = false); + Q_INVOKABLE bool qxUpdateRow_(int row, const QString &sQuery, const QStringList &relation = QStringList()); + Q_INVOKABLE bool qxSave_(const QStringList &relation = QStringList()); + Q_INVOKABLE bool qxSaveRow_(int row, const QStringList &relation = QStringList()); + Q_INVOKABLE bool qxDeleteById_(const QVariant &id); + Q_INVOKABLE bool qxDeleteAll_(); + Q_INVOKABLE bool qxDeleteByQuery_(const QString &sQuery); + Q_INVOKABLE bool qxDeleteRow_(int row); + Q_INVOKABLE bool qxDestroyById_(const QVariant &id); + Q_INVOKABLE bool qxDestroyAll_(); + Q_INVOKABLE bool qxDestroyByQuery_(const QString &sQuery); + Q_INVOKABLE bool qxDestroyRow_(int row); + Q_INVOKABLE bool qxExecuteQuery_(const QString &sQuery); + Q_INVOKABLE bool qxExist_(const QVariant &id); + Q_INVOKABLE QString qxValidate_(const QStringList &groups = QStringList()); + Q_INVOKABLE QString qxValidateRow_(int row, const QStringList &groups = QStringList()); + + QSqlError saveChildRelations(IxModel *pChild); + QVariant getIdFromChild(IxModel *pChild) const; //!< Used to save foreign key in a nested model + QPair getChildPosition(IxModel *pChild) const; + + protected: + void raiseEvent_headerDataChanged(Qt::Orientation orientation, int first, int last); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + void raiseEvent_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles = QVector()); + void raiseEvent_layoutAboutToBeChanged(const QList &parents = QList(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint); + void raiseEvent_layoutChanged(const QList &parents = QList(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + void raiseEvent_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void raiseEvent_layoutAboutToBeChanged(); + void raiseEvent_layoutChanged(); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + + public: + Q_INVOKABLE void clear(bool bUpdateColumns = false); + + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex parent(const QModelIndex &index) const; + virtual bool hasChildren(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual Qt::DropActions supportedDropActions() const; + virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + virtual bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole); + bool setHeaderData(const QString &sColumnName, const QVariant &value, int role = Qt::EditRole); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + virtual QHash roleNames() const; + virtual Qt::DropActions supportedDragActions() const; +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + + protected: + virtual QObject *cloneModelImpl() = 0; + virtual void dumpModelImpl(bool bJsonFormat) const = 0; + virtual void *getRowItemAsVoidPtr(int row) const = 0; + virtual bool isDirtyRow(int row) const = 0; + virtual void insertDirtyRowToModel() = 0; + virtual void updateShowEmptyLine() = 0; + virtual void syncNestedModel(int row, const QStringList &relation); + virtual void syncAllNestedModel(const QStringList &relation); + void syncNestedModelRecursive(IxModel *pNestedModel, const QStringList &relation); + + void generateRoleNames(); + QSqlDatabase *database(QSqlDatabase *other); + IxModel *getChild(long row, const QString &relation); + void insertChild(long row, const QString &relation, IxModel *pChild); + void removeListOfChild(long row); + bool removeRowsGeneric(int row, int count); + bool removeRowsAutoUpdateOnFieldChange(int row, int count); + +#ifndef _QX_NO_JSON + + virtual QString toJson_Helper(int row) const = 0; + virtual bool fromJson_Helper(const QString &json, int row) = 0; + + virtual QVariant getRelationshipValues_Helper(int row, const QString &relation, bool bLoadFromDatabase, const QString &sAppendRelations) = 0; + virtual bool setRelationshipValues_Helper(int row, const QString &relation, const QVariant &values) = 0; + +#endif // _QX_NO_JSON + }; + +} // namespace qx + +#endif // _IX_MODEL_H_ diff --git a/include/QxModelView/QxModel.h b/include/QxModelView/QxModel.h new file mode 100644 index 0000000..d564dde --- /dev/null +++ b/include/QxModelView/QxModel.h @@ -0,0 +1,1136 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_MODEL_H_ +#define _QX_MODEL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxModel.h + * \author XDL Team + * \ingroup QxModelView + * \brief All classes registered into QxOrm context can be used with Qt model/view architecture (Qt widgets and/or QML views) + */ + +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include + +#ifndef _QX_NO_JSON +#include +#include +#endif // _QX_NO_JSON + +namespace qx +{ + namespace model_view + { + namespace detail + { + + template + struct QxNestedModel; + template + struct QxNestedModel_Generic; + template + struct QxNestedModel_Container; + + } // namespace detail + } // namespace model_view +} // namespace qx + +namespace qx +{ + + /*! + * \ingroup QxModelView + * \brief qx::QxModel : all classes registered into QxOrm context can be used with Qt model/view architecture (Qt widgets and/or QML views) + * + * QxModelView module provides an easy way to work with Qt model/view engine with all classes registered into QxOrm context : + * - Qt widgets : QTableView or QListView for example to display/modify a database table content ; + * - QML : each property defined in QxOrm context is exposed to QML engine : QxModelView module makes easier integration between QML and databases. + * + * qx::IxModel interface provides a generic way for all models linked to persistents classes registered into QxOrm context. All methods of this class prefixed by qx call functions from qx::dao namespace and then communicate with database. + * + * The qxBlogModelView sample project in ./test/ directory of QxOrm package shows how to create quickly a model and associate it to the Qt model/view engine (first with a Qt widget, then with a QML view). + * + * 1- Here is an example to display/modify data from 'author' table (go to qxBlog tutorial for 'author' class definition) in a QTableView : + * \code + // Create a model and fetch all data from database + qx::IxModel * pModel = new qx::QxModel(); + pModel->qxFetchAll(); + + // Associate the model to a QTableView and display it + QTableView tableView; + tableView.setModel(pModel); + tableView.show(); + * \endcode + * + * 2- Here is another example in QML (with Qt5, QxModelView module works fine with Qt4 too) : + * \code + // Create a model and fetch all data from database + qx::IxModel * pModel = new qx::QxModel(); + pModel->qxFetchAll(); + + // Associate the model to a QML view and display it + QQuickView qmlView; + qmlView.rootContext()->setContextProperty("myModel", pModel); + qmlView.setSource(QUrl("qrc:/documents/main.qml")); + qmlView.show(); + * \endcode + * + * And here is the 'main.qml' file content : + * \code + import QtQuick 2.1 + import QtQuick.Controls 1.0 + + Item { + width: 400 + height: 300 + Row { + height: 20 + spacing: 20 + Button { + text: "Clear" + onClicked: myModel.clear() + } + Button { + text: "Fetch All" + onClicked: myModel.qxFetchAll_() + } + Button { + text: "Save" + onClicked: myModel.qxSave_() + } + } + ListView { + y: 30 + height: 270 + model: myModel + delegate: Row { + height: 20 + spacing: 10 + Text { text: "id: " + author_id } + TextField { + text: name + onTextChanged: name = text + } + } + } + } + * \endcode + * + * As you can see in the 'main.qml' file, 'author_id' and 'name' properties of 'author' model ('myModel' variable) can be automatically read and write (because they are registered into QxOrm context). + * Moreover, qx::IxModel interface provides a list of methods for QML side (Q_INVOKABLE) to communicate with database : for example, the 'Save' button will save the model in database without having to write a C++ function. + * + * Note : a QxEntityEditor plugin generates automatically the code to manage models with relationships. Then it is possible to work with nested C++ models. + */ + template + class QxModel : public B // B type must inherit from qx::IxModel and must not define new data-members (use 'm_hCustomProperties' or QObject dynamic properties if you need new properties in your derived class) + { + + template + friend struct qx::model_view::detail::QxNestedModel; + template + friend struct qx::model_view::detail::QxNestedModel_Generic; + template + friend struct qx::model_view::detail::QxNestedModel_Container; + + public: + typedef std::shared_ptr type_ptr; + typedef typename qx::trait::get_primary_key::type type_primary_key; + typedef qx::QxCollection type_collection; + typedef B type_base_class; + + enum + { + qx_is_valid = (qx::trait::is_qx_registered::value && std::is_base_of::value) + }; + + protected: + type_collection m_model; //!< Model associated to a class registered into QxOrm context + std::shared_ptr> m_pDirtyRow; //!< When displayed in a QTableView, this will cause an empty line awaiting user input to be displayed at the bottom (enabled with setShowEmptyLine() method) + + public: + QxModel(QObject *parent = 0) : B(parent) { qx::QxModel::init(); } + QxModel(qx::IxModel *other, QObject *parent) : B(parent) { qx::QxModel::initFrom(other); } + virtual ~QxModel() { ; } + + protected: + void init() + { + static_assert(qx_is_valid, "qx_is_valid"); + this->m_pClass = qx::QxClass::getSingleton(); + qAssert(this->m_pClass != NULL); + this->m_pDataMemberX = (this->m_pClass ? this->m_pClass->getDataMemberX() : NULL); + qAssert(this->m_pDataMemberX != NULL); + this->m_pDataMemberId = (this->m_pDataMemberX ? this->m_pDataMemberX->getId_WithDaoStrategy() : NULL); + this->m_pCollection = (&m_model); + this->generateRoleNames(); + } + + void initFrom(qx::IxModel *pOther) + { + init(); + qx::QxModel *pOtherWrk = static_cast *>(pOther); + m_model = pOtherWrk->m_model; + this->m_lManualInsertIndex = pOtherWrk->m_lManualInsertIndex; + this->setParentModel(pOtherWrk->m_pParent); + if (this->m_pParent) + { + this->m_eAutoUpdateDatabase = this->m_pParent->getAutoUpdateDatabase(); + } + this->m_hCustomProperties = pOtherWrk->m_hCustomProperties; + } + + public: + virtual bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) + { + if (parent.isValid()) + { + return false; + } + if ((row < 0) || (count <= 0)) + { + return false; + } + this->beginInsertRows(QModelIndex(), row, (row + count - 1)); + for (int i = 0; i < count; ++i) + { + type_ptr pItem = type_ptr(new T()); + insertItem(row, pItem); + } + this->endInsertRows(); + return true; + } + + virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) + { + IxDataMember *pDataMember = this->getDataMember(column); + if (!pDataMember) + { + return; + } + m_model.sort(qx::model_view::QxModelRowCompare((order == Qt::AscendingOrder), pDataMember)); + this->raiseEvent_layoutChanged(); + } + + virtual bool getShowEmptyLine() const { return m_pDirtyRow.get(); } + + virtual void setShowEmptyLine(bool b) + { + if (b == getShowEmptyLine()) + { + return; + } + if (b) + { + addDirtyRow(); + return; + } + this->beginRemoveRows(QModelIndex(), this->rowCount(), this->rowCount()); + m_pDirtyRow.reset(); + this->endRemoveRows(); + } + + public: + /*! + * \brief Return the number of lines in the table (database) mapped to the C++ class T (registered into QxOrm context) and filtered by a user SQL query + * \param query Define a user SQL query added to default SQL query builded by QxOrm library (optional parameter) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + */ + virtual long qxCount(const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL) + { + return qx::dao::count(query, this->database(pDatabase)); + } + + /*! + * \brief Return the number of lines in the table (database) mapped to the C++ class T (registered into QxOrm context) and filtered by a user SQL query + * \param lCount Output parameter with the number of lines in the table associated to the SQL query + * \param query Define a user SQL query added to default SQL query builded by QxOrm library (optional parameter) + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + */ + virtual QSqlError qxCount(long &lCount, const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL) + { + this->m_lastError = qx::dao::count(lCount, query, this->database(pDatabase)); + return this->m_lastError; + } + + /*! + * \brief Clear the model and fetch an object (retrieve all its properties) of type T (registered into QxOrm context) mapped to a table in the database + * \param id Row id to be fetched (retrieve all properties from database) + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) : use "|" separator to put many relationships keys into this parameter + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxFetchById(const QVariant &id, const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + this->clear(); + type_ptr pItem = type_ptr(new T()); + if (!this->m_pDataMemberId) + { + qDebug("[QxOrm] problem with 'qxFetchById()' method : '%s'", "data member id not registered"); + qAssert(false); + } + if (!this->m_pDataMemberId) + { + this->m_lastError = QSqlError("[QxOrm] problem with 'qxFetchById()' method : 'data member id not registered'", "", QSqlError::UnknownError); + return this->m_lastError; + } + this->m_pDataMemberId->fromVariant(pItem.get(), id); + + type_primary_key primaryKey; + qx::cvt::from_variant(id, primaryKey); + this->beginInsertRows(QModelIndex(), 0, 0); + m_model.insert(primaryKey, pItem); + + if (relation.count() == 0) + { + this->m_lastError = qx::dao::fetch_by_id((*pItem), this->database(pDatabase), this->m_lstColumns); + } + else + { + this->m_lastError = qx::dao::fetch_by_id_with_relation(relation, (*pItem), this->database(pDatabase)); + } + this->updateShowEmptyLine(); + this->endInsertRows(); + return this->m_lastError; + } + + /*! + * \brief Clear the model and fetch a list of objects (retrieve all elements and properties associated) of type T (container registered into QxOrm context) mapped to a table in the database + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) : use "|" separator to put many relationships keys into this parameter + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxFetchAll(const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + this->clear(); + type_collection tmp; + if (relation.count() == 0) + { + this->m_lastError = qx::dao::fetch_all(tmp, this->database(pDatabase), this->m_lstColumns); + } + else + { + this->m_lastError = qx::dao::fetch_all_with_relation(relation, tmp, this->database(pDatabase)); + } + + if (tmp.count() <= 0) + { + return this->m_lastError; + } + this->beginResetModel(); + m_model = tmp; + this->updateShowEmptyLine(); + this->endResetModel(); + return this->m_lastError; + } + + /*! + * \brief Clear the model and fetch a list of objects (retrieve all elements and properties associated) of type T (container registered into QxOrm context) mapped to a table in the database and filtered by a user SQL query + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) : use "|" separator to put many relationships keys into this parameter + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxFetchByQuery(const qx::QxSqlQuery &query, const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + this->clear(); + type_collection tmp; + if (relation.count() == 0) + { + this->m_lastError = qx::dao::fetch_by_query(query, tmp, this->database(pDatabase), this->m_lstColumns); + } + else + { + this->m_lastError = qx::dao::fetch_by_query_with_relation(relation, query, tmp, this->database(pDatabase)); + } + + if (tmp.count() <= 0) + { + return this->m_lastError; + } + this->beginResetModel(); + m_model = tmp; + this->updateShowEmptyLine(); + this->endResetModel(); + return this->m_lastError; + } + + /*! + * \brief Get an item in the model at line row and fetch all its properties mapped to a table in the database, then all views attached to this model are automatically updated + * \param row Get an item in the model at line row + * \param relation List of relationships keys to be fetched (eager fetch instead of default lazy fetch for a relation) : use "|" separator to put many relationships keys into this parameter + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxFetchRow(int row, const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + type_ptr pItem = getRowItemAt(row); + if (!pItem) + { + return QSqlError(); + } + if (relation.count() == 0) + { + this->m_lastError = qx::dao::fetch_by_id((*pItem), this->database(pDatabase), this->m_lstColumns); + } + else + { + this->m_lastError = qx::dao::fetch_by_id_with_relation(relation, (*pItem), this->database(pDatabase)); + } + if (this->m_lastError.isValid()) + { + return this->m_lastError; + } + + QModelIndex idxTopLeft = this->index(row, 0); + QModelIndex idxBottomRight = this->index(row, (this->m_lstDataMember.count() - 1)); + this->raiseEvent_dataChanged(idxTopLeft, idxBottomRight); + updateKey(row); + return this->m_lastError; + } + + /*! + * \brief Insert all items in the model into database + * \param relation List of relationships keys to be inserted in others tables of database : use "|" separator to put many relationships keys into this parameter + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance inserting a list of instances to database (but doesn't fill the last inserted identifier in the C++ instances) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxInsert(const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + if (relation.count() > 0) + { + this->syncAllNestedModel(relation); + } + if (relation.count() == 0) + { + this->m_lastError = qx::dao::insert(m_model, this->database(pDatabase), bUseExecBatch); + } + else + { + this->m_lastError = qx::dao::insert_with_relation(relation, m_model, this->database(pDatabase)); + } + if (!this->m_lastError.isValid()) + { + this->updateAllKeys(); + } + return this->m_lastError; + } + + /*! + * \brief Insert an item of the model at line row into database + * \param row Insert an item in the model at line row + * \param relation List of relationships keys to be inserted in others tables of database : use "|" separator to put many relationships keys into this parameter + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxInsertRow(int row, const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + type_ptr pItem = getRowItemAt(row); + if (!pItem) + { + return QSqlError(); + } + if (relation.count() > 0) + { + this->syncNestedModel(row, relation); + } + if (relation.count() == 0) + { + this->m_lastError = qx::dao::insert((*pItem), this->database(pDatabase)); + } + else + { + this->m_lastError = qx::dao::insert_with_relation(relation, (*pItem), this->database(pDatabase)); + } + if (!this->m_lastError.isValid()) + { + updateKey(row); + } + return this->m_lastError; + } + + /*! + * \brief Update all items in the model into database + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param relation List of relationships keys to be inserted in others tables of database : use "|" separator to put many relationships keys into this parameter + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \param bUseExecBatch If true then use the QSqlQuery::execBatch() method to improve performance updating a list of instances in database + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxUpdate(const qx::QxSqlQuery &query = qx::QxSqlQuery(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + if (relation.count() > 0) + { + this->syncAllNestedModel(relation); + } + if (relation.count() == 0) + { + this->m_lastError = qx::dao::update_by_query(query, m_model, this->database(pDatabase), this->m_lstColumns, bUseExecBatch); + } + else + { + this->m_lastError = qx::dao::update_by_query_with_relation(relation, query, m_model, this->database(pDatabase)); + } + if (!this->m_lastError.isValid()) + { + this->updateAllKeys(); + } + return this->m_lastError; + } + + /*! + * \brief Update an item of the model at line row into database + * \param row Update an item in the model at line row + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param relation List of relationships keys to be inserted in others tables of database : use "|" separator to put many relationships keys into this parameter + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxUpdateRow(int row, const qx::QxSqlQuery &query = qx::QxSqlQuery(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + if ((row < 0) || (row >= m_model.count())) + { + return QSqlError(); + } + if (relation.count() > 0) + { + this->syncNestedModel(row, relation); + } + type_ptr pItem = m_model.getByIndex(row); + if (!pItem) + { + return QSqlError(); + } + if (relation.count() == 0) + { + this->m_lastError = qx::dao::update_by_query(query, (*pItem), this->database(pDatabase), this->m_lstColumns); + } + else + { + this->m_lastError = qx::dao::update_by_query_with_relation(relation, query, (*pItem), this->database(pDatabase)); + } + if (!this->m_lastError.isValid()) + { + updateKey(row); + } + return this->m_lastError; + } + + /*! + * \brief Save all items (insert or update) in the model into database + * \param relation List of relationships keys to be inserted in others tables of database : use "|" separator to put many relationships keys into this parameter + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxSave(const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + if (relation.count() > 0) + { + this->syncAllNestedModel(relation); + } + if (relation.count() == 0) + { + this->m_lastError = qx::dao::save(m_model, this->database(pDatabase)); + } + else + { + this->m_lastError = qx::dao::save_with_relation(relation, m_model, this->database(pDatabase)); + } + if (!this->m_lastError.isValid()) + { + this->updateAllKeys(); + } + return this->m_lastError; + } + + /*! + * \brief Save an item of the model at line row into database + * \param row Save an item (insert or update) in the model at line row + * \param relation List of relationships keys to be inserted in others tables of database : use "|" separator to put many relationships keys into this parameter + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxSaveRow(int row, const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + if ((row < 0) || (row >= m_model.count())) + { + return QSqlError(); + } + if (relation.count() > 0) + { + this->syncNestedModel(row, relation); + } + type_ptr pItem = m_model.getByIndex(row); + if (!pItem) + { + return QSqlError(); + } + if (relation.count() == 0) + { + this->m_lastError = qx::dao::save((*pItem), this->database(pDatabase)); + } + else + { + this->m_lastError = qx::dao::save_with_relation(relation, (*pItem), this->database(pDatabase)); + } + if (!this->m_lastError.isValid()) + { + updateKey(row); + } + return this->m_lastError; + } + + /*! + * \brief Used internally by qx::IxModel::setData() method with e_auto_update_on_field_change option, save an item (even if it is the dirty row item) of the model at line row into database + * \param row Save an item (insert or update) in the model at line row + * \param column List of columns of model to save in database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxSaveRowData(int row, const QStringList &column = QStringList(), QSqlDatabase *pDatabase = NULL) + { + if (!this->m_pDataMemberId) + { + this->m_lastError = QSqlError("[QxOrm] problem with 'qxSaveRowData()' method : 'data member id not registered'", "", QSqlError::UnknownError); + return this->m_lastError; + } + type_ptr pItem = getRowItemAt(row); + if (!pItem) + { + return QSqlError(); + } + QVariant id = this->m_pDataMemberId->toVariant(pItem.get()); + bool bExist = qx::trait::is_valid_primary_key(id); + if (bExist) + { + bExist = qx::dao::exist((*pItem), this->database(pDatabase)); + } + if (bExist) + { + this->m_lastError = qx::dao::update((*pItem), this->database(pDatabase), column); + } + else + { + this->m_lastError = qx::dao::insert((*pItem), this->database(pDatabase)); + } + return this->m_lastError; + } + + /*! + * \brief Delete a line of a table (database) mapped to a C++ object of type T (registered into QxOrm context), if no error occurred then you should remove row from the model + * \param id Row id to be deleted from database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxDeleteById(const QVariant &id, QSqlDatabase *pDatabase = NULL) + { + type_ptr pItem = type_ptr(new T()); + if (!this->m_pDataMemberId) + { + qDebug("[QxOrm] problem with 'qxDeleteById()' method : '%s'", "data member id not registered"); + qAssert(false); + } + if (!this->m_pDataMemberId) + { + this->m_lastError = QSqlError("[QxOrm] problem with 'qxDeleteById()' method : 'data member id not registered'", "", QSqlError::UnknownError); + return this->m_lastError; + } + this->m_pDataMemberId->fromVariant(pItem.get(), id); + this->m_lastError = qx::dao::delete_by_id((*pItem), this->database(pDatabase)); + return this->m_lastError; + } + + /*! + * \brief Delete all lines of a table (database) mapped to a C++ class T (registered into QxOrm context), if no error occurred then you should clear the model + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxDeleteAll(QSqlDatabase *pDatabase = NULL) + { + this->m_lastError = qx::dao::delete_all(this->database(pDatabase)); + return this->m_lastError; + } + + /*! + * \brief Delete all lines of a table (database) mapped to a C++ class T (registered into QxOrm context) and filtered by a user SQL query, if no error occurred then you should refresh the model + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxDeleteByQuery(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) + { + this->m_lastError = qx::dao::delete_by_query(query, this->database(pDatabase)); + return this->m_lastError; + } + + /*! + * \brief Delete in database the item at line row in the model, if no error occurred then you should remove row from the model + * \param row Delete in database the item in the model at line row + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxDeleteRow(int row, QSqlDatabase *pDatabase = NULL) + { + if ((row < 0) || (row >= m_model.count())) + { + return QSqlError(); + } + type_ptr pItem = m_model.getByIndex(row); + if (!pItem) + { + return QSqlError(); + } + this->m_lastError = qx::dao::delete_by_id((*pItem), this->database(pDatabase)); + return this->m_lastError; + } + + /*! + * \brief Delete a line of a table (even if a logical delete is defined) mapped to a C++ object of type T (registered into QxOrm context), if no error occurred then you should remove row from the model + * \param id Row id to be deleted from database + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxDestroyById(const QVariant &id, QSqlDatabase *pDatabase = NULL) + { + type_ptr pItem = type_ptr(new T()); + if (!this->m_pDataMemberId) + { + qDebug("[QxOrm] problem with 'qxDeleteById()' method : '%s'", "data member id not registered"); + qAssert(false); + } + if (!this->m_pDataMemberId) + { + this->m_lastError = QSqlError("[QxOrm] problem with 'qxDeleteById()' method : 'data member id not registered'", "", QSqlError::UnknownError); + return this->m_lastError; + } + this->m_pDataMemberId->fromVariant(pItem.get(), id); + this->m_lastError = qx::dao::destroy_by_id((*pItem), this->database(pDatabase)); + return this->m_lastError; + } + + /*! + * \brief Delete all lines of a table (even if a logical delete is defined) mapped to a C++ class T (registered into QxOrm context), if no error occurred then you should clear the model + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxDestroyAll(QSqlDatabase *pDatabase = NULL) + { + this->m_lastError = qx::dao::destroy_all(this->database(pDatabase)); + return this->m_lastError; + } + + /*! + * \brief Delete all lines of a table (even if a logical delete is defined) mapped to a C++ class T (registered into QxOrm context) and filtered by a user SQL query, if no error occurred then you should refresh the model + * \param query Define a user SQL query added to default SQL query builded by QxOrm library + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxDestroyByQuery(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) + { + this->m_lastError = qx::dao::destroy_by_query(query, this->database(pDatabase)); + return this->m_lastError; + } + + /*! + * \brief Delete in database (even if a logical delete is defined) the item at line row in the model, if no error occurred then you should remove row from the model + * \param row Delete in database the item in the model at line row + * \param pDatabase Connection to database (you can manage your own connection pool for example, you can also define a transaction, etc.); if NULL, a valid connection for the current thread is provided by qx::QxSqlDatabase singleton class (optional parameter) + * \return Empty QSqlError object (from Qt library) if no error occurred; otherwise QSqlError contains a description of database error executing SQL query + */ + virtual QSqlError qxDestroyRow(int row, QSqlDatabase *pDatabase = NULL) + { + if ((row < 0) || (row >= m_model.count())) + { + return QSqlError(); + } + type_ptr pItem = m_model.getByIndex(row); + if (!pItem) + { + return QSqlError(); + } + this->m_lastError = qx::dao::destroy_by_id((*pItem), this->database(pDatabase)); + return this->m_lastError; + } + + virtual QSqlError qxExecuteQuery(qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) + { + this->clear(); + type_collection tmp; + this->m_lastError = qx::dao::execute_query(query, tmp, this->database(pDatabase)); + + if (tmp.count() <= 0) + { + return this->m_lastError; + } + this->beginResetModel(); + m_model = tmp; + this->updateShowEmptyLine(); + this->endResetModel(); + return this->m_lastError; + } + + virtual qx_bool qxExist(const QVariant &id, QSqlDatabase *pDatabase = NULL) + { + type_ptr pItem = type_ptr(new T()); + if (!this->m_pDataMemberId) + { + qDebug("[QxOrm] problem with 'qxExist()' method : '%s'", "data member id not registered"); + qAssert(false); + } + if (!this->m_pDataMemberId) + { + return qx_bool(false); + } + this->m_pDataMemberId->fromVariant(pItem.get(), id); + return qx::dao::exist((*pItem), this->database(pDatabase)); + } + + virtual qx::QxInvalidValueX qxValidate(const QStringList &groups = QStringList()) + { + return qx::validate(m_model, groups); + } + + virtual qx::QxInvalidValueX qxValidateRow(int row, const QStringList &groups = QStringList()) + { + type_ptr pItem = getRowItemAt(row); + if (!pItem) + { + return qx::QxInvalidValueX(); + } + return qx::validate((*pItem), groups); + } + + protected: + type_ptr getRowItemAt(int row) const + { + if ((row >= 0) && (row < m_model.count())) + { + return m_model.getByIndex(row); + } + if (m_pDirtyRow && (m_pDirtyRow->first == row)) + { + return m_pDirtyRow->second; + } + return type_ptr(); + } + + virtual void *getRowItemAsVoidPtr(int row) const { return getRowItemAt(row).get(); } + + virtual void dumpModelImpl(bool bJsonFormat) const { qx::dump(m_model, bJsonFormat); } + + virtual QObject *cloneModelImpl() + { + qx::QxModel *pClone = new qx::QxModel(this, NULL); + std::shared_ptr pModel = qx::clone(pClone->m_model); + if (pModel) + { + pClone->m_model = (*pModel); + } + return static_cast(pClone); + } + + virtual void updateShowEmptyLine() + { + if (m_pDirtyRow) + { + m_pDirtyRow->first = m_model.count(); + } + } + + virtual bool isDirtyRow(int row) const { return (m_pDirtyRow && (m_pDirtyRow->first == row)); } + + virtual void insertDirtyRowToModel() + { + if (!m_pDirtyRow) + { + return; + } + int row = m_pDirtyRow->first; + insertItem(row, m_pDirtyRow->second); + if (this->m_pParent) + { + this->m_pParent->saveChildRelations(this); + } + updateKey(row); + addDirtyRow(); + } + + void addDirtyRow() + { + this->beginInsertRows(QModelIndex(), m_model.count(), m_model.count()); + m_pDirtyRow.reset(new QPair(m_model.count(), type_ptr(new T()))); + IxSqlRelation *pRelationParent = (this->m_pDataMemberRelationToParent ? this->m_pDataMemberRelationToParent->getSqlRelation() : NULL); + IxSqlRelation::relation_type eRelationParentType = (pRelationParent ? pRelationParent->getRelationType() : IxSqlRelation::no_relation); + if (this->m_pParent && ((eRelationParentType == IxSqlRelation::many_to_many) || (eRelationParentType == IxSqlRelation::many_to_one))) + { + this->m_pDataMemberRelationToParent->fromVariant(m_pDirtyRow->second.get(), this->m_pParent->getIdFromChild(this)); + } + this->endInsertRows(); + } + + void insertItem(int row, const type_ptr &pItem) + { + if (!pItem) + { + return; + } + type_primary_key primaryKey; + this->m_lManualInsertIndex = (this->m_lManualInsertIndex - 1); + QVariant vNewId(static_cast(this->m_lManualInsertIndex)); + qx::cvt::from_variant(vNewId, primaryKey); + m_model.insert(row, primaryKey, pItem); + this->updateShowEmptyLine(); + } + + void updateKey(int row) + { + if ((row < 0) || (row >= m_model.count())) + { + return; + } + type_ptr pItem = m_model.getByIndex(row); + if (!pItem || !this->m_pDataMemberId) + { + return; + } + type_primary_key currPrimaryKey = m_model.getKeyByIndex(row); + QVariant vCurrPrimaryKey = qx::cvt::to_variant(currPrimaryKey); + QVariant vNextPrimaryKey = this->m_pDataMemberId->toVariant(pItem.get()); + if ((vCurrPrimaryKey == vNextPrimaryKey) || (!vNextPrimaryKey.isValid())) + { + return; + } + if (!qx::trait::is_valid_primary_key(vNextPrimaryKey)) + { + return; + } + type_primary_key updatedPrimaryKey; + qx::cvt::from_variant(vNextPrimaryKey, updatedPrimaryKey); + if (m_model.exist(updatedPrimaryKey)) + { + return; + } + m_model.removeByIndex(row); + m_model.insert(row, updatedPrimaryKey, pItem); + } + + void updateAllKeys() + { + for (long l = 0; l < m_model.count(); l++) + { + updateKey(l); + } + } + + protected: +#ifndef _QX_NO_JSON + + virtual QString toJson_Helper(int row) const + { + if (row == -1) + { + return qx::serialization::json::to_string(m_model); + } + type_ptr pItem = getRowItemAt(row); + if (!pItem) + { + return QString(); + } + return qx::serialization::json::to_string(*pItem); + } + + virtual bool fromJson_Helper(const QString &json, int row) + { + if (row == -1) + { + this->clear(); + type_collection tmp; + if (!qx::serialization::json::from_string(tmp, json)) + { + return false; + } + this->beginResetModel(); + m_model = tmp; + this->updateShowEmptyLine(); + this->endResetModel(); + return true; + } + + type_ptr pItem = getRowItemAt(row); + if (!pItem) + { + return false; + } + if (!qx::serialization::json::from_string((*pItem), json)) + { + return false; + } + + QModelIndex idxTopLeft = this->index(row, 0); + QModelIndex idxBottomRight = this->index(row, (this->m_lstDataMember.count() - 1)); + this->raiseEvent_dataChanged(idxTopLeft, idxBottomRight); + updateKey(row); + return true; + } + + virtual QVariant getRelationshipValues_Helper(int row, const QString &relation, bool bLoadFromDatabase, const QString &sAppendRelations) + { + if ((row < 0) || (row >= m_model.count())) + { + return QVariant(); + } + if (!this->m_pDataMemberId || !this->m_pDataMemberX || !this->m_pDataMemberX->exist(relation)) + { + return QVariant(); + } + IxDataMember *pDataMember = this->m_pDataMemberX->get_WithDaoStrategy(relation); + if (!pDataMember) + { + return QVariant(); + } + IxSqlRelation *pRelation = (pDataMember->hasSqlRelation() ? pDataMember->getSqlRelation() : NULL); + if (!pRelation) + { + return QVariant(); + } + type_ptr pItem = m_model.getByIndex(row); + if (!pItem) + { + return QVariant(); + } + type_ptr pItemTemp = pItem; + + if (bLoadFromDatabase) + { + QString sRelation = relation; + if (!sAppendRelations.isEmpty() && !sAppendRelations.startsWith("->") && !sAppendRelations.startsWith(">>")) + { + sRelation += "->" + sAppendRelations; + } + else if (!sAppendRelations.isEmpty()) + { + sRelation += sAppendRelations; + } + pItemTemp = type_ptr(new T()); + QVariant id = this->m_pDataMemberId->toVariant(pItem.get()); + this->m_pDataMemberId->fromVariant(pItemTemp.get(), id); + QSqlError daoError = qx::dao::fetch_by_id_with_relation(sRelation, (*pItemTemp)); + if (daoError.isValid()) + { + return QVariant(); + } + } + + QJsonValue json = pDataMember->toJson(pItemTemp.get()); + if (json.isNull()) + { + return QVariant(); + } + if (json.isArray()) + { + return json.toArray().toVariantList(); + } + return json.toObject().toVariantMap(); + } + + virtual bool setRelationshipValues_Helper(int row, const QString &relation, const QVariant &values) + { + if ((row < 0) || (row >= m_model.count())) + { + return false; + } + if ((values.type() != QVariant::List) && (values.type() != QVariant::Map)) + { + return false; + } + if (!this->m_pDataMemberId || !this->m_pDataMemberX || !this->m_pDataMemberX->exist(relation)) + { + return false; + } + IxDataMember *pDataMember = this->m_pDataMemberX->get_WithDaoStrategy(relation); + if (!pDataMember) + { + return false; + } + IxSqlRelation *pRelation = (pDataMember->hasSqlRelation() ? pDataMember->getSqlRelation() : NULL); + if (!pRelation) + { + return false; + } + type_ptr pItem = m_model.getByIndex(row); + if (!pItem) + { + return false; + } + + QJsonValue json; + if (values.type() == QVariant::List) + { + json = QJsonArray::fromVariantList(values.toList()); + } + else if (values.type() == QVariant::Map) + { + json = QJsonObject::fromVariantMap(values.toMap()); + } + if (!pDataMember->fromJson(pItem.get(), json)) + { + return false; + } + + QModelIndex idxTopLeft = this->index(row, 0); + QModelIndex idxBottomRight = this->index(row, (this->m_lstDataMember.count() - 1)); + this->raiseEvent_dataChanged(idxTopLeft, idxBottomRight); + return true; + } + +#endif // _QX_NO_JSON + }; + +} // namespace qx + +#endif // _QX_MODEL_H_ diff --git a/include/QxModelView/QxModelRowCompare.h b/include/QxModelView/QxModelRowCompare.h new file mode 100644 index 0000000..8ef96f5 --- /dev/null +++ b/include/QxModelView/QxModelRowCompare.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_MODEL_ROW_COMPARE_H_ +#define _QX_MODEL_ROW_COMPARE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxModelRowCompare.h + * \author XDL Team + * \ingroup QxModelView + * \brief Functor used to provide sort feature for all models based on qx::IxModel interface (please note that you can also use QSortFilterProxyModel Qt class to sort your model) + */ + +#include + +namespace qx +{ + namespace model_view + { + + QX_DLL_EXPORT bool compareQVariant(const QVariant &v1, const QVariant &v2, bool bAscending) QX_USED; + + /*! + * \ingroup QxModelView + * \brief qx::QxModelRowCompare : functor used to provide sort feature for all models based on qx::IxModel interface (please note that you can also use QSortFilterProxyModel Qt class to sort your model) + */ + template + struct QxModelRowCompare + { + + bool m_bAscendingOrder; //!< Sort your model using ascending or descending order + qx::IxDataMember *m_pDataMember; //!< Data member associated to the model column to sort + + QxModelRowCompare(bool bAscendingOrder, qx::IxDataMember *pDataMember) : m_bAscendingOrder(bAscendingOrder), m_pDataMember(pDataMember) { qAssert(m_pDataMember != NULL); } + + bool operator()(const T &t1, const T &t2) const + { + if (!m_pDataMember || !t1.second || !t2.second) + { + return false; + } + QVariant v1 = m_pDataMember->toVariant(&(*t1.second)); + QVariant v2 = m_pDataMember->toVariant(&(*t2.second)); + return qx::model_view::compareQVariant(v1, v2, m_bAscendingOrder); + } + }; + + } // namespace model_view +} // namespace qx + +#endif // _QX_MODEL_ROW_COMPARE_H_ diff --git a/include/QxModelView/QxModelService.h b/include/QxModelView/QxModelService.h new file mode 100644 index 0000000..249cba6 --- /dev/null +++ b/include/QxModelView/QxModelService.h @@ -0,0 +1,577 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_MODEL_SERVICE_H_ +#define _QX_MODEL_SERVICE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxModelService.h + * \author XDL Team + * \ingroup QxModelView + * \brief qx::QxModelService provides an easy way to connect your model to the QxService module (all queries are executed over network using client/server communication) + */ + +#include + +namespace qx +{ + + /*! + * \ingroup QxModelView + * \brief qx::QxModelService : provides an easy way to connect your model to the QxService module (all queries are executed over network using client/server communication) + * + * T template parameter is a persistent class registered into QxOrm context + * S template parameter is a service class generated by QxEntityEditor (or your own service class providing all methods) + */ + template + class QxModelService : public qx::QxModel + { + + public: + typedef typename qx::QxModel::type_ptr type_ptr; + typedef typename qx::QxModel::type_primary_key type_primary_key; + typedef typename qx::QxModel::type_collection type_collection; + typedef std::shared_ptr type_collection_ptr; + typedef B type_base_class; + + public: + QxModelService(QObject *parent = 0) : qx::QxModel(parent) { ; } + QxModelService(qx::IxModel *other, QObject *parent) : qx::QxModel(other, parent) { ; } + virtual ~QxModelService() { ; } + + virtual long qxCount(const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL) + { + long lCount = 0; + this->qxCount(lCount, query, pDatabase); + return lCount; + } + + virtual QSqlError qxCount(long &lCount, const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(pDatabase); + S services; + this->m_lastError = services.count(lCount, query); + return this->m_lastError; + } + + virtual QSqlError qxFetchById(const QVariant &id, const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(pDatabase); + this->clear(); + type_ptr pItem = type_ptr(new T()); + if (!this->m_pDataMemberId) + { + qDebug("[QxOrm] problem with 'qxFetchById()' method : '%s'", "data member id not registered"); + qAssert(false); + } + if (!this->m_pDataMemberId) + { + this->m_lastError = QSqlError("[QxOrm] problem with 'qxFetchById()' method : 'data member id not registered'", "", QSqlError::UnknownError); + return this->m_lastError; + } + this->m_pDataMemberId->fromVariant(pItem.get(), id); + + type_primary_key primaryKey; + qx::cvt::from_variant(id, primaryKey); + this->beginInsertRows(QModelIndex(), 0, 0); + this->m_model.insert(primaryKey, pItem); + + S services; + this->m_lastError = services.fetchById(pItem, this->m_lstColumns, relation); + this->updateShowEmptyLine(); + this->endInsertRows(); + return this->m_lastError; + } + + virtual QSqlError qxFetchAll(const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(pDatabase); + this->clear(); + + S services; + type_collection_ptr tmp = std::make_shared(); + this->m_lastError = services.fetchAll(tmp, this->m_lstColumns, relation); + + if (tmp->count() <= 0) + { + return this->m_lastError; + } + this->beginResetModel(); + this->m_model = (*tmp); + this->updateShowEmptyLine(); + this->endResetModel(); + return this->m_lastError; + } + + virtual QSqlError qxFetchByQuery(const qx::QxSqlQuery &query, const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(pDatabase); + this->clear(); + + S services; + type_collection_ptr tmp = std::make_shared(); + this->m_lastError = services.fetchByQuery(query, tmp, this->m_lstColumns, relation); + + if (tmp->count() <= 0) + { + return this->m_lastError; + } + this->beginResetModel(); + this->m_model = (*tmp); + this->updateShowEmptyLine(); + this->endResetModel(); + return this->m_lastError; + } + + virtual QSqlError qxFetchRow(int row, const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(pDatabase); + if ((row < 0) || (row >= this->m_model.count())) + { + return QSqlError(); + } + type_ptr pItem = this->m_model.getByIndex(row); + if (!pItem) + { + return QSqlError(); + } + + S services; + this->m_lastError = services.fetchById(pItem, this->m_lstColumns, relation); + if (this->m_lastError.isValid()) + { + return this->m_lastError; + } + + QModelIndex idxTopLeft = this->index(row, 0); + QModelIndex idxBottomRight = this->index(row, (this->m_lstDataMember.count() - 1)); + this->raiseEvent_dataChanged(idxTopLeft, idxBottomRight); + this->updateKey(row); + return this->m_lastError; + } + + virtual QSqlError qxInsert(const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + Q_UNUSED(pDatabase); + Q_UNUSED(bUseExecBatch); + if (relation.count() > 0) + { + this->syncAllNestedModel(relation); + } + + type_collection_ptr tmp = std::make_shared(); + (*tmp) = this->m_model; + + S services; + this->m_lastError = services.insert(tmp, relation); + + if (!this->m_lastError.isValid()) + { + this->updateAllKeys(); + } + return this->m_lastError; + } + + virtual QSqlError qxInsertRow(int row, const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(pDatabase); + if ((row < 0) || (row >= this->m_model.count())) + { + return QSqlError(); + } + if (relation.count() > 0) + { + this->syncNestedModel(row, relation); + } + type_ptr pItem = this->m_model.getByIndex(row); + if (!pItem) + { + return QSqlError(); + } + + S services; + this->m_lastError = services.insert(pItem, relation); + + if (!this->m_lastError.isValid()) + { + this->updateKey(row); + } + return this->m_lastError; + } + + virtual QSqlError qxUpdate(const qx::QxSqlQuery &query = qx::QxSqlQuery(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL, bool bUseExecBatch = false) + { + Q_UNUSED(pDatabase); + Q_UNUSED(bUseExecBatch); + if (relation.count() > 0) + { + this->syncAllNestedModel(relation); + } + + type_collection_ptr tmp = std::make_shared(); + (*tmp) = this->m_model; + + S services; + this->m_lastError = services.update(tmp, query, this->m_lstColumns, relation); + + if (!this->m_lastError.isValid()) + { + this->updateAllKeys(); + } + return this->m_lastError; + } + + virtual QSqlError qxUpdateRow(int row, const qx::QxSqlQuery &query = qx::QxSqlQuery(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(pDatabase); + if ((row < 0) || (row >= this->m_model.count())) + { + return QSqlError(); + } + if (relation.count() > 0) + { + this->syncNestedModel(row, relation); + } + type_ptr pItem = this->m_model.getByIndex(row); + if (!pItem) + { + return QSqlError(); + } + + S services; + this->m_lastError = services.update(pItem, query, this->m_lstColumns, relation); + + if (!this->m_lastError.isValid()) + { + this->updateKey(row); + } + return this->m_lastError; + } + + virtual QSqlError qxSave(const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(pDatabase); + if (relation.count() > 0) + { + this->syncAllNestedModel(relation); + } + + type_collection_ptr tmp = std::make_shared(); + (*tmp) = this->m_model; + + S services; + this->m_lastError = services.save(tmp, relation); + + if (!this->m_lastError.isValid()) + { + this->updateAllKeys(); + } + return this->m_lastError; + } + + virtual QSqlError qxSaveRow(int row, const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(pDatabase); + if ((row < 0) || (row >= this->m_model.count())) + { + return QSqlError(); + } + if (relation.count() > 0) + { + this->syncNestedModel(row, relation); + } + type_ptr pItem = this->m_model.getByIndex(row); + if (!pItem) + { + return QSqlError(); + } + + S services; + this->m_lastError = services.save(pItem, relation); + + if (!this->m_lastError.isValid()) + { + this->updateKey(row); + } + return this->m_lastError; + } + + virtual QSqlError qxDeleteById(const QVariant &id, QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(pDatabase); + type_ptr pItem = type_ptr(new T()); + if (!this->m_pDataMemberId) + { + qDebug("[QxOrm] problem with 'qxDeleteById()' method : '%s'", "data member id not registered"); + qAssert(false); + } + if (!this->m_pDataMemberId) + { + this->m_lastError = QSqlError("[QxOrm] problem with 'qxDeleteById()' method : 'data member id not registered'", "", QSqlError::UnknownError); + return this->m_lastError; + } + this->m_pDataMemberId->fromVariant(pItem.get(), id); + + S services; + this->m_lastError = services.deleteById(pItem); + return this->m_lastError; + } + + virtual QSqlError qxDeleteAll(QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(pDatabase); + S services; + this->m_lastError = services.deleteAll(); + return this->m_lastError; + } + + virtual QSqlError qxDeleteByQuery(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(pDatabase); + S services; + this->m_lastError = services.deleteByQuery(query); + return this->m_lastError; + } + + virtual QSqlError qxDeleteRow(int row, QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(pDatabase); + if ((row < 0) || (row >= this->m_model.count())) + { + return QSqlError(); + } + type_ptr pItem = this->m_model.getByIndex(row); + if (!pItem) + { + return QSqlError(); + } + + S services; + this->m_lastError = services.deleteById(pItem); + return this->m_lastError; + } + + virtual QSqlError qxDestroyById(const QVariant &id, QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(pDatabase); + type_ptr pItem = type_ptr(new T()); + if (!this->m_pDataMemberId) + { + qDebug("[QxOrm] problem with 'qxDeleteById()' method : '%s'", "data member id not registered"); + qAssert(false); + } + if (!this->m_pDataMemberId) + { + this->m_lastError = QSqlError("[QxOrm] problem with 'qxDeleteById()' method : 'data member id not registered'", "", QSqlError::UnknownError); + return this->m_lastError; + } + this->m_pDataMemberId->fromVariant(pItem.get(), id); + + S services; + this->m_lastError = services.destroyById(pItem); + return this->m_lastError; + } + + virtual QSqlError qxDestroyAll(QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(pDatabase); + S services; + this->m_lastError = services.destroyAll(); + return this->m_lastError; + } + + virtual QSqlError qxDestroyByQuery(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(pDatabase); + S services; + this->m_lastError = services.destroyByQuery(query); + return this->m_lastError; + } + + virtual QSqlError qxDestroyRow(int row, QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(pDatabase); + if ((row < 0) || (row >= this->m_model.count())) + { + return QSqlError(); + } + type_ptr pItem = this->m_model.getByIndex(row); + if (!pItem) + { + return QSqlError(); + } + + S services; + this->m_lastError = services.destroyById(pItem); + return this->m_lastError; + } + + virtual QSqlError qxExecuteQuery(qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(pDatabase); + this->clear(); + + S services; + type_collection_ptr tmp = std::make_shared(); + this->m_lastError = services.executeQuery(query, tmp); + + if (tmp->count() <= 0) + { + return this->m_lastError; + } + this->beginResetModel(); + this->m_model = (*tmp); + this->updateShowEmptyLine(); + this->endResetModel(); + return this->m_lastError; + } + + virtual qx_bool qxExist(const QVariant &id, QSqlDatabase *pDatabase = NULL) + { + Q_UNUSED(pDatabase); + type_ptr pItem = type_ptr(new T()); + if (!this->m_pDataMemberId) + { + qDebug("[QxOrm] problem with 'qxExist()' method : '%s'", "data member id not registered"); + qAssert(false); + } + if (!this->m_pDataMemberId) + { + return qx_bool(false); + } + this->m_pDataMemberId->fromVariant(pItem.get(), id); + + S services; + return services.exist(pItem); + } + + virtual qx::QxInvalidValueX qxValidate(const QStringList &groups = QStringList()) + { + Q_UNUSED(groups); + S services; + type_collection_ptr tmp = std::make_shared(); + (*tmp) = this->m_model; + return services.isValid(tmp); + } + + virtual qx::QxInvalidValueX qxValidateRow(int row, const QStringList &groups = QStringList()) + { + Q_UNUSED(groups); + if ((row < 0) || (row >= this->m_model.count())) + { + return qx::QxInvalidValueX(); + } + type_ptr pItem = this->m_model.getByIndex(row); + if (!pItem) + { + return qx::QxInvalidValueX(); + } + + S services; + return services.isValid(pItem); + } + + protected: +#ifndef _QX_NO_JSON + + virtual QVariant getRelationshipValues_Helper(int row, const QString &relation, bool bLoadFromDatabase, const QString &sAppendRelations) + { + if ((row < 0) || (row >= this->m_model.count())) + { + return QVariant(); + } + if (!this->m_pDataMemberId || !this->m_pDataMemberX || !this->m_pDataMemberX->exist(relation)) + { + return QVariant(); + } + IxDataMember *pDataMember = this->m_pDataMemberX->get_WithDaoStrategy(relation); + if (!pDataMember) + { + return QVariant(); + } + IxSqlRelation *pRelation = (pDataMember->hasSqlRelation() ? pDataMember->getSqlRelation() : NULL); + if (!pRelation) + { + return QVariant(); + } + type_ptr pItem = this->m_model.getByIndex(row); + if (!pItem) + { + return QVariant(); + } + type_ptr pItemTemp = pItem; + + if (bLoadFromDatabase) + { + QString sRelation = relation; + if (!sAppendRelations.isEmpty() && !sAppendRelations.startsWith("->") && !sAppendRelations.startsWith(">>")) + { + sRelation += "->" + sAppendRelations; + } + else if (!sAppendRelations.isEmpty()) + { + sRelation += sAppendRelations; + } + pItemTemp = type_ptr(new T()); + QVariant id = this->m_pDataMemberId->toVariant(pItem.get()); + this->m_pDataMemberId->fromVariant(pItemTemp.get(), id); + + S services; + QStringList columns; + QSqlError daoError = services.fetchById(pItemTemp, columns, QStringList() << sRelation); + if (daoError.isValid()) + { + return QVariant(); + } + } + + QJsonValue json = pDataMember->toJson(pItemTemp.get()); + if (json.isNull()) + { + return QVariant(); + } + if (json.isArray()) + { + return json.toArray().toVariantList(); + } + return json.toObject().toVariantMap(); + } + +#endif // _QX_NO_JSON + }; + +} // namespace qx + +#endif // _QX_MODEL_SERVICE_H_ diff --git a/include/QxModelView/QxNestedModel.h b/include/QxModelView/QxNestedModel.h new file mode 100644 index 0000000..8433386 --- /dev/null +++ b/include/QxModelView/QxNestedModel.h @@ -0,0 +1,483 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NESTED_MODEL_H_ +#define _QX_NESTED_MODEL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxNestedModel.h + * \author XDL Team + * \ingroup QxModelView + * \brief qx::model_view::create_nested_model is used by QxEntityEditor to manage complex data structure to work with relationships in QML views and Qt model/view architecture + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace model_view + { + + template + qx::IxModel *create_nested_model(qx::IxModel *pParent, const QModelIndex &idxParent, T &t); + + template + qx::IxModel *create_nested_model_with_type(qx::IxModel *pParent, const QModelIndex &idxParent, T &t, M *pModelType); + + template + void sync_nested_model(qx::IxModel *pModel, T &t); + + } // namespace model_view +} // namespace qx + +namespace qx +{ + namespace model_view + { + namespace detail + { + + template + struct QxNestedModel; + + template + struct QxNestedModel_Helper + { + + static std::shared_ptr clone(T &t) + { + std::shared_ptr p = std::make_shared(); + (*p) = t; + return p; + } + + static void synchronize(T &t1, T &t2) + { + t1 = t2; + } + }; + + template + struct QxNestedModel_Helper + { + + static std::shared_ptr clone(T &t) + { + std::shared_ptr p; + p.reset(qx::clone_to_nude_ptr(t)); + qAssert(p); + return p; + } + + static void synchronize(T &t1, T &t2) + { + qx::IxClass *pClass = qx::QxClass::getSingleton(); + qx::IxDataMemberX *pDataMemberX = (pClass ? pClass->getDataMemberX() : NULL); + for (long l = 0; (pDataMemberX && (l < pDataMemberX->count_WithDaoStrategy())); l++) + { + qx::IxDataMember *pDataMember = pDataMemberX->get_WithDaoStrategy(l); + if (!pDataMember) + { + continue; + } + QVariant value = pDataMember->toVariant(&t2); + pDataMember->fromVariant((&t1), value); + } + } + }; + + template + struct QxNestedModel_Creator + { + static qx::IxModel *newModel(qx::IxModel *pParent) { return new M(pParent); } + }; + + template + struct QxNestedModel_Creator + { + static qx::IxModel *newModel(qx::IxModel *pParent) { return new qx::QxModel(pParent); } + }; + + template + struct QxNestedModel_Generic + { + + typedef typename qx::QxModel::type_collection type_collection; + typedef typename qx::QxModel::type_primary_key type_primary_key; + typedef typename qx::QxModel::type_ptr type_ptr; + + enum + { + is_valid = qx::trait::is_qx_registered::value + }; + + static inline qx::IxModel *create(qx::IxModel *pParent, const QModelIndex &idxParent, T &t) + { + static_assert(is_valid, "is_valid"); + qx::IxModel *pModel = qx::model_view::detail::QxNestedModel_Creator::value>::newModel(pParent); + pModel->setParentModel(pParent); + type_collection *pListOfItems = static_cast(pModel->getCollection()); + long &idx = pModel->m_lManualInsertIndex; + type_primary_key key; + + pModel->beginInsertRows(idxParent, 0, 0); + type_ptr ptr = qx::model_view::detail::QxNestedModel_Helper::value>::clone(t); + qx::IxDataMember *pDataMemberId = pModel->m_pDataMemberId; + if (!pDataMemberId) + { + qAssert(false); + pModel->endInsertRows(); + return pModel; + } + QVariant value = pDataMemberId->toVariant(&t); + if (!qx::trait::is_valid_primary_key(value)) + { + idx--; + value = QVariant(static_cast(idx)); + } + qx::cvt::from_variant(value, key); + pListOfItems->insert(0, key, ptr); + pModel->endInsertRows(); + return pModel; + } + + static inline void synchronize(qx::IxModel *pModel, T &t) + { + if (!pModel) + { + qAssert(false); + return; + } + type_collection *pListOfItems = static_cast(pModel->getCollection()); + if (!pListOfItems || (pListOfItems->count() <= 0)) + { + return; + } + type_ptr ptr = pListOfItems->getByIndex(0); + if (!ptr) + { + return; + } + qx::model_view::detail::QxNestedModel_Helper::value>::synchronize(t, (*ptr)); + } + }; + + template + struct QxNestedModel_Container + { + + typedef qx::trait::generic_container type_generic_container; + typedef typename type_generic_container::type_value_qx type_data; + typedef typename type_generic_container::type_item type_item; + typedef typename qx::QxModel::type_collection type_collection; + typedef typename qx::QxModel::type_primary_key type_primary_key; + typedef typename qx::QxModel::type_ptr type_ptr; + + enum + { + is_valid = qx::trait::is_qx_registered::value + }; + + static inline qx::IxModel *create(qx::IxModel *pParent, const QModelIndex &idxParent, T &t) + { + int iCurrRow = 0; + static_assert(is_valid, "is_valid"); + qx::IxModel *pModel = qx::model_view::detail::QxNestedModel_Creator::value>::newModel(pParent); + pModel->setParentModel(pParent); + long lCount = static_cast(type_generic_container::size(t)); + if (lCount <= 0) + { + return pModel; + } + + pModel->beginInsertRows(idxParent, 0, (lCount - 1)); + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + insertItem(pModel, (*it), iCurrRow); + iCurrRow++; + } + pModel->endInsertRows(); + return pModel; + } + + template + static inline bool insert(qx::IxModel *pModel, U &item, int iRow) + { + if (!pModel) + { + qAssert(false); + return false; + } + type_collection *pListOfItems = static_cast(pModel->getCollection()); + long &idx = pModel->m_lManualInsertIndex; + type_primary_key key; + + type_ptr ptr = qx::model_view::detail::QxNestedModel_Helper::value>::clone(item); + qx::IxDataMember *pDataMemberId = pModel->m_pDataMemberId; + if (!pDataMemberId) + { + qAssert(false); + return false; + } + QVariant value = pDataMemberId->toVariant(&item); + if (!qx::trait::is_valid_primary_key(value)) + { + idx--; + value = QVariant(static_cast(idx)); + } + qx::cvt::from_variant(value, key); + pListOfItems->insert(iRow, key, ptr); + return true; + } + + static inline void synchronize(qx::IxModel *pModel, T &t) + { + if (!pModel) + { + qAssert(false); + return; + } + type_generic_container::clear(t); + type_collection *pListOfItems = static_cast(pModel->getCollection()); + + for (long l = 0; l < pListOfItems->count(); l++) + { + type_ptr ptr = pListOfItems->getByIndex(l); + if (!ptr) + { + continue; + } + type_item item = type_generic_container::createItem(); + type_data &item_val = item.value_qx(); + qx::model_view::detail::QxNestedModel_Helper::value>::synchronize(item_val, (*ptr)); + QVariant vKey = QVariant(static_cast(l)); + qx::cvt::from_variant(vKey, item.key()); + type_generic_container::insertItem(t, item); + } + } + + private: + template + static inline bool insertItem(qx::IxModel *pModel, U &item, int iRow) + { + return insertItem_Helper < U, std::is_pointer::value || qx::trait::is_smart_ptr::value > ::insert(pModel, item, iRow); + } + + template + struct insertItem_Helper + { + static inline bool insert(qx::IxModel *pModel, U &item, int iRow) + { + return (item ? qx::model_view::detail::QxNestedModel_Container::insertItem(pModel, (*item), iRow) : true); + } + }; + + template + struct insertItem_Helper, false> + { + static inline bool insert(qx::IxModel *pModel, std::pair &item, int iRow) + { + return qx::model_view::detail::QxNestedModel_Container::insertItem(pModel, item.second, iRow); + } + }; + + template + struct insertItem_Helper, false> + { + static inline bool insert(qx::IxModel *pModel, const std::pair &item, int iRow) + { + return qx::model_view::detail::QxNestedModel_Container::insertItem(pModel, item.second, iRow); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct insertItem_Helper, false> + { + static inline bool insert(qx::IxModel *pModel, QPair &item, int iRow) + { + return qx::model_view::detail::QxNestedModel_Container::insertItem(pModel, item.second, iRow); + } + }; + + template + struct insertItem_Helper, false> + { + static inline bool insert(qx::IxModel *pModel, const QPair &item, int iRow) + { + return qx::model_view::detail::QxNestedModel_Container::insertItem(pModel, item.second, iRow); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct insertItem_Helper + { + enum + { + is_same_type = std::is_same::type_data, U>::value + }; + static bool insert(qx::IxModel *pModel, U &item, int iRow) + { + static_assert(is_same_type, "is_same_type"); + return qx::model_view::detail::QxNestedModel_Container::insert(pModel, item, iRow); + } + }; + }; + + template + struct QxNestedModel_Ptr + { + + static inline qx::IxModel *create(qx::IxModel *pParent, const QModelIndex &idxParent, T &t) + { + return (t ? create_Helper(pParent, idxParent, (*t)) : create_NullHelper(pParent, idxParent)); + } + + static inline void synchronize(qx::IxModel *pModel, T &t) + { + if (!t) + { + qx::trait::construct_ptr::get(t); + }; + if (t) + { + qx::model_view::sync_nested_model(pModel, (*t)); + } + } + + private: + template + static inline qx::IxModel *create_Helper(qx::IxModel *pParent, const QModelIndex &idxParent, U &u) + { + return qx::model_view::detail::QxNestedModel::create(pParent, idxParent, u); + } + + static inline qx::IxModel *create_NullHelper(qx::IxModel *pParent, const QModelIndex &idxParent) + { + M *pModelType = NULL; + Q_UNUSED(pModelType); + T t; + qx::trait::construct_ptr::get(t); + if (!t) + { + qAssert(false); + return NULL; + } + qx::IxModel *pModel = qx::model_view::create_nested_model_with_type(pParent, idxParent, (*t), pModelType); + if (pModel) + { + pModel->clear(); + } + qAssert(pModel != NULL); + return pModel; + } + }; + + template + struct QxNestedModel + { + + typedef typename std::conditional::value, qx::model_view::detail::QxNestedModel_Ptr, qx::model_view::detail::QxNestedModel_Generic>::type type_model_view_1; + typedef typename std::conditional::value, qx::model_view::detail::QxNestedModel_Ptr, type_model_view_1>::type type_model_view_2; + typedef typename std::conditional::value, qx::model_view::detail::QxNestedModel_Container, type_model_view_2>::type type_model_view_3; + + static inline qx::IxModel *create(qx::IxModel *pParent, const QModelIndex &idxParent, T &t) + { + return type_model_view_3::create(pParent, idxParent, t); + } + + static inline void synchronize(qx::IxModel *pModel, T &t) + { + type_model_view_3::synchronize(pModel, t); + } + }; + + } // namespace detail + } // namespace model_view +} // namespace qx + +namespace qx +{ + namespace model_view + { + + /*! + * \ingroup QxModelView + * \brief qx::model_view::create_nested_model is used by QxEntityEditor to manage complex data structure to work with relationships in QML views and Qt model/view architecture + * \param pParent Parent model, qx::model_view::create_nested_model creates a child model associated to this parent model (a NULL value means that the created model is a root model) + * \param idxParent Index parent model, qx::model_view::create_nested_model creates a child model indexed by this parent index model (an empty index means that the created model is a root model) + * \param t Item which contain all values exposed by the model to the views, t item can be a simple type, a pointer, a smart pointer, or a collection of items + */ + template + qx::IxModel *create_nested_model(qx::IxModel *pParent, const QModelIndex &idxParent, T &t) + { + return qx::model_view::detail::QxNestedModel::create(pParent, idxParent, t); + } + + template + qx::IxModel *create_nested_model_with_type(qx::IxModel *pParent, const QModelIndex &idxParent, T &t, M *pModelType) + { + Q_UNUSED(pModelType); + static_assert((std::is_base_of::value || std::is_same::value), "(std::is_base_of::value || std::is_same::value)"); + return qx::model_view::detail::QxNestedModel::create(pParent, idxParent, t); + } + + template + void sync_nested_model(qx::IxModel *pModel, T &t) + { + qx::model_view::detail::QxNestedModel::synchronize(pModel, t); + } + + } // namespace model_view +} // namespace qx + +#endif // _QX_NESTED_MODEL_H_ diff --git a/include/QxOrm.h b/include/QxOrm.h new file mode 100644 index 0000000..f863747 --- /dev/null +++ b/include/QxOrm.h @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_ORM_H_ +#define _QX_ORM_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \defgroup QxCommon QxCommon : QxOrm library common tools and macros + * \defgroup QxCollection QxCollection : QxOrm library container (keep insertion order + quick access by index + quick access by key) + * \defgroup QxSingleton QxSingleton : QxOrm library thread-safe singleton pattern + * \defgroup QxFactory QxFactory : QxOrm library factory pattern used by introspection engine + * \defgroup QxDataMember QxDataMember : QxOrm library data member (or property) used by introspection engine + * \defgroup QxFunction QxFunction : QxOrm library function used by introspection engine + * \defgroup QxRegister QxRegister : QxOrm library register context used by introspection engine (class, function, property...) + * \defgroup QxSerialize QxSerialize : QxOrm library serialization engine based on 'boost::serialization' library + * \defgroup QxTraits QxTraits : QxOrm library traits (template metaprogramming) not available in 'boost::type_traits' library + * \defgroup QxDao QxDao : QxOrm library database communication used by persistence engine (ORM - Object Relational Mapping) + * \defgroup QxValidator QxValidator : QxOrm library validation engine using validator pattern + * \defgroup QxModelView QxModelView : All classes registered into QxOrm context can be used with Qt model/view architecture (Qt widgets and/or QML views) + * \defgroup QxRestApi QxRestApi : QxOrm library REST API to send requests in JSON format from external application, from web-site or from QML view + * \defgroup QxService QxService : QxOrm library services engine to provide easy and powerful way to create C++ application server (to transfer data over network) + * \defgroup QxHttpServer QxHttpServer : QxOrm library HTTP server (based on QxService module) : support SSL/TLS, persistent connection, deliver static files, REST API, etc... + * \defgroup QxCache QxCache : QxOrm library basic thread-safe cache feature to backup and restore any kind of objects (for example, object fetched from database) + * \defgroup QxConvert QxConvert : QxOrm library conversion tools to-from QString type and to-from QVariant type + * \defgroup QxExtras QxExtras : QxOrm library extra-tools (for example : enable std::optional for C++17 compilers, enable boost::optional feature without enabling all boost features) + * \defgroup QxMemLeak QxMemLeak : QxOrm library memory leak detection (by Wu Yongwei) + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#ifdef _QX_ENABLE_MONGODB +#include +#endif // _QX_ENABLE_MONGODB + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#include +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#include +#include +#include +#include + +#ifndef _QX_NO_JSON +#include +#include +#endif // _QX_NO_JSON + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _QX_NO_JSON +#include +#endif // _QX_NO_JSON + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _QX_ENABLE_QT_NETWORK +#include +#include +#include +#include +#include +#include +#include +#endif // _QX_ENABLE_QT_NETWORK + +#endif // _QX_ORM_H_ diff --git a/include/QxOrm_Impl.h b/include/QxOrm_Impl.h new file mode 100644 index 0000000..06624e6 --- /dev/null +++ b/include/QxOrm_Impl.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_ORM_IMPL_H_ +#define _QX_ORM_IMPL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxOrm_Impl.h + * \author XDL Team + * \brief QxOrm_Impl.h file should be included in all *.cpp files which depend on QxOrm library if you are using QxMemLeak module or boost::serialization engine. Otherwise, include QxOrm_Impl.h file is optional (not required). + */ + +#include + +#endif // _QX_ORM_IMPL_H_ diff --git a/include/QxPrecompiled.h b/include/QxPrecompiled.h new file mode 100644 index 0000000..12dd6ed --- /dev/null +++ b/include/QxPrecompiled.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_ORM_PRECOMPILED_HEADER_H_ +#define _QX_ORM_PRECOMPILED_HEADER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#ifdef _MSC_VER +#if (_MSC_VER < 1800) // MSVC 2012 and below +#ifndef _VARIADIC_MAX +#define _VARIADIC_MAX 10 // to manage correctly std::tuple +#endif // _VARIADIC_MAX +#endif // (_MSC_VER < 1800) +#endif // _MSC_VER + +#ifdef _MSC_VER +#pragma warning(disable : 4503) /* -- Disable 4503 warning because of boost template -- */ +#pragma warning(disable : 4100) /* -- Disable warning 'unreferenced formal parameter' -- */ +#pragma warning(disable : 4996) +#pragma warning(disable : 4661) +#endif // _MSC_VER + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4396) +#endif // _MSC_VER + +#ifndef _QX_ENABLE_BOOST +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#define _QX_ENABLE_BOOST +#endif // _QX_ENABLE_BOOST_SERIALIZATION +#endif // _QX_ENABLE_BOOST + +#ifdef _QX_ENABLE_BOOST +#define BOOST_ALL_NO_LIB /* -- Disable auto-link feature provided by boost -- */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif // _QX_ENABLE_BOOST + +#ifdef BOOST_DLLEXPORT +#undef BOOST_DLLEXPORT +#define BOOST_DLLEXPORT /* Nothing */ +#endif // BOOST_DLLEXPORT + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifdef _MSC_VER +#include /* -- To avoid warning 4100 => header in precompiled header -- */ +#include +#include +#endif // _MSC_VER +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +#include +#include + +#endif // _QX_ORM_PRECOMPILED_HEADER_H_ diff --git a/include/QxRegister/IxClass.h b/include/QxRegister/IxClass.h new file mode 100644 index 0000000..c039c52 --- /dev/null +++ b/include/QxRegister/IxClass.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_CLASS_H_ +#define _IX_CLASS_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxClass.h + * \author XDL Team + * \ingroup QxRegister + * \brief Common interface for all classes registered into QxOrm context + */ + +#ifndef _QX_NO_RTTI +#include +#endif // _QX_NO_RTTI + +#include + +#include + +#include + +#include + +#include + +namespace qx +{ + + class IxSqlRelation; + + /*! + * \ingroup QxRegister + * \brief qx::IxClass : common interface for all classes registered into QxOrm context + */ + class QX_DLL_EXPORT IxClass : public qx::QxPropertyBag + { + + private: + struct IxClassImpl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + + protected: + IxClass(); + virtual ~IxClass() = 0; + + public: + QString getKey() const; + QString getName() const; + const char *getNamePtr() const; + QString getDescription() const; + long getVersion() const; + qx::dao::strategy::inheritance getDaoStrategy() const; + qx::QxSoftDelete getSoftDelete() const; + bool isFinalClass() const; + bool isDaoReadOnly() const; + bool isRegistered() const; + IxDataMemberX *getDataMemberX() const; + IxFunctionX *getFctMemberX() const; + IxFunctionX *getFctStaticX() const; + std::shared_ptr> getSqlRelationX(); + std::shared_ptr> getSqlDataMemberX(); + + void setKey(const QString &s); + void setName(const QString &s); + void setDescription(const QString &s); + void setDaoStrategy(qx::dao::strategy::inheritance e); + void setSoftDelete(const qx::QxSoftDelete &o); + void setDaoReadOnly(bool b); + void setVersion(long l); + + virtual bool isAbstract() const = 0; + virtual bool implementIxPersistable() const = 0; + virtual IxClass *getBaseClass() const = 0; + virtual IxValidatorX *getAllValidator(); + + IxDataMember *getId(bool bRecursive = false) const; + bool isKindOf(const QString &sClassName) const; + QString dumpClass() const; + +#ifndef _QX_NO_RTTI + virtual const std::type_info &typeInfo() const = 0; + bool isKindOf(const std::type_info &typeInfo) const; +#endif // _QX_NO_RTTI + + protected: + void updateClassX(); + void setRegistered(bool b); + void setFinalClass(bool b); + void setDataMemberX(IxDataMemberX *p); + void setFctMemberX(IxFunctionX *p); + void setFctStaticX(IxFunctionX *p); + IxValidatorX_ptr &getAllValidatorRef(); + }; + + typedef std::shared_ptr IxClass_ptr; + +} // namespace qx + +#endif // _IX_CLASS_H_ diff --git a/include/QxRegister/IxTypeInfo.h b/include/QxRegister/IxTypeInfo.h new file mode 100644 index 0000000..65ba9db --- /dev/null +++ b/include/QxRegister/IxTypeInfo.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_TYPE_INFO_H_ +#define _IX_TYPE_INFO_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +namespace qx +{ + + class IxTypeInfo + { + + public: + IxTypeInfo() { ; } + virtual ~IxTypeInfo() { ; } + + virtual QString className() const = 0; + }; + +} // namespace qx + +#endif // _IX_TYPE_INFO_H_ diff --git a/include/QxRegister/QxClass.h b/include/QxRegister/QxClass.h new file mode 100644 index 0000000..d8d5c4d --- /dev/null +++ b/include/QxRegister/QxClass.h @@ -0,0 +1,433 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_CLASS_H_ +#define _QX_CLASS_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxClass.h + * \author XDL Team + * \ingroup QxRegister + * \brief Concrete class registered into QxOrm context + */ + +#include + +#include + +#include + +#include + +#include +#include +#include + +#include + +namespace qx +{ + + namespace trait + { + template + struct is_ix_persistable; + } // namespace trait + + /*! + * \ingroup QxRegister + * \brief qx::register_class(T & t) : specialize this template to register a class of type T into QxOrm context + */ + template + void register_class(T &t) + { + Q_UNUSED(t); + qAssert(false); + }; + + /*! + * \ingroup QxRegister + * \brief qx::QxClass : concrete class of type T registered into QxOrm context (this class is a singleton and is thread-safe) + */ + template + class QxClass : public IxClass, public QxSingleton> + { + + friend class QxSingleton>; + + public: + typedef typename qx::trait::get_primary_key::type type_primary_key; + typedef typename qx::trait::get_base_class::type type_base_class; + typedef IxFunction::type_any_params type_any_params; + + enum + { + is_valid_base_class = ((!std::is_same::value) && (std::is_base_of::value || std::is_same::value)) + }; + + protected: + QMutex m_oMutexClass; //!< Mutex -> 'QxClass' is thread-safe + + protected: + QxClass() : IxClass(), QxSingleton>(QString("qx::QxClass_") + qx::trait::get_class_name::get_xml_tag()) { init(); } + virtual ~QxClass() { ; } + + public: + QxDataMemberX *dataMemberX() const { return static_cast *>(this->getDataMemberX()); } + IxFunctionX *fctMemberX() const { return this->getFctMemberX(); } + IxFunctionX *fctStaticX() const { return this->getFctStaticX(); } + + IxDataMember *id(type_primary_key T::*pDataMemberId, const QString &sKey, long lVersion = 0); + IxDataMember *id(const QString &sKey, long lVersion); + IxDataMember *data(const QString &sKey, long lVersion); + + template + IxDataMember *data(V U::*pData, const QString &sKey, long lVersion = 0, bool bSerialize = true, bool bDao = true); + template + IxSqlRelation *relationOneToOne(V U::*pData, const QString &sKey, long lVersion = 0); + template + IxSqlRelation *relationManyToOne(V U::*pData, const QString &sKey, long lVersion = 0); + template + IxSqlRelation *relationOneToMany(V U::*pData, const QString &sKey, const QString &sForeignKey, long lVersion = 0); + template + IxSqlRelation *relationManyToMany(V U::*pData, const QString &sKey, const QString &sExtraTable, const QString &sForeignKeyOwner, const QString &sForeignKeyDataType, long lVersion = 0); + + template + IxDataMember *pimpl(V U::*pData, const QString &sKey = QString("_PIMPL_")); + template + IxDataMember *id(type_primary_key U::*pDataMemberId, const QString &sKey, long lVersion, IxDataMember *pImpl); + template + IxDataMember *data(V U::*pData, const QString &sKey, long lVersion, bool bSerialize, bool bDao, IxDataMember *pImpl); + template + IxSqlRelation *relationOneToOne(V U::*pData, const QString &sKey, long lVersion, IxDataMember *pImpl); + template + IxSqlRelation *relationManyToOne(V U::*pData, const QString &sKey, long lVersion, IxDataMember *pImpl); + template + IxSqlRelation *relationOneToMany(V U::*pData, const QString &sKey, const QString &sForeignKey, long lVersion, IxDataMember *pImpl); + template + IxSqlRelation *relationManyToMany(V U::*pData, const QString &sKey, const QString &sExtraTable, const QString &sForeignKeyOwner, const QString &sForeignKeyDataType, long lVersion, IxDataMember *pImpl); + + template + IxFunction *fct_0(const typename QxFunction_0::type_fct &fct, const QString &sKey); + template + IxFunction *fct_1(const typename QxFunction_1::type_fct &fct, const QString &sKey); + template + IxFunction *fct_2(const typename QxFunction_2::type_fct &fct, const QString &sKey); + template + IxFunction *fct_3(const typename QxFunction_3::type_fct &fct, const QString &sKey); + template + IxFunction *fct_4(const typename QxFunction_4::type_fct &fct, const QString &sKey); + template + IxFunction *fct_5(const typename QxFunction_5::type_fct &fct, const QString &sKey); + template + IxFunction *fct_6(const typename QxFunction_6::type_fct &fct, const QString &sKey); + template + IxFunction *fct_7(const typename QxFunction_7::type_fct &fct, const QString &sKey); + template + IxFunction *fct_8(const typename QxFunction_8::type_fct &fct, const QString &sKey); + template + IxFunction *fct_9(const typename QxFunction_9::type_fct &fct, const QString &sKey); + + template + IxFunction *fctStatic_0(const typename QxFunction_0::type_fct &fct, const QString &sKey); + template + IxFunction *fctStatic_1(const typename QxFunction_1::type_fct &fct, const QString &sKey); + template + IxFunction *fctStatic_2(const typename QxFunction_2::type_fct &fct, const QString &sKey); + template + IxFunction *fctStatic_3(const typename QxFunction_3::type_fct &fct, const QString &sKey); + template + IxFunction *fctStatic_4(const typename QxFunction_4::type_fct &fct, const QString &sKey); + template + IxFunction *fctStatic_5(const typename QxFunction_5::type_fct &fct, const QString &sKey); + template + IxFunction *fctStatic_6(const typename QxFunction_6::type_fct &fct, const QString &sKey); + template + IxFunction *fctStatic_7(const typename QxFunction_7::type_fct &fct, const QString &sKey); + template + IxFunction *fctStatic_8(const typename QxFunction_8::type_fct &fct, const QString &sKey); + template + IxFunction *fctStatic_9(const typename QxFunction_9::type_fct &fct, const QString &sKey); + + static qx_bool invoke(const QString &sKey, T *pOwner, const QString ¶ms = QString(), qx::any *ret = NULL) + { + IxFunctionX *pFctX = QxClass::getSingleton()->fctMemberX(); + IxFunction_ptr pFct = ((pFctX && pFctX->exist(sKey)) ? pFctX->getByKey(sKey) : IxFunction_ptr()); + return invokeHelper::invoke(sKey, pOwner, params, ret, pFct); + } + static qx_bool invoke(const QString &sKey, T *pOwner, const type_any_params ¶ms, qx::any *ret = NULL) + { + IxFunctionX *pFctX = QxClass::getSingleton()->fctMemberX(); + IxFunction_ptr pFct = ((pFctX && pFctX->exist(sKey)) ? pFctX->getByKey(sKey) : IxFunction_ptr()); + return invokeHelper::invoke(sKey, pOwner, params, ret, pFct); + } + static qx_bool invokeStatic(const QString &sKey, const QString ¶ms = QString(), qx::any *ret = NULL) + { + IxFunctionX *pFctX = QxClass::getSingleton()->fctStaticX(); + IxFunction_ptr pFct = ((pFctX && pFctX->exist(sKey)) ? pFctX->getByKey(sKey) : IxFunction_ptr()); + return invokeHelper::invoke(sKey, params, ret, pFct); + } + static qx_bool invokeStatic(const QString &sKey, const type_any_params ¶ms, qx::any *ret = NULL) + { + IxFunctionX *pFctX = QxClass::getSingleton()->fctStaticX(); + IxFunction_ptr pFct = ((pFctX && pFctX->exist(sKey)) ? pFctX->getByKey(sKey) : IxFunction_ptr()); + return invokeHelper::invoke(sKey, params, ret, pFct); + } + + virtual bool isAbstract() const + { + return std::is_abstract::value; + } + + virtual bool implementIxPersistable() const + { + return implementIxPersistable_Helper::get(); + } + +#ifndef _QX_NO_RTTI + virtual const std::type_info &typeInfo() const + { + return typeid(T); + } +#endif // _QX_NO_RTTI + + virtual IxClass *getBaseClass() const + { + return (std::is_same::value ? NULL : QxClass::getSingleton()); + } + +#if _QX_SUPPORT_COVARIANT_RETURN_TYPE + virtual QxValidatorX *getAllValidator() + { + if (!this->getAllValidatorRef()) + { + this->getAllValidatorRef().reset(new QxValidatorX()); + IxClass::getAllValidator(); + } + return static_cast *>(this->getAllValidatorRef().get()); + } +#else // _QX_SUPPORT_COVARIANT_RETURN_TYPE + virtual IxValidatorX *getAllValidator() + { + if (!this->getAllValidatorRef()) + { + this->getAllValidatorRef().reset(new QxValidatorX()); + IxClass::getAllValidator(); + } + return this->getAllValidatorRef().get(); + } +#endif // _QX_SUPPORT_COVARIANT_RETURN_TYPE + + private: + void init(); + IxFunction *insertFct(IxFunction_ptr pFct, const QString &sKey); + IxFunction *insertFctStatic(IxFunction_ptr pFct, const QString &sKey); + + void registerClass() { qx::register_class>(*this); } + + void beforeRegisterClass() + { + static_assert(is_valid_base_class, "is_valid_base_class"); + QMutexLocker locker(&m_oMutexClass); + QxClass::getSingleton(); + bool bNeedReg = (!this->isRegistered()); + this->setRegistered(true); + if (bNeedReg) + { + registerClass(); + } + } + + template + struct invokeHelper + { + static qx_bool invoke(const QString &sKey, U *pOwner, const QString ¶ms, qx::any *ret, IxFunction_ptr pFct) + { + return ((pOwner && pFct) ? pFct->invoke(pOwner, params, ret) : QxClass::invoke(sKey, static_cast(pOwner), params, ret)); + } + + static qx_bool invoke(const QString &sKey, U *pOwner, const type_any_params ¶ms, qx::any *ret, IxFunction_ptr pFct) + { + return ((pOwner && pFct) ? pFct->invoke(pOwner, params, ret) : QxClass::invoke(sKey, static_cast(pOwner), params, ret)); + } + + static qx_bool invoke(const QString &sKey, const QString ¶ms, qx::any *ret, IxFunction_ptr pFct) + { + return (pFct ? pFct->invoke(params, ret) : QxClass::invokeStatic(sKey, params, ret)); + } + + static qx_bool invoke(const QString &sKey, const type_any_params ¶ms, qx::any *ret, IxFunction_ptr pFct) + { + return (pFct ? pFct->invoke(params, ret) : QxClass::invokeStatic(sKey, params, ret)); + } + }; + + template + struct invokeHelper + { + static qx_bool invoke(const QString &sKey, U *pOwner, const QString ¶ms, qx::any *ret, IxFunction_ptr pFct) + { + Q_UNUSED(sKey); + return ((pOwner && pFct) ? pFct->invoke(pOwner, params, ret) : qx_bool(false)); + } + + static qx_bool invoke(const QString &sKey, U *pOwner, const type_any_params ¶ms, qx::any *ret, IxFunction_ptr pFct) + { + Q_UNUSED(sKey); + return ((pOwner && pFct) ? pFct->invoke(pOwner, params, ret) : qx_bool(false)); + } + + static qx_bool invoke(const QString &sKey, const QString ¶ms, qx::any *ret, IxFunction_ptr pFct) + { + Q_UNUSED(sKey); + return (pFct ? pFct->invoke(params, ret) : qx_bool(false)); + } + + static qx_bool invoke(const QString &sKey, const type_any_params ¶ms, qx::any *ret, IxFunction_ptr pFct) + { + Q_UNUSED(sKey); + return (pFct ? pFct->invoke(params, ret) : qx_bool(false)); + } + }; + + template + struct invokeHelper + { + static qx_bool invoke(const QString &sKey, qx::trait::no_base_class_defined *pOwner, const QString ¶ms, qx::any *ret, IxFunction_ptr pFct) + { + Q_UNUSED(sKey); + Q_UNUSED(pOwner); + Q_UNUSED(params); + Q_UNUSED(ret); + Q_UNUSED(pFct); + return qx_bool(false); + } + + static qx_bool invoke(const QString &sKey, qx::trait::no_base_class_defined *pOwner, const type_any_params ¶ms, qx::any *ret, IxFunction_ptr pFct) + { + Q_UNUSED(sKey); + Q_UNUSED(pOwner); + Q_UNUSED(params); + Q_UNUSED(ret); + Q_UNUSED(pFct); + return qx_bool(false); + } + + static qx_bool invoke(const QString &sKey, const QString ¶ms, qx::any *ret, IxFunction_ptr pFct) + { + Q_UNUSED(sKey); + Q_UNUSED(params); + Q_UNUSED(ret); + Q_UNUSED(pFct); + return qx_bool(false); + } + + static qx_bool invoke(const QString &sKey, const type_any_params ¶ms, qx::any *ret, IxFunction_ptr pFct) + { + Q_UNUSED(sKey); + Q_UNUSED(params); + Q_UNUSED(ret); + Q_UNUSED(pFct); + return qx_bool(false); + } + }; + + template + struct invokeHelper + { + static qx_bool invoke(const QString &sKey, QObject *pOwner, const QString ¶ms, qx::any *ret, IxFunction_ptr pFct) + { + Q_UNUSED(sKey); + Q_UNUSED(pOwner); + Q_UNUSED(params); + Q_UNUSED(ret); + Q_UNUSED(pFct); + return qx_bool(false); + } + + static qx_bool invoke(const QString &sKey, QObject *pOwner, const type_any_params ¶ms, qx::any *ret, IxFunction_ptr pFct) + { + Q_UNUSED(sKey); + Q_UNUSED(pOwner); + Q_UNUSED(params); + Q_UNUSED(ret); + Q_UNUSED(pFct); + return qx_bool(false); + } + + static qx_bool invoke(const QString &sKey, const QString ¶ms, qx::any *ret, IxFunction_ptr pFct) + { + Q_UNUSED(sKey); + Q_UNUSED(params); + Q_UNUSED(ret); + Q_UNUSED(pFct); + return qx_bool(false); + } + + static qx_bool invoke(const QString &sKey, const type_any_params ¶ms, qx::any *ret, IxFunction_ptr pFct) + { + Q_UNUSED(sKey); + Q_UNUSED(params); + Q_UNUSED(ret); + Q_UNUSED(pFct); + return qx_bool(false); + } + }; + + private: + template + struct implementIxPersistable_Helper + { + static bool get() { return qx::trait::is_ix_persistable::value; } + }; + + template + struct implementIxPersistable_Helper + { + static bool get() { return false; } + }; + + template + struct implementIxPersistable_Helper + { + static bool get() { return false; } + }; + }; + +} // namespace qx + +#include "../../inl/QxRegister/QxClass.inl" + +#endif // _QX_CLASS_H_ diff --git a/include/QxRegister/QxClassName.h b/include/QxRegister/QxClassName.h new file mode 100644 index 0000000..b9ee2d2 --- /dev/null +++ b/include/QxRegister/QxClassName.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_CLASS_NAME_H_ +#define _QX_CLASS_NAME_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxClassName.h + * \author XDL Team + * \ingroup QxRegister + * \brief Provide a class helper to retrieve the class name under const char * format + */ + +#include +#include + +#include + +namespace qx +{ + + /*! + * \ingroup QxRegister + * \brief qx::QxClassName : class helper to retrieve the class name of type T under const char * format + */ + template + class QxClassName + { + + public: + static inline const char *get() { return qxGetClassName::value, 0>::get(); } + static inline const char *get_xml_tag() { return qxGetClassName::value, 0>::get_xml_tag(); } + + private: + template + struct qxGetClassName + { + static inline const char *get() { return QX_GET_CLASS_NAME_WITH_TYPENAME(T); } + static inline const char *get_xml_tag() { return QX_GET_CLASS_NAME_XML_TAG_WITH_TYPENAME(T); } + }; + + template + struct qxGetClassName + { + static inline const char *get() { return qx::QxClass::getSingleton()->getNamePtr(); } + static inline const char *get_xml_tag() { return qx::QxClass::getSingleton()->getNamePtr(); } + }; + }; + +} // namespace qx + +#endif // _QX_CLASS_NAME_H_ diff --git a/include/QxRegister/QxClassX.h b/include/QxRegister/QxClassX.h new file mode 100644 index 0000000..964c192 --- /dev/null +++ b/include/QxRegister/QxClassX.h @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_CLASS_X_H_ +#define _QX_CLASS_X_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxClassX.h + * \author XDL Team + * \ingroup QxRegister + * \brief List of all classes registered into QxOrm context + */ + +#include + +#include + +#include + +#include + +#include +#include + +namespace qx +{ + + /*! + * \ingroup QxRegister + * \brief qx::QxClassX : list of all classes registered into QxOrm context (this container is a singleton) + */ + class QX_DLL_EXPORT QxClassX : public QxSingleton + { + + friend class QxSingleton; + friend class IxClass; + + public: + typedef IxFunction::type_any_params type_any_params; + typedef std::function type_fct_save_qvariant_usertype; + typedef std::function type_fct_load_qvariant_usertype; + + protected: + QxCollection m_lstClass; //!< Collection of classes registered into QxOrm context + QHash m_lstSqlTypeByClassName; //!< List of SQL types by class name + QHash m_lstValidatorMessage; //!< List of validator message when an invalid value is detected + type_fct_save_qvariant_usertype m_fctSaveQVariantUserType; //!< Serialization of QVariant UserType => function to save a QVariant UserType (cf. QxSerialize_QVariant.cpp file) + type_fct_load_qvariant_usertype m_fctLoadQVariantUserType; //!< Serialization of QVariant UserType => function to load a QVariant UserType (cf. QxSerialize_QVariant.cpp file) + + private: + QxClassX(); + virtual ~QxClassX(); + + QxCollection *getAll(); + IxClass *get(const QString &sKey) const; + bool exist(const QString &sKey) const; + bool insert(const QString &sKey, IxClass *pClass); + bool remove(const QString &sKey); + void clear(); + void initSqlTypeByClassName(); + void initValidatorMessage(); + +#ifndef _QX_NO_RTTI + const std::type_info &typeInfo(const QString &sKey) const; +#endif // _QX_NO_RTTI + + public: + static qx::any create(const QString &sKey); + static IxClass *getClass(const QString &sKey); + static IxDataMemberX *getDataMemberX(const QString &sKey); + static IxFunctionX *getFctMemberX(const QString &sKey); + static IxFunctionX *getFctStaticX(const QString &sKey); + static IxDataMember *getDataMember(const QString &sClassKey, const QString &sDataKey, bool bRecursive = true); + static IxFunction *getFctMember(const QString &sClassKey, const QString &sFctKey, bool bRecursive = true); + static IxFunction *getFctStatic(const QString &sClassKey, const QString &sFctKey, bool bRecursive = true); + static bool implementIxPersistable(const QString &sKey, bool bTraceIfFalse = true); + + static QxCollection *getAllClasses(); + static void registerAllClasses(bool bInitAllRelations = true, bool bInitValidator = true); + static QString dumpAllClasses(); + static QString dumpSqlSchema(); + + static QHash *getAllValidatorMessage() { return (&QxClassX::getSingleton()->m_lstValidatorMessage); } + static QHash *getAllSqlTypeByClassName() { return (&QxClassX::getSingleton()->m_lstSqlTypeByClassName); } + static QString getSqlTypeByClassName(const QString &sClassName) { return QxClassX::getAllSqlTypeByClassName()->value(sClassName); } + +#ifndef _QX_NO_RTTI + static const std::type_info &getTypeInfo(const QString &sClassName) { return QxClassX::getSingleton()->typeInfo(sClassName); } +#endif // _QX_NO_RTTI + + static type_fct_save_qvariant_usertype getFctSaveQVariantUserType() { return QxClassX::getSingleton()->m_fctSaveQVariantUserType; } + static type_fct_load_qvariant_usertype getFctLoadQVariantUserType() { return QxClassX::getSingleton()->m_fctLoadQVariantUserType; } + static void setFctSaveQVariantUserType(type_fct_save_qvariant_usertype fct) { QxClassX::getSingleton()->m_fctSaveQVariantUserType = fct; } + static void setFctLoadQVariantUserType(type_fct_load_qvariant_usertype fct) { QxClassX::getSingleton()->m_fctLoadQVariantUserType = fct; } + + template + static inline qx_bool invoke(const QString &sClassKey, const QString &sFctKey, U &pOwner, const QString ¶ms = QString(), qx::any *ret = NULL) + { + typedef typename std::conditional::value, QxClassX::invoke_ptr, QxClassX::invoke_default>::type type_invoke_1; + typedef typename std::conditional::value, QxClassX::invoke_ptr, type_invoke_1>::type type_invoke_2; + return type_invoke_2::invoke(sClassKey, sFctKey, pOwner, params, ret); + } + + template + static inline qx_bool invoke(const QString &sClassKey, const QString &sFctKey, U &pOwner, const type_any_params ¶ms, qx::any *ret = NULL) + { + typedef typename std::conditional::value, QxClassX::invoke_ptr, QxClassX::invoke_default>::type type_invoke_1; + typedef typename std::conditional::value, QxClassX::invoke_ptr, type_invoke_1>::type type_invoke_2; + return type_invoke_2::invoke(sClassKey, sFctKey, pOwner, params, ret); + } + + static qx_bool invokeStatic(const QString &sClassKey, const QString &sFctKey, const QString ¶ms = QString(), qx::any *ret = NULL); + static qx_bool invokeStatic(const QString &sClassKey, const QString &sFctKey, const type_any_params ¶ms, qx::any *ret = NULL); + + private: + template + struct invoke_ptr + { + static inline qx_bool invoke(const QString &sClassKey, const QString &sFctKey, U &pOwner, const QString ¶ms = QString(), qx::any *ret = NULL) + { + return QxClassX::invokeVoidPtr(sClassKey, sFctKey, static_cast(&(*pOwner)), params, ret); + } + static inline qx_bool invoke(const QString &sClassKey, const QString &sFctKey, U &pOwner, const type_any_params ¶ms, qx::any *ret = NULL) + { + return QxClassX::invokeVoidPtr(sClassKey, sFctKey, static_cast(&(*pOwner)), params, ret); + } + }; + + template + struct invoke_default + { + static inline qx_bool invoke(const QString &sClassKey, const QString &sFctKey, U &pOwner, const QString ¶ms = QString(), qx::any *ret = NULL) + { + return QxClassX::invokeVoidPtr(sClassKey, sFctKey, static_cast(&pOwner), params, ret); + } + static inline qx_bool invoke(const QString &sClassKey, const QString &sFctKey, U &pOwner, const type_any_params ¶ms, qx::any *ret = NULL) + { + return QxClassX::invokeVoidPtr(sClassKey, sFctKey, static_cast(&pOwner), params, ret); + } + }; + + static qx_bool invokeVoidPtr(const QString &sClassKey, const QString &sFctKey, void *pOwner, const QString ¶ms = QString(), qx::any *ret = NULL); + static qx_bool invokeVoidPtr(const QString &sClassKey, const QString &sFctKey, void *pOwner, const type_any_params ¶ms, qx::any *ret = NULL); + + static bool isValid_DataMember(IxDataMember *p); + static bool isValid_SqlRelation(IxDataMember *p); + }; + +} // namespace qx + +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::QxClassX) + +#endif // _QX_CLASS_X_H_ diff --git a/include/QxRegister/QxRegister.h b/include/QxRegister/QxRegister.h new file mode 100644 index 0000000..0a03aaa --- /dev/null +++ b/include/QxRegister/QxRegister.h @@ -0,0 +1,517 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_REGISTER_H_ +#define _QX_REGISTER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxRegister.h + * \author XDL Team + * \ingroup QxRegister + * \brief Provide macros to register a class into QxOrm context + */ + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) +#pragma warning(disable : 4094) +#endif // _MSC_VER + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#define QX_REGISTER_CLASS_MAPPING_FCT_HPP(dllImportExport, className) \ + namespace qx \ + { \ + template <> \ + dllImportExport void register_class(QxClass &t) QX_USED; \ + } + +#define QX_REGISTER_SERIALIZE_QDATASTREAM_HPP(dllImportExport, className) \ + dllImportExport QDataStream &operator<<(QDataStream &stream, const className &t) QX_USED; \ + dllImportExport QDataStream &operator>>(QDataStream &stream, className &t) QX_USED; + +#define QX_REGISTER_SERIALIZE_QDATASTREAM_CPP(className) \ + QDataStream &operator<<(QDataStream &stream, const className &t) \ + { \ + return qx::QxSerializeRegistered::save(stream, t); \ + } \ + QDataStream &operator>>(QDataStream &stream, className &t) \ + { \ + return qx::QxSerializeRegistered::load(stream, t); \ + } + +#define QX_REGISTER_CLASS_MAPPING_FCT_EMPTY_CPP(className) \ + namespace qx \ + { \ + template <> \ + void register_class(QxClass &t) { Q_UNUSED(t); } \ + } + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#define QX_SERIALIZE_IMPLEMENT_ARCHIVE_CPP(Archive, className) \ + namespace boost \ + { \ + namespace serialization \ + { \ + inline void serialize(Archive &ar, className &t, const unsigned int file_version) \ + { \ + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered< className >::value"); \ + typedef std::conditional, qx::serialization::detail::loader>::type type_invoker; \ + type_invoker::invoke(ar, t, file_version); \ + } \ + } \ + } // namespace boost::serialization +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#define QX_SERIALIZE_ARCHIVE_HPP(dllImportExport, Archive, className) \ + namespace boost \ + { \ + namespace serialization \ + { \ + dllImportExport void serialize(Archive &ar, className &t, const unsigned int file_version) QX_USED; \ + } \ + } // namespace boost::serialization +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#define QX_SERIALIZE_IMPLEMENT_ARCHIVE_EMPTY_CPP(Archive, className) \ + namespace boost \ + { \ + namespace serialization \ + { \ + inline void serialize(Archive &ar, className &t, const unsigned int file_version) \ + { \ + Q_UNUSED(ar); \ + Q_UNUSED(t); \ + Q_UNUSED(file_version); \ + } \ + } \ + } // namespace boost::serialization +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#define QX_SERIALIZE_ARCHIVE_TEMPLATE_HPP(className) \ + namespace boost \ + { \ + namespace serialization \ + { \ + template \ + void serialize(Archive &ar, className &t, const unsigned int file_version); \ + } \ + } // namespace boost::serialization +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#if _QX_SERIALIZE_POLYMORPHIC +#define QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_POLYMORPHIC_CPP(className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_CPP(boost::archive::polymorphic_oarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_CPP(boost::archive::polymorphic_iarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_EMPTY_CPP(boost::archive::polymorphic_binary_oarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_EMPTY_CPP(boost::archive::polymorphic_binary_iarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_EMPTY_CPP(boost::archive::polymorphic_text_oarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_EMPTY_CPP(boost::archive::polymorphic_text_iarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_EMPTY_CPP(boost::archive::polymorphic_xml_oarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_EMPTY_CPP(boost::archive::polymorphic_xml_iarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_EMPTY_CPP(boost::archive::binary_oarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_EMPTY_CPP(boost::archive::binary_iarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_EMPTY_CPP(boost::archive::text_oarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_EMPTY_CPP(boost::archive::text_iarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_EMPTY_CPP(boost::archive::xml_oarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_EMPTY_CPP(boost::archive::xml_iarchive, className) +// --- +#define QX_SERIALIZE_LIST_ARCHIVE_POLYMORPHIC_HPP(dllImportExport, className) \ + QX_SERIALIZE_ARCHIVE_HPP(dllImportExport, boost::archive::polymorphic_oarchive, className) \ + QX_SERIALIZE_ARCHIVE_HPP(dllImportExport, boost::archive::polymorphic_iarchive, className) \ + QX_SERIALIZE_ARCHIVE_TEMPLATE_HPP(className) +#else // _QX_SERIALIZE_POLYMORPHIC +#define QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_POLYMORPHIC_CPP(className) /* Nothing */ +#define QX_SERIALIZE_LIST_ARCHIVE_POLYMORPHIC_HPP(dllImportExport, className) /* Nothing */ +#endif // _QX_SERIALIZE_POLYMORPHIC +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#if _QX_SERIALIZE_BINARY +#define QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_BINARY_CPP(className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_CPP(boost::archive::binary_oarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_CPP(boost::archive::binary_iarchive, className) +// --- +#define QX_SERIALIZE_LIST_ARCHIVE_BINARY_HPP(dllImportExport, className) \ + QX_SERIALIZE_ARCHIVE_HPP(dllImportExport, boost::archive::binary_oarchive, className) \ + QX_SERIALIZE_ARCHIVE_HPP(dllImportExport, boost::archive::binary_iarchive, className) +#else // _QX_SERIALIZE_BINARY +#define QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_BINARY_CPP(className) /* Nothing */ +#define QX_SERIALIZE_LIST_ARCHIVE_BINARY_HPP(dllImportExport, className) /* Nothing */ +#endif // _QX_SERIALIZE_BINARY +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#if _QX_SERIALIZE_TEXT +#define QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_TEXT_CPP(className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_CPP(boost::archive::text_oarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_CPP(boost::archive::text_iarchive, className) +// --- +#define QX_SERIALIZE_LIST_ARCHIVE_TEXT_HPP(dllImportExport, className) \ + QX_SERIALIZE_ARCHIVE_HPP(dllImportExport, boost::archive::text_oarchive, className) \ + QX_SERIALIZE_ARCHIVE_HPP(dllImportExport, boost::archive::text_iarchive, className) +#else // _QX_SERIALIZE_TEXT +#define QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_TEXT_CPP(className) /* Nothing */ +#define QX_SERIALIZE_LIST_ARCHIVE_TEXT_HPP(dllImportExport, className) /* Nothing */ +#endif // _QX_SERIALIZE_TEXT +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#if _QX_SERIALIZE_XML +#define QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_XML_CPP(className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_CPP(boost::archive::xml_oarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_CPP(boost::archive::xml_iarchive, className) +// --- +#define QX_SERIALIZE_LIST_ARCHIVE_XML_HPP(dllImportExport, className) \ + QX_SERIALIZE_ARCHIVE_HPP(dllImportExport, boost::archive::xml_oarchive, className) \ + QX_SERIALIZE_ARCHIVE_HPP(dllImportExport, boost::archive::xml_iarchive, className) +#else // _QX_SERIALIZE_XML +#define QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_XML_CPP(className) /* Nothing */ +#define QX_SERIALIZE_LIST_ARCHIVE_XML_HPP(dllImportExport, className) /* Nothing */ +#endif // _QX_SERIALIZE_XML +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#if _QX_SERIALIZE_PORTABLE_BINARY +#define QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_PORTABLE_BINARY_CPP(className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_CPP(eos::portable_oarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_CPP(eos::portable_iarchive, className) +// --- +#define QX_SERIALIZE_LIST_ARCHIVE_PORTABLE_BINARY_HPP(dllImportExport, className) \ + QX_SERIALIZE_ARCHIVE_HPP(dllImportExport, eos::portable_oarchive, className) \ + QX_SERIALIZE_ARCHIVE_HPP(dllImportExport, eos::portable_iarchive, className) +#else // _QX_SERIALIZE_PORTABLE_BINARY +#define QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_PORTABLE_BINARY_CPP(className) /* Nothing */ +#define QX_SERIALIZE_LIST_ARCHIVE_PORTABLE_BINARY_HPP(dllImportExport, className) /* Nothing */ +#endif // _QX_SERIALIZE_PORTABLE_BINARY +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#if _QX_SERIALIZE_WIDE_BINARY +#define QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_WIDE_BINARY_CPP(className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_CPP(boost::archive::binary_woarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_CPP(boost::archive::binary_wiarchive, className) +// --- +#define QX_SERIALIZE_LIST_ARCHIVE_WIDE_BINARY_HPP(dllImportExport, className) \ + QX_SERIALIZE_ARCHIVE_HPP(dllImportExport, boost::archive::binary_woarchive, className) \ + QX_SERIALIZE_ARCHIVE_HPP(dllImportExport, boost::archive::binary_wiarchive, className) +#else // _QX_SERIALIZE_WIDE_BINARY +#define QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_WIDE_BINARY_CPP(className) /* Nothing */ +#define QX_SERIALIZE_LIST_ARCHIVE_WIDE_BINARY_HPP(dllImportExport, className) /* Nothing */ +#endif // _QX_SERIALIZE_WIDE_BINARY +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#if _QX_SERIALIZE_WIDE_TEXT +#define QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_WIDE_TEXT_CPP(className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_CPP(boost::archive::text_woarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_CPP(boost::archive::text_wiarchive, className) +// --- +#define QX_SERIALIZE_LIST_ARCHIVE_WIDE_TEXT_HPP(dllImportExport, className) \ + QX_SERIALIZE_ARCHIVE_HPP(dllImportExport, boost::archive::text_woarchive, className) \ + QX_SERIALIZE_ARCHIVE_HPP(dllImportExport, boost::archive::text_wiarchive, className) +#else // _QX_SERIALIZE_WIDE_TEXT +#define QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_WIDE_TEXT_CPP(className) /* Nothing */ +#define QX_SERIALIZE_LIST_ARCHIVE_WIDE_TEXT_HPP(dllImportExport, className) /* Nothing */ +#endif // _QX_SERIALIZE_WIDE_TEXT +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#if _QX_SERIALIZE_WIDE_XML +#define QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_WIDE_XML_CPP(className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_CPP(boost::archive::xml_woarchive, className) \ + QX_SERIALIZE_IMPLEMENT_ARCHIVE_CPP(boost::archive::xml_wiarchive, className) +// --- +#define QX_SERIALIZE_LIST_ARCHIVE_WIDE_XML_HPP(dllImportExport, className) \ + QX_SERIALIZE_ARCHIVE_HPP(dllImportExport, boost::archive::xml_woarchive, className) \ + QX_SERIALIZE_ARCHIVE_HPP(dllImportExport, boost::archive::xml_wiarchive, className) +#else // _QX_SERIALIZE_WIDE_XML +#define QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_WIDE_XML_CPP(className) /* Nothing */ +#define QX_SERIALIZE_LIST_ARCHIVE_WIDE_XML_HPP(dllImportExport, className) /* Nothing */ +#endif // _QX_SERIALIZE_WIDE_XML +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#define QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_CPP(className) \ + QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_POLYMORPHIC_CPP(className) \ + QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_BINARY_CPP(className) \ + QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_TEXT_CPP(className) \ + QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_XML_CPP(className) \ + QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_PORTABLE_BINARY_CPP(className) \ + QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_WIDE_BINARY_CPP(className) \ + QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_WIDE_TEXT_CPP(className) \ + QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_WIDE_XML_CPP(className) +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#define QX_SERIALIZE_LIST_ARCHIVE_HPP(dllImportExport, className) \ + QX_SERIALIZE_LIST_ARCHIVE_POLYMORPHIC_HPP(dllImportExport, className) \ + QX_SERIALIZE_LIST_ARCHIVE_BINARY_HPP(dllImportExport, className) \ + QX_SERIALIZE_LIST_ARCHIVE_TEXT_HPP(dllImportExport, className) \ + QX_SERIALIZE_LIST_ARCHIVE_XML_HPP(dllImportExport, className) \ + QX_SERIALIZE_LIST_ARCHIVE_PORTABLE_BINARY_HPP(dllImportExport, className) \ + QX_SERIALIZE_LIST_ARCHIVE_WIDE_BINARY_HPP(dllImportExport, className) \ + QX_SERIALIZE_LIST_ARCHIVE_WIDE_TEXT_HPP(dllImportExport, className) \ + QX_SERIALIZE_LIST_ARCHIVE_WIDE_XML_HPP(dllImportExport, className) +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#if _QX_WRITE_BOOST_CLASS_EXPORT_IN_HPP_FILE +#define QX_BOOST_CLASS_EXPORT_HPP(className) BOOST_CLASS_EXPORT_GUID(className, #className) +#else +#define QX_BOOST_CLASS_EXPORT_HPP(className) /* Nothing */ +#endif // _QX_WRITE_BOOST_CLASS_EXPORT_IN_HPP_FILE +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#if _QX_WRITE_BOOST_CLASS_EXPORT_IN_CPP_FILE +#define QX_BOOST_CLASS_EXPORT_CPP(className) BOOST_CLASS_EXPORT_GUID(className, #className) +#else +#define QX_BOOST_CLASS_EXPORT_CPP(className) /* Nothing */ +#endif // _QX_WRITE_BOOST_CLASS_EXPORT_IN_CPP_FILE +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#define QX_REGISTER_FRIEND_CLASS(className) \ + template \ + friend void qx::register_class(T &t); + +#define QX_DLL_EXPORT_TEMPLATE_QX_CLASS_HPP(className) \ + QX_DLL_EXPORT_TEMPLATE_HPP(class, qx::QxDataMemberX) \ + QX_DLL_EXPORT_TEMPLATE_HPP(class, qx::QxSingleton>) \ + QX_DLL_EXPORT_TEMPLATE_HPP(class, qx::QxClass) \ + QX_DLL_EXPORT_TEMPLATE_HPP(class, qx::QxSingleton>) + +#define QX_DLL_EXPORT_TEMPLATE_QX_CLASS_CPP(className) \ + QX_DLL_EXPORT_TEMPLATE_CPP(class, qx::QxDataMemberX) \ + QX_DLL_EXPORT_TEMPLATE_CPP(class, qx::QxSingleton>) \ + QX_DLL_EXPORT_TEMPLATE_CPP(class, qx::QxClass) \ + QX_DLL_EXPORT_TEMPLATE_CPP(class, qx::QxSingleton>) + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#define QX_REGISTER_COMPLEX_CLASS_NAME_HPP(className, baseClass, version, classNameFormatted) \ + QX_SET_REGISTERED(className) \ + QX_REGISTER_CLASS_NAME(className) \ + QX_REGISTER_BASE_CLASS(className, baseClass) \ + QX_REGISTER_FACTORY_COMPLEX_CLASS_NAME_HPP(className, classNameFormatted) \ + QX_REGISTER_BOOST_SERIALIZE_HELPER_COMPLEX_CLASS_NAME(className, classNameFormatted) \ + QX_BOOST_CLASS_EXPORT_HPP(className) \ + QX_CLASS_VERSION(className, version) +#else // _QX_ENABLE_BOOST_SERIALIZATION +#define QX_REGISTER_COMPLEX_CLASS_NAME_HPP(className, baseClass, version, classNameFormatted) \ + QX_SET_REGISTERED(className) \ + QX_REGISTER_CLASS_NAME(className) \ + QX_REGISTER_BASE_CLASS(className, baseClass) \ + QX_REGISTER_FACTORY_COMPLEX_CLASS_NAME_HPP(className, classNameFormatted) \ + QX_CLASS_VERSION(className, version) +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#define QX_REGISTER_HPP(className, baseClass, version) \ + QX_REGISTER_COMPLEX_CLASS_NAME_HPP(className, baseClass, version, className) + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#define QX_REGISTER_COMPLEX_CLASS_NAME_CPP(className, classNameFormatted) \ + QX_BOOST_CLASS_EXPORT_CPP(className) \ + QX_REGISTER_FACTORY_COMPLEX_CLASS_NAME_CPP(className, classNameFormatted) \ + QX_REGISTER_BOOST_SERIALIZE_HELPER_COMPLEX_CLASS_NAME_CPP(className, classNameFormatted) \ + QX_SERIALIZE_IMPLEMENT_LIST_ARCHIVE_CPP(className) +#else // _QX_ENABLE_BOOST_SERIALIZATION +#define QX_REGISTER_COMPLEX_CLASS_NAME_CPP(className, classNameFormatted) \ + QX_REGISTER_FACTORY_COMPLEX_CLASS_NAME_CPP(className, classNameFormatted) +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#define QX_REGISTER_CPP(className) \ + QX_REGISTER_COMPLEX_CLASS_NAME_CPP(className, className) + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _MSC_VER +#define QX_REGISTER_COMPLEX_CLASS_NAME_HPP_EXPORT_DLL(className, baseClass, version, classNameFormatted) \ + QX_REGISTER_COMPLEX_CLASS_NAME_HPP(className, baseClass, version, classNameFormatted) \ + QX_SERIALIZE_LIST_ARCHIVE_HPP(QX_DLL_EXPORT_HELPER, className) \ + QX_REGISTER_CLASS_MAPPING_FCT_HPP(QX_DLL_EXPORT_HELPER, className) \ + QX_REGISTER_SERIALIZE_QDATASTREAM_HPP(QX_DLL_EXPORT_HELPER, className) +// --- +#define QX_REGISTER_HPP_EXPORT_DLL(className, baseClass, version) \ + QX_REGISTER_COMPLEX_CLASS_NAME_HPP_EXPORT_DLL(className, baseClass, version, className) +#else // _MSC_VER +#define QX_REGISTER_COMPLEX_CLASS_NAME_HPP_EXPORT_DLL(className, baseClass, version, classNameFormatted) \ + QX_REGISTER_COMPLEX_CLASS_NAME_HPP(className, baseClass, version, classNameFormatted) \ + QX_DLL_EXPORT_TEMPLATE_QX_CLASS_HPP(className) \ + QX_BOOST_EXPORT_SERIALIZATION_HPP(className) \ + QX_SERIALIZE_LIST_ARCHIVE_HPP(QX_DLL_EXPORT_HELPER, className) \ + QX_REGISTER_CLASS_MAPPING_FCT_HPP(QX_DLL_EXPORT_HELPER, className) \ + QX_REGISTER_SERIALIZE_QDATASTREAM_HPP(QX_DLL_EXPORT_HELPER, className) +// --- +#define QX_REGISTER_HPP_EXPORT_DLL(className, baseClass, version) \ + QX_REGISTER_COMPLEX_CLASS_NAME_HPP_EXPORT_DLL(className, baseClass, version, className) +#endif // _MSC_VER + +#else // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _MSC_VER +#define QX_REGISTER_COMPLEX_CLASS_NAME_HPP_EXPORT_DLL(className, baseClass, version, classNameFormatted) \ + QX_REGISTER_COMPLEX_CLASS_NAME_HPP(className, baseClass, version, classNameFormatted) \ + QX_REGISTER_CLASS_MAPPING_FCT_HPP(QX_DLL_EXPORT_HELPER, className) \ + QX_REGISTER_SERIALIZE_QDATASTREAM_HPP(QX_DLL_EXPORT_HELPER, className) +// --- +#define QX_REGISTER_HPP_EXPORT_DLL(className, baseClass, version) \ + QX_REGISTER_COMPLEX_CLASS_NAME_HPP_EXPORT_DLL(className, baseClass, version, className) +#else // _MSC_VER +#define QX_REGISTER_COMPLEX_CLASS_NAME_HPP_EXPORT_DLL(className, baseClass, version, classNameFormatted) \ + QX_REGISTER_COMPLEX_CLASS_NAME_HPP(className, baseClass, version, classNameFormatted) \ + QX_DLL_EXPORT_TEMPLATE_QX_CLASS_HPP(className) \ + QX_REGISTER_CLASS_MAPPING_FCT_HPP(QX_DLL_EXPORT_HELPER, className) \ + QX_REGISTER_SERIALIZE_QDATASTREAM_HPP(QX_DLL_EXPORT_HELPER, className) +// --- +#define QX_REGISTER_HPP_EXPORT_DLL(className, baseClass, version) \ + QX_REGISTER_COMPLEX_CLASS_NAME_HPP_EXPORT_DLL(className, baseClass, version, className) +#endif // _MSC_VER + +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#define QX_REGISTER_COMPLEX_CLASS_NAME_HPP_IMPORT_DLL(className, baseClass, version, classNameFormatted) \ + QX_REGISTER_COMPLEX_CLASS_NAME_HPP(className, baseClass, version, classNameFormatted) \ + QX_DLL_EXPORT_TEMPLATE_QX_CLASS_HPP(className) \ + QX_BOOST_EXPORT_SERIALIZATION_HPP(className) \ + QX_SERIALIZE_LIST_ARCHIVE_HPP(QX_DLL_IMPORT_HELPER, className) \ + QX_REGISTER_CLASS_MAPPING_FCT_HPP(QX_DLL_IMPORT_HELPER, className) \ + QX_REGISTER_SERIALIZE_QDATASTREAM_HPP(QX_DLL_IMPORT_HELPER, className) +#else // _QX_ENABLE_BOOST_SERIALIZATION +#define QX_REGISTER_COMPLEX_CLASS_NAME_HPP_IMPORT_DLL(className, baseClass, version, classNameFormatted) \ + QX_REGISTER_COMPLEX_CLASS_NAME_HPP(className, baseClass, version, classNameFormatted) \ + QX_DLL_EXPORT_TEMPLATE_QX_CLASS_HPP(className) \ + QX_REGISTER_CLASS_MAPPING_FCT_HPP(QX_DLL_IMPORT_HELPER, className) \ + QX_REGISTER_SERIALIZE_QDATASTREAM_HPP(QX_DLL_IMPORT_HELPER, className) +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#define QX_REGISTER_HPP_IMPORT_DLL(className, baseClass, version) \ + QX_REGISTER_COMPLEX_CLASS_NAME_HPP_IMPORT_DLL(className, baseClass, version, className) + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#define QX_REGISTER_COMPLEX_CLASS_NAME_CPP_EXPORT_DLL(className, classNameFormatted) \ + QX_REGISTER_COMPLEX_CLASS_NAME_CPP(className, classNameFormatted) \ + QX_DLL_EXPORT_TEMPLATE_QX_CLASS_CPP(className) \ + QX_BOOST_EXPORT_SERIALIZATION_CPP(className) \ + QX_REGISTER_SERIALIZE_QDATASTREAM_CPP(className) +#else // _QX_ENABLE_BOOST_SERIALIZATION +#define QX_REGISTER_COMPLEX_CLASS_NAME_CPP_EXPORT_DLL(className, classNameFormatted) \ + QX_REGISTER_COMPLEX_CLASS_NAME_CPP(className, classNameFormatted) \ + QX_DLL_EXPORT_TEMPLATE_QX_CLASS_CPP(className) \ + QX_REGISTER_SERIALIZE_QDATASTREAM_CPP(className) +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#define QX_REGISTER_CPP_EXPORT_DLL(className) \ + QX_REGISTER_COMPLEX_CLASS_NAME_CPP_EXPORT_DLL(className, className) + +#ifdef _MSC_VER +#define QX_REGISTER_COMPLEX_CLASS_NAME_CPP_IMPORT_DLL(className, classNameFormatted) \ + QX_REGISTER_COMPLEX_CLASS_NAME_CPP(className, classNameFormatted) +// --- +#define QX_REGISTER_CPP_IMPORT_DLL(className) \ + QX_REGISTER_COMPLEX_CLASS_NAME_CPP_IMPORT_DLL(className, className) +#else // _MSC_VER +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#define QX_REGISTER_COMPLEX_CLASS_NAME_CPP_IMPORT_DLL(className, classNameFormatted) \ + QX_REGISTER_COMPLEX_CLASS_NAME_CPP(className, classNameFormatted) \ + QX_DLL_EXPORT_TEMPLATE_QX_CLASS_CPP(className) \ + QX_BOOST_EXPORT_SERIALIZATION_CPP(className) +#else // _QX_ENABLE_BOOST_SERIALIZATION +#define QX_REGISTER_COMPLEX_CLASS_NAME_CPP_IMPORT_DLL(className, classNameFormatted) \ + QX_REGISTER_COMPLEX_CLASS_NAME_CPP(className, classNameFormatted) \ + QX_DLL_EXPORT_TEMPLATE_QX_CLASS_CPP(className) +#endif // _QX_ENABLE_BOOST_SERIALIZATION +// --- +#define QX_REGISTER_CPP_IMPORT_DLL(className) \ + QX_REGISTER_COMPLEX_CLASS_NAME_CPP_IMPORT_DLL(className, className) +#endif // _MSC_VER + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#define QX_REGISTER_ABSTRACT_CLASS(className) \ + BOOST_SERIALIZATION_ASSUME_ABSTRACT(className) \ + namespace boost \ + { \ + namespace serialization \ + { \ + template \ + inline void load_construct_data(Archive &ar, className *t, const unsigned int file_version) \ + { \ + Q_UNUSED(ar); \ + Q_UNUSED(t); \ + Q_UNUSED(file_version); \ + } \ + } \ + } // namespace boost::serialization +#else // _QX_ENABLE_BOOST_SERIALIZATION +#define QX_REGISTER_ABSTRACT_CLASS(className) /* Nothing */ +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +/* -- Create your macro (in your precompiled header) replacing "MY_DLL" +#ifdef _BUILDING_MY_DLL +#define QX_REGISTER_HPP_MY_DLL QX_REGISTER_HPP_EXPORT_DLL +#define QX_REGISTER_CPP_MY_DLL QX_REGISTER_CPP_EXPORT_DLL +#define QX_REGISTER_COMPLEX_CLASS_NAME_HPP_MY_DLL QX_REGISTER_COMPLEX_CLASS_NAME_HPP_EXPORT_DLL +#define QX_REGISTER_COMPLEX_CLASS_NAME_CPP_MY_DLL QX_REGISTER_COMPLEX_CLASS_NAME_CPP_EXPORT_DLL +#else +#define QX_REGISTER_HPP_MY_DLL QX_REGISTER_HPP_IMPORT_DLL +#define QX_REGISTER_CPP_MY_DLL QX_REGISTER_CPP_IMPORT_DLL +#define QX_REGISTER_COMPLEX_CLASS_NAME_HPP_MY_DLL QX_REGISTER_COMPLEX_CLASS_NAME_HPP_IMPORT_DLL +#define QX_REGISTER_COMPLEX_CLASS_NAME_CPP_MY_DLL QX_REGISTER_COMPLEX_CLASS_NAME_CPP_IMPORT_DLL +#endif // _BUILDING_MY_DLL +*/ + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +#endif // _QX_REGISTER_H_ diff --git a/include/QxRegister/QxRegisterInternalHelper.h b/include/QxRegister/QxRegisterInternalHelper.h new file mode 100644 index 0000000..db027f0 --- /dev/null +++ b/include/QxRegister/QxRegisterInternalHelper.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_REGISTER_INTERNAL_HELPER_H_ +#define _QX_REGISTER_INTERNAL_HELPER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#include +#include +#include +#include +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#define QX_REGISTER_INTERNAL_HELPER_HPP(dllImportExport, className, version) \ + QX_CLASS_VERSION(className, version) \ + QX_REGISTER_CLASS_NAME(className) \ + QX_REGISTER_CLASS_MAPPING_FCT_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(dllImportExport, className) \ + QX_BOOST_EXPORT_SERIALIZATION_FAST_COMPIL_HPP(className) \ + QX_REGISTER_BOOST_SERIALIZE_HELPER(className) + +#define QX_REGISTER_INTERNAL_HELPER_START_FILE_CPP(className) \ + QX_REGISTER_CLASS_MAPPING_FCT_EMPTY_CPP(className) \ + QX_BOOST_EXPORT_SERIALIZATION_FAST_COMPIL_CPP(className) + +#define QX_REGISTER_INTERNAL_HELPER_END_FILE_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(className) \ + QX_REGISTER_BOOST_SERIALIZE_HELPER_CPP(className) + +#else // _QX_ENABLE_BOOST_SERIALIZATION + +#define QX_REGISTER_INTERNAL_HELPER_HPP(dllImportExport, className, version) \ + QX_CLASS_VERSION(className, version) \ + QX_REGISTER_CLASS_NAME(className) \ + QX_REGISTER_CLASS_MAPPING_FCT_HPP(dllImportExport, className) + +#define QX_REGISTER_INTERNAL_HELPER_START_FILE_CPP(className) \ + QX_REGISTER_CLASS_MAPPING_FCT_EMPTY_CPP(className) + +#define QX_REGISTER_INTERNAL_HELPER_END_FILE_CPP(className) /* Nothing */ + +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#endif // _QX_REGISTER_INTERNAL_HELPER_H_ diff --git a/include/QxRegister/QxRegisterQtProperty.h b/include/QxRegister/QxRegisterQtProperty.h new file mode 100644 index 0000000..1b0da8a --- /dev/null +++ b/include/QxRegister/QxRegisterQtProperty.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_REGISTER_QT_META_PROPERTY_H_ +#define _QX_REGISTER_QT_META_PROPERTY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxRegisterQtProperty.h + * \author XDL Team + * \ingroup QxRegister + * \brief Register automatically Qt meta-property defined with Q_PROPERTY() macro into QxOrm context + */ + +#include +#include + +#include + +#include + +#define QX_REGISTER_ALL_QT_PROPERTIES(className, propertyId) \ + namespace qx \ + { \ + template <> \ + void register_class(QxClass &t) \ + { \ + qx::register_all_qt_properties(t, propertyId); \ + } \ + } + +namespace qx +{ + + template + void register_all_qt_properties(QxClass &t, const QString &sPropertyId) + { + static_assert(qx::trait::qt_meta_object::is_valid, "qx::trait::qt_meta_object::is_valid"); + + const QMetaObject *pMetaObject = qx::trait::qt_meta_object::get(); + int iIndexId = (sPropertyId.isEmpty() ? -2 : (pMetaObject ? pMetaObject->indexOfProperty(qPrintable(sPropertyId)) : -1)); + if (!pMetaObject || (iIndexId == -1)) + { + qAssert(false); + return; + } + + for (int i = pMetaObject->propertyOffset(); i < pMetaObject->propertyCount(); i++) + { + QMetaProperty prop = pMetaObject->property(i); + if (i == iIndexId) + { + t.id(sPropertyId, 0); + } + else + { + t.data(prop.name(), 0); + } + } + } + +} // namespace qx + +#endif // _QX_REGISTER_QT_META_PROPERTY_H_ diff --git a/include/QxRegister/QxVersion.h b/include/QxRegister/QxVersion.h new file mode 100644 index 0000000..ea7c563 --- /dev/null +++ b/include/QxRegister/QxVersion.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_VERSION_H_ +#define _QX_VERSION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxVersion.h + * \author XDL Team + * \ingroup QxRegister + * \brief Register a version number per class + */ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#define QX_CLASS_VERSION(C, V) BOOST_CLASS_VERSION(C, V) + +#else // _QX_ENABLE_BOOST_SERIALIZATION + +namespace qx +{ + + template + struct version + { + enum + { + value = 0 + }; + }; + +} // namespace qx + +#define QX_CLASS_VERSION(C, V) \ + namespace qx \ + { \ + template <> \ + struct version \ + { \ + enum \ + { \ + value = V \ + }; \ + }; \ + } + +#endif // _QX_ENABLE_BOOST_SERIALIZATION +#endif // _QX_VERSION_H_ diff --git a/include/QxRestApi/QxRestApi.h b/include/QxRestApi/QxRestApi.h new file mode 100644 index 0000000..8bbb75c --- /dev/null +++ b/include/QxRestApi/QxRestApi.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_REST_API_H_ +#define _QX_REST_API_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxRestApi.h + * \author XDL Team + * \ingroup QxRestApi + * \brief Provide a REST API to send requests in JSON format from external application, from web-site or from QML view (https://www.qxorm.com/qxorm_en/manual.html#manual_97) + */ + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +#include + +#ifndef _QX_NO_JSON +#include +#include +#include +#include +#endif // _QX_NO_JSON + +namespace qx +{ + + /*! + * \ingroup QxRestApi + * \brief qx::QxRestApi : provide a REST API to send requests in JSON format from external application, from web-site or from QML view (https://www.qxorm.com/qxorm_en/manual.html#manual_97) + */ + class QX_DLL_EXPORT QxRestApi : public QObject + { + + Q_OBJECT + + private: + struct QxRestApiImpl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + + public: + QxRestApi(QObject *parent = NULL); + virtual ~QxRestApi(); + + Q_INVOKABLE QString processRequest(const QString &request); + Q_INVOKABLE void clearAll(); + + Q_INVOKABLE QString getErrorDesc() const; + Q_INVOKABLE int getErrorCode() const; + QSqlError getError() const; + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) +#if (QT_VERSION >= 0x050300) // Double check here to support Qt4 (which doesn't manage macro expansion in the moc process) + Q_INVOKABLE QString getNativeErrorCode() const; +#endif // (QT_VERSION >= 0x050300) +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + + Q_INVOKABLE void setEntity(const QString &entity); + Q_INVOKABLE void setAction(const QString &action); + Q_INVOKABLE void setFunction(const QString &fct); + Q_INVOKABLE void setColumns(const QStringList &columns); + Q_INVOKABLE void setRelations(const QStringList &relations); + Q_INVOKABLE void setOutputFormat(const QStringList &outputFormat); + Q_INVOKABLE void setDatabase(const QString &database); + Q_INVOKABLE void setQuery(const QString &query); + Q_INVOKABLE void setData(const QString &data); + Q_INVOKABLE void setUseExecBatch(bool useExecBatch); + +#ifndef _QX_NO_JSON + QJsonValue processRequest(const QJsonValue &request); + void setData(const QJsonValue &data); +#endif // _QX_NO_JSON + }; + +} // namespace qx + +#endif // _QX_REST_API_H_ diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_QFlags.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_QFlags.h new file mode 100644 index 0000000..b8e5220 --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_QFlags.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_QDATASTREAM_QFLAGS_H_ +#define _QX_SERIALIZE_QDATASTREAM_QFLAGS_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_QFlags.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type QFlags + */ + +#include +#include + +#if (QT_VERSION < QT_VERSION_CHECK(5, 9, 0)) + +template +QDataStream &operator<<(QDataStream &stream, const QFlags &t) +{ + qint64 iFlags = static_cast(t); + stream << iFlags; + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, QFlags &t) +{ + qint64 iFlags = 0; + stream >> iFlags; + t = QFlags(QFlag(static_cast(iFlags))); + return stream; +} + +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 9, 0)) +#endif // _QX_SERIALIZE_QDATASTREAM_QFLAGS_H_ diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_QObject.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_QObject.h new file mode 100644 index 0000000..bec3752 --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_QObject.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_QDATASTREAM_QOBJECT_H_ +#define _QX_SERIALIZE_QDATASTREAM_QOBJECT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_QObject.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type QObject (serialize dynamic properties) + */ + +#include +#include + +QX_DLL_EXPORT QDataStream &operator<<(QDataStream &stream, const QObject &t) QX_USED; +QX_DLL_EXPORT QDataStream &operator>>(QDataStream &stream, QObject &t) QX_USED; + +#endif // _QX_SERIALIZE_QDATASTREAM_QOBJECT_H_ diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_QScopedPointer.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_QScopedPointer.h new file mode 100644 index 0000000..baab071 --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_QScopedPointer.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) +#ifndef _QX_SERIALIZE_QDATASTREAM_QSCOPEDPOINTER_H_ +#define _QX_SERIALIZE_QDATASTREAM_QSCOPEDPOINTER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_QScopedPointer.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type QScopedPointer + */ + +#include +#include + +template +QDataStream &operator<<(QDataStream &stream, const QScopedPointer &t) +{ + qint8 iIsNull = (t ? 0 : 1); + stream << iIsNull; + if (t) + { + stream << (*t); + } + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, QScopedPointer &t) +{ + qint8 iIsNull = 0; + stream >> iIsNull; + if (!iIsNull) + { + t.reset(new T()); + stream >> (*t); + } + else + { + t.reset(); + } + return stream; +} + +#endif // _QX_SERIALIZE_QDATASTREAM_QSCOPEDPOINTER_H_ +#endif // (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_QSharedPointer.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_QSharedPointer.h new file mode 100644 index 0000000..619e665 --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_QSharedPointer.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_QDATASTREAM_QSHAREDPOINTER_H_ +#define _QX_SERIALIZE_QDATASTREAM_QSHAREDPOINTER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_QSharedPointer.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type QSharedPointer + */ + +#include +#include + +template +QDataStream &operator<<(QDataStream &stream, const QSharedPointer &t) +{ + qint8 iIsNull = (t ? 0 : 1); + stream << iIsNull; + if (t) + { + stream << (*t); + } + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, QSharedPointer &t) +{ + qint8 iIsNull = 0; + stream >> iIsNull; + if (!iIsNull) + { + t = QSharedPointer(new T()); + stream >> (*t); + } + else + { + t = QSharedPointer(); + } + return stream; +} + +#endif // _QX_SERIALIZE_QDATASTREAM_QSHAREDPOINTER_H_ diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_QSqlError.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_QSqlError.h new file mode 100644 index 0000000..80cd2b5 --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_QSqlError.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_QDATASTREAM_QSQLERROR_H_ +#define _QX_SERIALIZE_QDATASTREAM_QSQLERROR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_QSqlError.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type QSqlError + */ + +#include + +#include + +QX_DLL_EXPORT QDataStream &operator<<(QDataStream &stream, const QSqlError &t) QX_USED; +QX_DLL_EXPORT QDataStream &operator>>(QDataStream &stream, QSqlError &t) QX_USED; + +#endif // _QX_SERIALIZE_QDATASTREAM_QSQLERROR_H_ diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_QWeakPointer.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_QWeakPointer.h new file mode 100644 index 0000000..364d534 --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_QWeakPointer.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_QDATASTREAM_QWEAKPOINTER_H_ +#define _QX_SERIALIZE_QDATASTREAM_QWEAKPOINTER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_QWeakPointer.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type QWeakPointer + */ + +#include +#include + +#include + +template +QDataStream &operator<<(QDataStream &stream, const QWeakPointer &t) +{ + QSharedPointer ptr = t.toStrongRef(); + stream << ptr; + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, QWeakPointer &t) +{ + QSharedPointer ptr; + stream >> ptr; + t = QWeakPointer(ptr); + return stream; +} + +#endif // _QX_SERIALIZE_QDATASTREAM_QWEAKPOINTER_H_ diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_all_include.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_all_include.h new file mode 100644 index 0000000..ac192c0 --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_all_include.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_QDATASTREAM_ALL_INCLUDE_H_ +#define _QX_SERIALIZE_QDATASTREAM_ALL_INCLUDE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_all_include.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Include all Qt QDataStream serialization method (save/load) provided by QxOrm library + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // _QX_SERIALIZE_QDATASTREAM_ALL_INCLUDE_H_ diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_optional.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_optional.h new file mode 100644 index 0000000..1b16f55 --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_optional.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST +#ifndef _QX_SERIALIZE_QDATASTREAM_BOOST_OPTIONAL_H_ +#define _QX_SERIALIZE_QDATASTREAM_BOOST_OPTIONAL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_boost_optional.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type boost::optional + */ + +#include + +template +QDataStream &operator<<(QDataStream &stream, const boost::optional &t) +{ + qint8 iHasData = (t ? 1 : 0); + stream << iHasData; + if (t) + { + stream << (*t); + } + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, boost::optional &t) +{ + qint8 iHasData = 0; + stream >> iHasData; + if (iHasData) + { + t = T(); + stream >> (*t); + } + else + { + t = boost::none; + } + return stream; +} + +#endif // _QX_SERIALIZE_QDATASTREAM_BOOST_OPTIONAL_H_ +#endif // _QX_ENABLE_BOOST diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_scoped_ptr.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_scoped_ptr.h new file mode 100644 index 0000000..c95c8bb --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_scoped_ptr.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST +#ifndef _QX_SERIALIZE_QDATASTREAM_BOOST_SCOPED_PTR_H_ +#define _QX_SERIALIZE_QDATASTREAM_BOOST_SCOPED_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_boost_scoped_ptr.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type boost::scoped_ptr + */ + +#include + +template +QDataStream &operator<<(QDataStream &stream, const boost::scoped_ptr &t) +{ + qint8 iIsNull = (t ? 0 : 1); + stream << iIsNull; + if (t) + { + stream << (*t); + } + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, boost::scoped_ptr &t) +{ + qint8 iIsNull = 0; + stream >> iIsNull; + if (!iIsNull) + { + t.reset(new T()); + stream >> (*t); + } + else + { + t.reset(); + } + return stream; +} + +#endif // _QX_SERIALIZE_QDATASTREAM_BOOST_SCOPED_PTR_H_ +#endif // _QX_ENABLE_BOOST diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_shared_ptr.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_shared_ptr.h new file mode 100644 index 0000000..c0246dc --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_shared_ptr.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST +#ifndef _QX_SERIALIZE_QDATASTREAM_BOOST_SHARED_PTR_H_ +#define _QX_SERIALIZE_QDATASTREAM_BOOST_SHARED_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_boost_shared_ptr.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type boost::shared_ptr + */ + +#include + +template +QDataStream &operator<<(QDataStream &stream, const boost::shared_ptr &t) +{ + qint8 iIsNull = (t ? 0 : 1); + stream << iIsNull; + if (t) + { + stream << (*t); + } + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, boost::shared_ptr &t) +{ + qint8 iIsNull = 0; + stream >> iIsNull; + if (!iIsNull) + { + t.reset(new T()); + stream >> (*t); + } + else + { + t.reset(); + } + return stream; +} + +#endif // _QX_SERIALIZE_QDATASTREAM_BOOST_SHARED_PTR_H_ +#endif // _QX_ENABLE_BOOST diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_tuple.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_tuple.h new file mode 100644 index 0000000..af2470c --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_tuple.h @@ -0,0 +1,266 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST +#ifndef _QX_SERIALIZE_QDATASTREAM_BOOST_TUPLE_H_ +#define _QX_SERIALIZE_QDATASTREAM_BOOST_TUPLE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_boost_tuple.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type boost::tuple + */ + +#include + +template +QDataStream &operator<<(QDataStream &stream, const boost::tuple &t) +{ + stream << boost::get<0>(t); + stream << boost::get<1>(t); + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const boost::tuple &t) +{ + stream << boost::get<0>(t); + stream << boost::get<1>(t); + stream << boost::get<2>(t); + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const boost::tuple &t) +{ + stream << boost::get<0>(t); + stream << boost::get<1>(t); + stream << boost::get<2>(t); + stream << boost::get<3>(t); + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const boost::tuple &t) +{ + stream << boost::get<0>(t); + stream << boost::get<1>(t); + stream << boost::get<2>(t); + stream << boost::get<3>(t); + stream << boost::get<4>(t); + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const boost::tuple &t) +{ + stream << boost::get<0>(t); + stream << boost::get<1>(t); + stream << boost::get<2>(t); + stream << boost::get<3>(t); + stream << boost::get<4>(t); + stream << boost::get<5>(t); + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const boost::tuple &t) +{ + stream << boost::get<0>(t); + stream << boost::get<1>(t); + stream << boost::get<2>(t); + stream << boost::get<3>(t); + stream << boost::get<4>(t); + stream << boost::get<5>(t); + stream << boost::get<6>(t); + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const boost::tuple &t) +{ + stream << boost::get<0>(t); + stream << boost::get<1>(t); + stream << boost::get<2>(t); + stream << boost::get<3>(t); + stream << boost::get<4>(t); + stream << boost::get<5>(t); + stream << boost::get<6>(t); + stream << boost::get<7>(t); + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const boost::tuple &t) +{ + stream << boost::get<0>(t); + stream << boost::get<1>(t); + stream << boost::get<2>(t); + stream << boost::get<3>(t); + stream << boost::get<4>(t); + stream << boost::get<5>(t); + stream << boost::get<6>(t); + stream << boost::get<7>(t); + stream << boost::get<8>(t); + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const boost::tuple &t) +{ + stream << boost::get<0>(t); + stream << boost::get<1>(t); + stream << boost::get<2>(t); + stream << boost::get<3>(t); + stream << boost::get<4>(t); + stream << boost::get<5>(t); + stream << boost::get<6>(t); + stream << boost::get<7>(t); + stream << boost::get<8>(t); + stream << boost::get<9>(t); + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, boost::tuple &t) +{ + stream >> boost::get<0>(t); + stream >> boost::get<1>(t); + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, boost::tuple &t) +{ + stream >> boost::get<0>(t); + stream >> boost::get<1>(t); + stream >> boost::get<2>(t); + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, boost::tuple &t) +{ + stream >> boost::get<0>(t); + stream >> boost::get<1>(t); + stream >> boost::get<2>(t); + stream >> boost::get<3>(t); + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, boost::tuple &t) +{ + stream >> boost::get<0>(t); + stream >> boost::get<1>(t); + stream >> boost::get<2>(t); + stream >> boost::get<3>(t); + stream >> boost::get<4>(t); + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, boost::tuple &t) +{ + stream >> boost::get<0>(t); + stream >> boost::get<1>(t); + stream >> boost::get<2>(t); + stream >> boost::get<3>(t); + stream >> boost::get<4>(t); + stream >> boost::get<5>(t); + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, boost::tuple &t) +{ + stream >> boost::get<0>(t); + stream >> boost::get<1>(t); + stream >> boost::get<2>(t); + stream >> boost::get<3>(t); + stream >> boost::get<4>(t); + stream >> boost::get<5>(t); + stream >> boost::get<6>(t); + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, boost::tuple &t) +{ + stream >> boost::get<0>(t); + stream >> boost::get<1>(t); + stream >> boost::get<2>(t); + stream >> boost::get<3>(t); + stream >> boost::get<4>(t); + stream >> boost::get<5>(t); + stream >> boost::get<6>(t); + stream >> boost::get<7>(t); + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, boost::tuple &t) +{ + stream >> boost::get<0>(t); + stream >> boost::get<1>(t); + stream >> boost::get<2>(t); + stream >> boost::get<3>(t); + stream >> boost::get<4>(t); + stream >> boost::get<5>(t); + stream >> boost::get<6>(t); + stream >> boost::get<7>(t); + stream >> boost::get<8>(t); + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, boost::tuple &t) +{ + stream >> boost::get<0>(t); + stream >> boost::get<1>(t); + stream >> boost::get<2>(t); + stream >> boost::get<3>(t); + stream >> boost::get<4>(t); + stream >> boost::get<5>(t); + stream >> boost::get<6>(t); + stream >> boost::get<7>(t); + stream >> boost::get<8>(t); + stream >> boost::get<9>(t); + return stream; +} + +#endif // _QX_SERIALIZE_QDATASTREAM_BOOST_TUPLE_H_ +#endif // _QX_ENABLE_BOOST diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_unordered_map.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_unordered_map.h new file mode 100644 index 0000000..4521202 --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_unordered_map.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST +#ifndef _QX_SERIALIZE_QDATASTREAM_BOOST_UNORDERED_MAP_H_ +#define _QX_SERIALIZE_QDATASTREAM_BOOST_UNORDERED_MAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_boost_unordered_map.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type boost::unordered_map and boost::unordered_multimap + */ + +#include + +template +QDataStream &operator<<(QDataStream &stream, const boost::unordered_map &t) +{ + typedef typename boost::unordered_map::const_iterator type_itr; + quint64 uiSize = static_cast(t.size()); + stream << uiSize; + + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + stream << itr->first; + stream << itr->second; + } + + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, boost::unordered_map &t) +{ + quint64 uiSize = 0; + stream >> uiSize; + t.clear(); + t.reserve(static_cast::size_type>(uiSize)); + + for (quint64 i = 0; i < uiSize; ++i) + { + Key key; + stream >> key; + Value value; + stream >> value; + t.insert(std::make_pair(key, value)); + } + + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const boost::unordered_multimap &t) +{ + typedef typename boost::unordered_multimap::const_iterator type_itr; + quint64 uiSize = static_cast(t.size()); + stream << uiSize; + + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + stream << itr->first; + stream << itr->second; + } + + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, boost::unordered_multimap &t) +{ + quint64 uiSize = 0; + stream >> uiSize; + t.clear(); + t.reserve(static_cast::size_type>(uiSize)); + + for (quint64 i = 0; i < uiSize; ++i) + { + Key key; + stream >> key; + Value value; + stream >> value; + t.insert(std::make_pair(key, value)); + } + + return stream; +} + +#endif // _QX_SERIALIZE_QDATASTREAM_BOOST_UNORDERED_MAP_H_ +#endif // _QX_ENABLE_BOOST diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_unordered_set.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_unordered_set.h new file mode 100644 index 0000000..74c5208 --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_boost_unordered_set.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST +#ifndef _QX_SERIALIZE_QDATASTREAM_BOOST_UNORDERED_SET_H_ +#define _QX_SERIALIZE_QDATASTREAM_BOOST_UNORDERED_SET_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_boost_unordered_set.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type boost::unordered_set and boost::unordered_multiset + */ + +#include + +template +QDataStream &operator<<(QDataStream &stream, const boost::unordered_set &t) +{ + typedef typename boost::unordered_set::const_iterator type_itr; + quint64 uiSize = static_cast(t.size()); + stream << uiSize; + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + stream << (*itr); + } + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, boost::unordered_set &t) +{ + quint64 uiSize = 0; + stream >> uiSize; + t.clear(); + t.reserve(static_cast::size_type>(uiSize)); + for (quint64 i = 0; i < uiSize; ++i) + { + T tmp; + stream >> tmp; + t.insert(tmp); + } + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const boost::unordered_multiset &t) +{ + typedef typename boost::unordered_multiset::const_iterator type_itr; + quint64 uiSize = static_cast(t.size()); + stream << uiSize; + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + stream << (*itr); + } + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, boost::unordered_multiset &t) +{ + quint64 uiSize = 0; + stream >> uiSize; + t.clear(); + t.reserve(static_cast::size_type>(uiSize)); + for (quint64 i = 0; i < uiSize; ++i) + { + T tmp; + stream >> tmp; + t.insert(tmp); + } + return stream; +} + +#endif // _QX_SERIALIZE_QDATASTREAM_BOOST_UNORDERED_SET_H_ +#endif // _QX_ENABLE_BOOST diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_primitive_type.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_primitive_type.h new file mode 100644 index 0000000..c1d5103 --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_primitive_type.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_QDATASTREAM_PRIMITIVE_TYPE_H_ +#define _QX_SERIALIZE_QDATASTREAM_PRIMITIVE_TYPE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_primitive_type.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for primitive types (long, etc...) + */ + +#include + +QX_DLL_EXPORT QDataStream &operator<<(QDataStream &stream, const long &t) QX_USED; +QX_DLL_EXPORT QDataStream &operator>>(QDataStream &stream, long &t) QX_USED; + +#endif // _QX_SERIALIZE_QDATASTREAM_PRIMITIVE_TYPE_H_ diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_qx_registered_class.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_qx_registered_class.h new file mode 100644 index 0000000..5dfce1e --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_qx_registered_class.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_QDATASTREAM_QX_REGISTERED_CLASS_H_ +#define _QX_SERIALIZE_QDATASTREAM_QX_REGISTERED_CLASS_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_qx_registered_class.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a generic Qt QDataStream serialization method (save/load) for classes registered into QxOrm context (void qx::register_class() function), it is possible to specialize qx::QxSerializeRegistered template to implement your own serialization method for a specific class + */ + +#include + +#include + +#include +#include + +namespace qx +{ + + struct QX_DLL_EXPORT QxSerializeRegistered_Helper + { + + static QDataStream &save(QDataStream &stream, IxClass *pClass, const void *pOwner); + static QDataStream &load(QDataStream &stream, IxClass *pClass, void *pOwner); + + private: + static void saveHelper(QDataStream &stream, IxClass *pClass, const void *pOwner); + static void loadHelper(QDataStream &stream, IxClass *pClass, void *pOwner); + }; + + template + struct QxSerializeRegistered + { + + enum + { + is_valid = qx::trait::is_qx_registered::value + }; + + static QDataStream &save(QDataStream &stream, const T &t) + { + static_assert(is_valid, "is_valid"); + return qx::QxSerializeRegistered_Helper::save(stream, qx::QxClass::getSingleton(), (&t)); + } + + static QDataStream &load(QDataStream &stream, T &t) + { + static_assert(is_valid, "is_valid"); + return qx::QxSerializeRegistered_Helper::load(stream, qx::QxClass::getSingleton(), (&t)); + } + }; + +} // namespace qx + +#endif // _QX_SERIALIZE_QDATASTREAM_QX_REGISTERED_CLASS_H_ diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_list.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_list.h new file mode 100644 index 0000000..bc92f67 --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_list.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_QDATASTREAM_STD_LIST_H_ +#define _QX_SERIALIZE_QDATASTREAM_STD_LIST_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_std_list.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type std::list + */ + +#include + +#include + +template +QDataStream &operator<<(QDataStream &stream, const std::list &t) +{ + typedef typename std::list::const_iterator type_itr; + quint64 uiSize = static_cast(t.size()); + stream << uiSize; + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + stream << (*itr); + } + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::list &t) +{ + quint64 uiSize = 0; + stream >> uiSize; + t.clear(); + for (quint64 i = 0; i < uiSize; ++i) + { + T tmp; + stream >> tmp; + t.push_back(tmp); + } + return stream; +} + +#endif // _QX_SERIALIZE_QDATASTREAM_STD_LIST_H_ diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_map.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_map.h new file mode 100644 index 0000000..03559ad --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_map.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_QDATASTREAM_STD_MAP_H_ +#define _QX_SERIALIZE_QDATASTREAM_STD_MAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_std_map.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type std::map + */ + +#include + +#include + +template +QDataStream &operator<<(QDataStream &stream, const std::map &t) +{ + typedef typename std::map::const_iterator type_itr; + quint64 uiSize = static_cast(t.size()); + stream << uiSize; + + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + stream << itr->first; + stream << itr->second; + } + + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::map &t) +{ + quint64 uiSize = 0; + stream >> uiSize; + t.clear(); + + for (quint64 i = 0; i < uiSize; ++i) + { + Key key; + stream >> key; + Value value; + stream >> value; + t.insert(std::make_pair(key, value)); + } + + return stream; +} + +#endif // _QX_SERIALIZE_QDATASTREAM_STD_MAP_H_ diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_pair.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_pair.h new file mode 100644 index 0000000..a02e43b --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_pair.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#ifndef _QX_SERIALIZE_QDATASTREAM_STD_PAIR_H_ +#define _QX_SERIALIZE_QDATASTREAM_STD_PAIR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_std_pair.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type std::pair + */ + +#include + +template +QDataStream &operator<<(QDataStream &stream, const std::pair &t) +{ + stream << t.first; + stream << t.second; + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::pair &t) +{ + stream >> t.first; + stream >> t.second; + return stream; +} + +#endif // _QX_SERIALIZE_QDATASTREAM_STD_PAIR_H_ +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_set.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_set.h new file mode 100644 index 0000000..1412c3f --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_set.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_QDATASTREAM_STD_SET_H_ +#define _QX_SERIALIZE_QDATASTREAM_STD_SET_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_std_set.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type std::set + */ + +#include + +#include + +template +QDataStream &operator<<(QDataStream &stream, const std::set &t) +{ + typedef typename std::set::const_iterator type_itr; + quint64 uiSize = static_cast(t.size()); + stream << uiSize; + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + stream << (*itr); + } + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::set &t) +{ + quint64 uiSize = 0; + stream >> uiSize; + t.clear(); + for (quint64 i = 0; i < uiSize; ++i) + { + T tmp; + stream >> tmp; + t.insert(tmp); + } + return stream; +} + +#endif // _QX_SERIALIZE_QDATASTREAM_STD_SET_H_ diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_shared_ptr.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_shared_ptr.h new file mode 100644 index 0000000..dd3dcb6 --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_shared_ptr.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_QDATASTREAM_STD_SHARED_PTR_H_ +#define _QX_SERIALIZE_QDATASTREAM_STD_SHARED_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_std_shared_ptr.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type std::shared_ptr (C++11 compilation option _QX_CPP_11_SMART_PTR must be defined) + */ + +#include + +template +QDataStream &operator<<(QDataStream &stream, const std::shared_ptr &t) +{ + qint8 iIsNull = (t ? 0 : 1); + stream << iIsNull; + if (t) + { + stream << (*t); + } + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::shared_ptr &t) +{ + qint8 iIsNull = 0; + stream >> iIsNull; + if (!iIsNull) + { + t = std::make_shared(); + stream >> (*t); + } + else + { + t.reset(); + } + return stream; +} + +#endif // _QX_SERIALIZE_QDATASTREAM_STD_SHARED_PTR_H_ diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_string.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_string.h new file mode 100644 index 0000000..ba75390 --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_string.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_QDATASTREAM_STD_STRING_H_ +#define _QX_SERIALIZE_QDATASTREAM_STD_STRING_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_std_string.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type std::string and std::wstring + */ + +#include + +#include + +QX_DLL_EXPORT QDataStream &operator<<(QDataStream &stream, const std::string &t) QX_USED; +QX_DLL_EXPORT QDataStream &operator>>(QDataStream &stream, std::string &t) QX_USED; + +QX_DLL_EXPORT QDataStream &operator<<(QDataStream &stream, const std::wstring &t) QX_USED; +QX_DLL_EXPORT QDataStream &operator>>(QDataStream &stream, std::wstring &t) QX_USED; + +#endif // _QX_SERIALIZE_QDATASTREAM_STD_STRING_H_ diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_tuple.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_tuple.h new file mode 100644 index 0000000..209027f --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_tuple.h @@ -0,0 +1,264 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_QDATASTREAM_STD_TUPLE_H_ +#define _QX_SERIALIZE_QDATASTREAM_STD_TUPLE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_std_tuple.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type std::tuple (C++11 compilation option _QX_CPP_11_TUPLE must be defined) + */ + +#include + +template +QDataStream &operator<<(QDataStream &stream, const std::tuple &t) +{ + stream << std::get<0>(t); + stream << std::get<1>(t); + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const std::tuple &t) +{ + stream << std::get<0>(t); + stream << std::get<1>(t); + stream << std::get<2>(t); + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const std::tuple &t) +{ + stream << std::get<0>(t); + stream << std::get<1>(t); + stream << std::get<2>(t); + stream << std::get<3>(t); + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const std::tuple &t) +{ + stream << std::get<0>(t); + stream << std::get<1>(t); + stream << std::get<2>(t); + stream << std::get<3>(t); + stream << std::get<4>(t); + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const std::tuple &t) +{ + stream << std::get<0>(t); + stream << std::get<1>(t); + stream << std::get<2>(t); + stream << std::get<3>(t); + stream << std::get<4>(t); + stream << std::get<5>(t); + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const std::tuple &t) +{ + stream << std::get<0>(t); + stream << std::get<1>(t); + stream << std::get<2>(t); + stream << std::get<3>(t); + stream << std::get<4>(t); + stream << std::get<5>(t); + stream << std::get<6>(t); + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const std::tuple &t) +{ + stream << std::get<0>(t); + stream << std::get<1>(t); + stream << std::get<2>(t); + stream << std::get<3>(t); + stream << std::get<4>(t); + stream << std::get<5>(t); + stream << std::get<6>(t); + stream << std::get<7>(t); + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const std::tuple &t) +{ + stream << std::get<0>(t); + stream << std::get<1>(t); + stream << std::get<2>(t); + stream << std::get<3>(t); + stream << std::get<4>(t); + stream << std::get<5>(t); + stream << std::get<6>(t); + stream << std::get<7>(t); + stream << std::get<8>(t); + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const std::tuple &t) +{ + stream << std::get<0>(t); + stream << std::get<1>(t); + stream << std::get<2>(t); + stream << std::get<3>(t); + stream << std::get<4>(t); + stream << std::get<5>(t); + stream << std::get<6>(t); + stream << std::get<7>(t); + stream << std::get<8>(t); + stream << std::get<9>(t); + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::tuple &t) +{ + stream >> std::get<0>(t); + stream >> std::get<1>(t); + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::tuple &t) +{ + stream >> std::get<0>(t); + stream >> std::get<1>(t); + stream >> std::get<2>(t); + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::tuple &t) +{ + stream >> std::get<0>(t); + stream >> std::get<1>(t); + stream >> std::get<2>(t); + stream >> std::get<3>(t); + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::tuple &t) +{ + stream >> std::get<0>(t); + stream >> std::get<1>(t); + stream >> std::get<2>(t); + stream >> std::get<3>(t); + stream >> std::get<4>(t); + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::tuple &t) +{ + stream >> std::get<0>(t); + stream >> std::get<1>(t); + stream >> std::get<2>(t); + stream >> std::get<3>(t); + stream >> std::get<4>(t); + stream >> std::get<5>(t); + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::tuple &t) +{ + stream >> std::get<0>(t); + stream >> std::get<1>(t); + stream >> std::get<2>(t); + stream >> std::get<3>(t); + stream >> std::get<4>(t); + stream >> std::get<5>(t); + stream >> std::get<6>(t); + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::tuple &t) +{ + stream >> std::get<0>(t); + stream >> std::get<1>(t); + stream >> std::get<2>(t); + stream >> std::get<3>(t); + stream >> std::get<4>(t); + stream >> std::get<5>(t); + stream >> std::get<6>(t); + stream >> std::get<7>(t); + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::tuple &t) +{ + stream >> std::get<0>(t); + stream >> std::get<1>(t); + stream >> std::get<2>(t); + stream >> std::get<3>(t); + stream >> std::get<4>(t); + stream >> std::get<5>(t); + stream >> std::get<6>(t); + stream >> std::get<7>(t); + stream >> std::get<8>(t); + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::tuple &t) +{ + stream >> std::get<0>(t); + stream >> std::get<1>(t); + stream >> std::get<2>(t); + stream >> std::get<3>(t); + stream >> std::get<4>(t); + stream >> std::get<5>(t); + stream >> std::get<6>(t); + stream >> std::get<7>(t); + stream >> std::get<8>(t); + stream >> std::get<9>(t); + return stream; +} + +#endif // _QX_SERIALIZE_QDATASTREAM_STD_TUPLE_H_ diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_unique_ptr.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_unique_ptr.h new file mode 100644 index 0000000..859987f --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_unique_ptr.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_QDATASTREAM_STD_UNIQUE_PTR_H_ +#define _QX_SERIALIZE_QDATASTREAM_STD_UNIQUE_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_std_unique_ptr.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type std::unique_ptr (C++11 compilation option _QX_CPP_11_SMART_PTR must be defined) + */ + +#include + +template +QDataStream &operator<<(QDataStream &stream, const std::unique_ptr &t) +{ + qint8 iIsNull = (t ? 0 : 1); + stream << iIsNull; + if (t) + { + stream << (*t); + } + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::unique_ptr &t) +{ + qint8 iIsNull = 0; + stream >> iIsNull; + if (!iIsNull) + { + t.reset(new T()); + stream >> (*t); + } + else + { + t.reset(); + } + return stream; +} + +#endif // _QX_SERIALIZE_QDATASTREAM_STD_UNIQUE_PTR_H_ diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_unordered_map.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_unordered_map.h new file mode 100644 index 0000000..6b2aea8 --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_unordered_map.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_QDATASTREAM_STD_UNORDERED_MAP_H_ +#define _QX_SERIALIZE_QDATASTREAM_STD_UNORDERED_MAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_std_unordered_map.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type std::unordered_map and std::unordered_multimap (C++11 compilation option _QX_CPP_11_CONTAINER must be defined) + */ + +#include + +#include + +template +QDataStream &operator<<(QDataStream &stream, const std::unordered_map &t) +{ + typedef typename std::unordered_map::const_iterator type_itr; + quint64 uiSize = static_cast(t.size()); + stream << uiSize; + + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + stream << itr->first; + stream << itr->second; + } + + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::unordered_map &t) +{ + quint64 uiSize = 0; + stream >> uiSize; + t.clear(); + t.reserve(static_cast::size_type>(uiSize)); + + for (quint64 i = 0; i < uiSize; ++i) + { + Key key; + stream >> key; + Value value; + stream >> value; + t.insert(std::make_pair(key, value)); + } + + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const std::unordered_multimap &t) +{ + typedef typename std::unordered_multimap::const_iterator type_itr; + quint64 uiSize = static_cast(t.size()); + stream << uiSize; + + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + stream << itr->first; + stream << itr->second; + } + + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::unordered_multimap &t) +{ + quint64 uiSize = 0; + stream >> uiSize; + t.clear(); + t.reserve(static_cast::size_type>(uiSize)); + + for (quint64 i = 0; i < uiSize; ++i) + { + Key key; + stream >> key; + Value value; + stream >> value; + t.insert(std::make_pair(key, value)); + } + + return stream; +} + +#endif // _QX_SERIALIZE_QDATASTREAM_STD_UNORDERED_MAP_H_ diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_unordered_set.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_unordered_set.h new file mode 100644 index 0000000..cf796cc --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_unordered_set.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_QDATASTREAM_STD_UNORDERED_SET_H_ +#define _QX_SERIALIZE_QDATASTREAM_STD_UNORDERED_SET_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_std_unordered_set.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type std::unordered_set and std::unordered_multiset (C++11 compilation option _QX_CPP_11_CONTAINER must be defined) + */ + +#include + +#include + +template +QDataStream &operator<<(QDataStream &stream, const std::unordered_set &t) +{ + typedef typename std::unordered_set::const_iterator type_itr; + quint64 uiSize = static_cast(t.size()); + stream << uiSize; + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + stream << (*itr); + } + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::unordered_set &t) +{ + quint64 uiSize = 0; + stream >> uiSize; + t.clear(); + t.reserve(static_cast::size_type>(uiSize)); + for (quint64 i = 0; i < uiSize; ++i) + { + T tmp; + stream >> tmp; + t.insert(tmp); + } + return stream; +} + +template +QDataStream &operator<<(QDataStream &stream, const std::unordered_multiset &t) +{ + typedef typename std::unordered_multiset::const_iterator type_itr; + quint64 uiSize = static_cast(t.size()); + stream << uiSize; + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + stream << (*itr); + } + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::unordered_multiset &t) +{ + quint64 uiSize = 0; + stream >> uiSize; + t.clear(); + t.reserve(static_cast::size_type>(uiSize)); + for (quint64 i = 0; i < uiSize; ++i) + { + T tmp; + stream >> tmp; + t.insert(tmp); + } + return stream; +} + +#endif // _QX_SERIALIZE_QDATASTREAM_STD_UNORDERED_SET_H_ diff --git a/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_vector.h b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_vector.h new file mode 100644 index 0000000..3cd2977 --- /dev/null +++ b/include/QxSerialize/QDataStream/QxSerializeQDataStream_std_vector.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_QDATASTREAM_STD_VECTOR_H_ +#define _QX_SERIALIZE_QDATASTREAM_STD_VECTOR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream_std_vector.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QDataStream serialization method (save/load) for type std::vector + */ + +#include + +#include + +template +QDataStream &operator<<(QDataStream &stream, const std::vector &t) +{ + typedef typename std::vector::const_iterator type_itr; + quint64 uiSize = static_cast(t.size()); + stream << uiSize; + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + stream << (*itr); + } + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, std::vector &t) +{ + quint64 uiSize = 0; + stream >> uiSize; + t.clear(); + t.reserve(static_cast::size_type>(uiSize)); + for (quint64 i = 0; i < uiSize; ++i) + { + T tmp; + stream >> tmp; + t.push_back(tmp); + } + return stream; +} + +#endif // _QX_SERIALIZE_QDATASTREAM_STD_VECTOR_H_ diff --git a/include/QxSerialize/QJson/QxSerializeQJson_IxParameter.h b/include/QxSerialize/QJson/QxSerializeQJson_IxParameter.h new file mode 100644 index 0000000..182629a --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_IxParameter.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _QX_SERIALIZE_QJSON_IX_PARAMETER_H_ +#define _QX_SERIALIZE_QJSON_IX_PARAMETER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_IxParameter.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type qx::service::IxParameter + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const qx::service::IxParameter &t, const QString &format) + { + Q_UNUSED(t); + Q_UNUSED(format); + return QJsonValue(); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, qx::service::IxParameter &t, const QString &format) + { + Q_UNUSED(j); + Q_UNUSED(t); + Q_UNUSED(format); + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_IX_PARAMETER_H_ +#endif // _QX_ENABLE_QT_NETWORK +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_IxPersistable.h b/include/QxSerialize/QJson/QxSerializeQJson_IxPersistable.h new file mode 100644 index 0000000..21a0eb2 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_IxPersistable.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_IX_PERSISTABLE_H_ +#define _QX_SERIALIZE_QJSON_IX_PERSISTABLE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_IxPersistable.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type qx::IxPersistable + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const qx::IxPersistable &t, const QString &format) + { + QJsonParseError err; + QString json = t.toJson(format); + QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8(), (&err)); + if (err.error != QJsonParseError::NoError) + { + return QJsonValue(); + } + return (doc.isArray() ? QJsonValue(doc.array()) : QJsonValue(doc.object())); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, qx::IxPersistable &t, const QString &format) + { + QJsonDocument doc = (j.isArray() ? QJsonDocument(j.toArray()) : QJsonDocument(j.toObject())); + QString json = QString::fromUtf8(doc.toJson()); + return t.fromJson(json, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_IX_PERSISTABLE_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_IxService.h b/include/QxSerialize/QJson/QxSerializeQJson_IxService.h new file mode 100644 index 0000000..ab94b46 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_IxService.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _QX_SERIALIZE_QJSON_IX_SERVICE_H_ +#define _QX_SERIALIZE_QJSON_IX_SERVICE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_IxService.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type qx::service::IxService + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const qx::service::IxService &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, qx::service::IxService &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_IX_SERVICE_H_ +#endif // _QX_ENABLE_QT_NETWORK +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_IxSqlElement.h b/include/QxSerialize/QJson/QxSerializeQJson_IxSqlElement.h new file mode 100644 index 0000000..b497378 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_IxSqlElement.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_IX_SQL_ELEMENT_H_ +#define _QX_SERIALIZE_QJSON_IX_SQL_ELEMENT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_IxSqlElement.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type qx::dao::detail::IxSqlElement + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const qx::dao::detail::IxSqlElement &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, qx::dao::detail::IxSqlElement &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_IX_SQL_ELEMENT_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QBrush.h b/include/QxSerialize/QJson/QxSerializeQJson_QBrush.h new file mode 100644 index 0000000..1720b67 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QBrush.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_GUI +#ifndef _QX_SERIALIZE_QJSON_QBRUSH_H_ +#define _QX_SERIALIZE_QJSON_QBRUSH_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QBrush.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QBrush + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const QBrush &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QBrush &t, const QString &format) QX_USED; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QBrush &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QBrush &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QBRUSH_H_ +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QColor.h b/include/QxSerialize/QJson/QxSerializeQJson_QColor.h new file mode 100644 index 0000000..b1955c3 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QColor.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_GUI +#ifndef _QX_SERIALIZE_QJSON_QCOLOR_H_ +#define _QX_SERIALIZE_QJSON_QCOLOR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QColor.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QColor + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const QColor &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QColor &t, const QString &format) QX_USED; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QColor &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QColor &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QCOLOR_H_ +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QFlags.h b/include/QxSerialize/QJson/QxSerializeQJson_QFlags.h new file mode 100644 index 0000000..6f23e07 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QFlags.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QFLAGS_H_ +#define _QX_SERIALIZE_QJSON_QFLAGS_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QFlags.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QFlags + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const QFlags &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(static_cast(t)); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, QFlags &t, const QString &format) + { + Q_UNUSED(format); + t = QFlags(QFlag(qRound(j.toDouble()))); + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QFLAGS_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QFont.h b/include/QxSerialize/QJson/QxSerializeQJson_QFont.h new file mode 100644 index 0000000..97fbe24 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QFont.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_GUI +#ifndef _QX_SERIALIZE_QJSON_QFONT_H_ +#define _QX_SERIALIZE_QJSON_QFONT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QFont.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QFont + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const QFont &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QFont &t, const QString &format) QX_USED; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QFont &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QFont &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QFONT_H_ +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QHash.h b/include/QxSerialize/QJson/QxSerializeQJson_QHash.h new file mode 100644 index 0000000..9285e6c --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QHash.h @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QHASH_H_ +#define _QX_SERIALIZE_QJSON_QHASH_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QHash.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QHash + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const QHash &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + QHashIterator itr(t); + + while (itr.hasNext()) + { + itr.next(); + QJsonObject obj; + val = qx::cvt::to_json(itr.key(), format); + obj.insert("key", val); + val = qx::cvt::to_json(itr.value(), format); + obj.insert("value", val); + arr.append(obj); + } + + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, QHash &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + QJsonValue val; + QJsonObject obj; + t.reserve(arr.count()); + + for (int i = 0; i < arr.count(); i++) + { + val = arr.at(i); + if (!val.isObject()) + { + continue; + } + obj = val.toObject(); + Key key; + Value value; + qx::cvt::from_json(obj.value("key"), key, format); + qx::cvt::from_json(obj.value("value"), value, format); + t.insert(key, value); + } + + return qx_bool(true); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const QHash &t, const QString &format) + { + QJsonObject obj; + QJsonValue val; + QHashIterator itr(t); + + while (itr.hasNext()) + { + itr.next(); + val = qx::cvt::to_json(itr.value(), format); + obj.insert(itr.key(), val); + } + + return QJsonValue(obj); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, QHash &t, const QString &format) + { + t.clear(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + QJsonValue val; + t.reserve(obj.count()); + + for (QJsonObject::const_iterator itr = obj.constBegin(); itr != obj.constEnd(); ++itr) + { + QString key = itr.key(); + Value value; + qx::cvt::from_json(itr.value(), value, format); + t.insert(key, value); + } + + return qx_bool(true); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const QHash &t, const QString &format) + { + QJsonObject obj; + QJsonValue val; + QHashIterator itr(t); + + while (itr.hasNext()) + { + itr.next(); + +#ifndef QT_NO_STL + QString key = QString::fromStdString(itr.key()); +#else // QT_NO_STL + std::string s = itr.key(); + QString key = QString::fromLatin1(s.data(), int(s.size())); +#endif // QT_NO_STL + + val = qx::cvt::to_json(itr.value(), format); + obj.insert(key, val); + } + + return QJsonValue(obj); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, QHash &t, const QString &format) + { + t.clear(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + QJsonValue val; + t.reserve(obj.count()); + + for (QJsonObject::const_iterator itr = obj.constBegin(); itr != obj.constEnd(); ++itr) + { + QString key = itr.key(); + Value value; + qx::cvt::from_json(itr.value(), value, format); + +#ifndef QT_NO_STL + std::string s = key.toStdString(); +#else // QT_NO_STL + std::string s = key.toLatin1().constData(); +#endif // QT_NO_STL + + t.insert(s, value); + } + + return qx_bool(true); + } + }; + +#if ((!defined(QT_NO_STL)) && (!defined(QT_NO_STL_WCHAR))) + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const QHash &t, const QString &format) + { + QJsonObject obj; + QJsonValue val; + QHashIterator itr(t); + + while (itr.hasNext()) + { + itr.next(); + val = qx::cvt::to_json(itr.value(), format); + obj.insert(QString::fromStdWString(itr.key()), val); + } + + return QJsonValue(obj); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, QHash &t, const QString &format) + { + t.clear(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + QJsonValue val; + t.reserve(obj.count()); + + for (QJsonObject::const_iterator itr = obj.constBegin(); itr != obj.constEnd(); ++itr) + { + QString key = itr.key(); + Value value; + qx::cvt::from_json(itr.value(), value, format); + t.insert(key.toStdWString(), value); + } + + return qx_bool(true); + } + }; + +#endif // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QHASH_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QImage.h b/include/QxSerialize/QJson/QxSerializeQJson_QImage.h new file mode 100644 index 0000000..7fb2ad8 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QImage.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_GUI +#ifndef _QX_SERIALIZE_QJSON_QIMAGE_H_ +#define _QX_SERIALIZE_QJSON_QIMAGE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QImage.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QImage + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const QImage &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QImage &t, const QString &format) QX_USED; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QImage &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QImage &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QIMAGE_H_ +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QLinkedList.h b/include/QxSerialize/QJson/QxSerializeQJson_QLinkedList.h new file mode 100644 index 0000000..5ca8e12 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QLinkedList.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QLINKEDLIST_H_ +#define _QX_SERIALIZE_QJSON_QLINKEDLIST_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QLinkedList.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QLinkedList + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const QLinkedList &t, const QString &format) + { + QJsonArray arr; + QLinkedListIterator itr(t); + while (itr.hasNext()) + { + arr.append(qx::cvt::to_json(itr.next(), format)); + } + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, QLinkedList &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + for (int i = 0; i < arr.count(); i++) + { + T tmp; + qx::cvt::from_json(arr.at(i), tmp, format); + t.append(tmp); + } + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QLINKEDLIST_H_ +#endif // _QX_NO_JSON +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QList.h b/include/QxSerialize/QJson/QxSerializeQJson_QList.h new file mode 100644 index 0000000..a3540c8 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QList.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QLIST_H_ +#define _QX_SERIALIZE_QJSON_QLIST_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QList.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QList + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const QList &t, const QString &format) + { + QJsonArray arr; + for (int i = 0; i < t.count(); i++) + { + arr.append(qx::cvt::to_json(t.at(i), format)); + } + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, QList &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + +#if (QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) + t.reserve(arr.count()); +#endif // (QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) + + for (int i = 0; i < arr.count(); i++) + { + T tmp; + qx::cvt::from_json(arr.at(i), tmp, format); + t.append(tmp); + } + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QLIST_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QMap.h b/include/QxSerialize/QJson/QxSerializeQJson_QMap.h new file mode 100644 index 0000000..95a05ca --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QMap.h @@ -0,0 +1,278 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QMAP_H_ +#define _QX_SERIALIZE_QJSON_QMAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QMap.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QMap + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const QMap &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + QMapIterator itr(t); + + while (itr.hasNext()) + { + itr.next(); + QJsonObject obj; + val = qx::cvt::to_json(itr.key(), format); + obj.insert("key", val); + val = qx::cvt::to_json(itr.value(), format); + obj.insert("value", val); + arr.append(obj); + } + + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, QMap &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + QJsonValue val; + QJsonObject obj; + + for (int i = 0; i < arr.count(); i++) + { + val = arr.at(i); + if (!val.isObject()) + { + continue; + } + obj = val.toObject(); + Key key; + Value value; + qx::cvt::from_json(obj.value("key"), key, format); + qx::cvt::from_json(obj.value("value"), value, format); + t.insert(key, value); + } + + return qx_bool(true); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const QMap &t, const QString &format) + { + QJsonObject obj; + QJsonValue val; + QMapIterator itr(t); + + while (itr.hasNext()) + { + itr.next(); + val = qx::cvt::to_json(itr.value(), format); + obj.insert(itr.key(), val); + } + + return QJsonValue(obj); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, QMap &t, const QString &format) + { + t.clear(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + QJsonValue val; + + for (QJsonObject::const_iterator itr = obj.constBegin(); itr != obj.constEnd(); ++itr) + { + QString key = itr.key(); + Value value; + qx::cvt::from_json(itr.value(), value, format); + t.insert(key, value); + } + + return qx_bool(true); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const QMap &t, const QString &format) + { + QJsonObject obj; + QJsonValue val; + QMapIterator itr(t); + + while (itr.hasNext()) + { + itr.next(); + +#ifndef QT_NO_STL + QString key = QString::fromStdString(itr.key()); +#else // QT_NO_STL + std::string s = itr.key(); + QString key = QString::fromLatin1(s.data(), int(s.size())); +#endif // QT_NO_STL + + val = qx::cvt::to_json(itr.value(), format); + obj.insert(key, val); + } + + return QJsonValue(obj); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, QMap &t, const QString &format) + { + t.clear(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + QJsonValue val; + + for (QJsonObject::const_iterator itr = obj.constBegin(); itr != obj.constEnd(); ++itr) + { + QString key = itr.key(); + Value value; + qx::cvt::from_json(itr.value(), value, format); + +#ifndef QT_NO_STL + std::string s = key.toStdString(); +#else // QT_NO_STL + std::string s = key.toLatin1().constData(); +#endif // QT_NO_STL + + t.insert(s, value); + } + + return qx_bool(true); + } + }; + +#if ((!defined(QT_NO_STL)) && (!defined(QT_NO_STL_WCHAR))) + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const QMap &t, const QString &format) + { + QJsonObject obj; + QJsonValue val; + QMapIterator itr(t); + + while (itr.hasNext()) + { + itr.next(); + val = qx::cvt::to_json(itr.value(), format); + obj.insert(QString::fromStdWString(itr.key()), val); + } + + return QJsonValue(obj); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, QMap &t, const QString &format) + { + t.clear(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + QJsonValue val; + + for (QJsonObject::const_iterator itr = obj.constBegin(); itr != obj.constEnd(); ++itr) + { + QString key = itr.key(); + Value value; + qx::cvt::from_json(itr.value(), value, format); + t.insert(key.toStdWString(), value); + } + + return qx_bool(true); + } + }; + +#endif // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QMAP_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QMatrix.h b/include/QxSerialize/QJson/QxSerializeQJson_QMatrix.h new file mode 100644 index 0000000..91aea97 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QMatrix.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_GUI +#ifndef _QX_SERIALIZE_QJSON_QMATRIX_H_ +#define _QX_SERIALIZE_QJSON_QMATRIX_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QMatrix.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QMatrix + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const QMatrix &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QMatrix &t, const QString &format) QX_USED; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QMatrix &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QMatrix &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QMATRIX_H_ +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_NO_JSON +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QMultiHash.h b/include/QxSerialize/QJson/QxSerializeQJson_QMultiHash.h new file mode 100644 index 0000000..e3bc4f7 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QMultiHash.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QMULTIHASH_H_ +#define _QX_SERIALIZE_QJSON_QMULTIHASH_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QMultiHash.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QMultiHash + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const QMultiHash &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + QHashIterator itr(t); + + while (itr.hasNext()) + { + itr.next(); + QJsonObject obj; + val = qx::cvt::to_json(itr.key(), format); + obj.insert("key", val); + val = qx::cvt::to_json(itr.value(), format); + obj.insert("value", val); + arr.append(obj); + } + + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, QMultiHash &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + QJsonValue val; + QJsonObject obj; + t.reserve(arr.count()); + + for (int i = 0; i < arr.count(); i++) + { + val = arr.at(i); + if (!val.isObject()) + { + continue; + } + obj = val.toObject(); + Key key; + Value value; + qx::cvt::from_json(obj.value("key"), key, format); + qx::cvt::from_json(obj.value("value"), value, format); + t.insert(key, value); + } + + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QMULTIHASH_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QMultiMap.h b/include/QxSerialize/QJson/QxSerializeQJson_QMultiMap.h new file mode 100644 index 0000000..ea28343 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QMultiMap.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QMULTIMAP_H_ +#define _QX_SERIALIZE_QJSON_QMULTIMAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QMultiMap.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QMultiMap + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const QMultiMap &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + QMapIterator itr(t); + + while (itr.hasNext()) + { + itr.next(); + QJsonObject obj; + val = qx::cvt::to_json(itr.key(), format); + obj.insert("key", val); + val = qx::cvt::to_json(itr.value(), format); + obj.insert("value", val); + arr.append(obj); + } + + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, QMultiMap &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + QJsonValue val; + QJsonObject obj; + + for (int i = 0; i < arr.count(); i++) + { + val = arr.at(i); + if (!val.isObject()) + { + continue; + } + obj = val.toObject(); + Key key; + Value value; + qx::cvt::from_json(obj.value("key"), key, format); + qx::cvt::from_json(obj.value("value"), value, format); + t.insert(key, value); + } + + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QMULTIMAP_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QObject.h b/include/QxSerialize/QJson/QxSerializeQJson_QObject.h new file mode 100644 index 0000000..02de41e --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QObject.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QOBJECT_H_ +#define _QX_SERIALIZE_QJSON_QOBJECT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QObject.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QObject (serialize dynamic properties) + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const QObject &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QObject &t, const QString &format) QX_USED; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QObject &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QObject &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QOBJECT_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QPair.h b/include/QxSerialize/QJson/QxSerializeQJson_QPair.h new file mode 100644 index 0000000..b0dde00 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QPair.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QPAIR_H_ +#define _QX_SERIALIZE_QJSON_QPAIR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QPair.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QPair + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const QPair &t, const QString &format) + { + QJsonArray arr; + arr.append(qx::cvt::to_json(t.first, format)); + arr.append(qx::cvt::to_json(t.second, format)); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, QPair &t, const QString &format) + { + t = QPair(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + if (arr.count() != 2) + { + return qx_bool(true); + } + T1 tmp1; + qx::cvt::from_json(arr.at(0), tmp1, format); + T2 tmp2; + qx::cvt::from_json(arr.at(1), tmp2, format); + t = qMakePair(tmp1, tmp2); + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QPAIR_H_ +#endif // _QX_NO_JSON +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QPicture.h b/include/QxSerialize/QJson/QxSerializeQJson_QPicture.h new file mode 100644 index 0000000..5e9c8b0 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QPicture.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_GUI +#ifndef _QX_SERIALIZE_QJSON_QPICTURE_H_ +#define _QX_SERIALIZE_QJSON_QPICTURE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QPicture.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QPicture + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const QPicture &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QPicture &t, const QString &format) QX_USED; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QPicture &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QPicture &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QPICTURE_H_ +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QPixmap.h b/include/QxSerialize/QJson/QxSerializeQJson_QPixmap.h new file mode 100644 index 0000000..de0e0b9 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QPixmap.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_GUI +#ifndef _QX_SERIALIZE_QJSON_QPIXMAP_H_ +#define _QX_SERIALIZE_QJSON_QPIXMAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QPixmap.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QPixmap + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const QPixmap &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QPixmap &t, const QString &format) QX_USED; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QPixmap &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QPixmap &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QPIXMAP_H_ +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QPoint.h b/include/QxSerialize/QJson/QxSerializeQJson_QPoint.h new file mode 100644 index 0000000..f1a1a59 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QPoint.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QPOINT_H_ +#define _QX_SERIALIZE_QJSON_QPOINT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QPoint.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QPoint + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const QPoint &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QPoint &t, const QString &format) QX_USED; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QPoint &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QPoint &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QPOINT_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QRect.h b/include/QxSerialize/QJson/QxSerializeQJson_QRect.h new file mode 100644 index 0000000..6a55cf7 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QRect.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QRECT_H_ +#define _QX_SERIALIZE_QJSON_QRECT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QRect.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QRect + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const QRect &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QRect &t, const QString &format) QX_USED; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QRect &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QRect &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QRECT_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QRegExp.h b/include/QxSerialize/QJson/QxSerializeQJson_QRegExp.h new file mode 100644 index 0000000..ecc6c0d --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QRegExp.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QREGEXP_H_ +#define _QX_SERIALIZE_QJSON_QREGEXP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QRegExp.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QRegExp + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const QRegExp &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QRegExp &t, const QString &format) QX_USED; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QRegExp &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QRegExp &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QREGEXP_H_ +#endif // _QX_NO_JSON +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QRegion.h b/include/QxSerialize/QJson/QxSerializeQJson_QRegion.h new file mode 100644 index 0000000..8a1625b --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QRegion.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_GUI +#ifndef _QX_SERIALIZE_QJSON_QREGION_H_ +#define _QX_SERIALIZE_QJSON_QREGION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QRegion.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QRegion + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const QRegion &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QRegion &t, const QString &format) QX_USED; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QRegion &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QRegion &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QREGION_H_ +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QScopedPointer.h b/include/QxSerialize/QJson/QxSerializeQJson_QScopedPointer.h new file mode 100644 index 0000000..4ac9b86 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QScopedPointer.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) +#ifndef _QX_SERIALIZE_QJSON_QSCOPEDPOINTER_H_ +#define _QX_SERIALIZE_QJSON_QSCOPEDPOINTER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QScopedPointer.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QScopedPointer + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const QScopedPointer &t, const QString &format) + { + if (t) + { + return qx::cvt::to_json((*t), format); + } + return QJsonValue(); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, QScopedPointer &t, const QString &format) + { + if (j.isNull()) + { + t.reset(); + return qx_bool(true); + } + t.reset(new T()); + return qx::cvt::from_json(j, (*t), format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QSCOPEDPOINTER_H_ +#endif // (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QSharedPointer.h b/include/QxSerialize/QJson/QxSerializeQJson_QSharedPointer.h new file mode 100644 index 0000000..3fe9427 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QSharedPointer.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QSHAREDPOINTER_H_ +#define _QX_SERIALIZE_QJSON_QSHAREDPOINTER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QSharedPointer.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QSharedPointer + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const QSharedPointer &t, const QString &format) + { + if (t) + { + return qx::cvt::to_json((*t), format); + } + return QJsonValue(); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, QSharedPointer &t, const QString &format) + { + if (j.isNull()) + { + t.clear(); + return qx_bool(true); + } + t = QSharedPointer(new T()); + return qx::cvt::from_json(j, (*t), format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QSHAREDPOINTER_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QSize.h b/include/QxSerialize/QJson/QxSerializeQJson_QSize.h new file mode 100644 index 0000000..719bf99 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QSize.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QSIZE_H_ +#define _QX_SERIALIZE_QJSON_QSIZE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QSize.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QSize + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const QSize &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QSize &t, const QString &format) QX_USED; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QSize &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QSize &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QSIZE_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QSqlError.h b/include/QxSerialize/QJson/QxSerializeQJson_QSqlError.h new file mode 100644 index 0000000..3633247 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QSqlError.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QSQLERROR_H_ +#define _QX_SERIALIZE_QJSON_QSQLERROR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QSqlError.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QSqlError + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const QSqlError &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QSqlError &t, const QString &format) QX_USED; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QSqlError &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QSqlError &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QSQLERROR_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QStringList.h b/include/QxSerialize/QJson/QxSerializeQJson_QStringList.h new file mode 100644 index 0000000..d3852a2 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QStringList.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QSTRINGLIST_H_ +#define _QX_SERIALIZE_QJSON_QSTRINGLIST_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QStringList.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QStringList + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const QStringList &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QStringList &t, const QString &format) QX_USED; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QStringList &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QStringList &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QSTRINGLIST_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QUrl.h b/include/QxSerialize/QJson/QxSerializeQJson_QUrl.h new file mode 100644 index 0000000..6696642 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QUrl.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QURL_H_ +#define _QX_SERIALIZE_QJSON_QURL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QUrl.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QUrl + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QUrl &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(t.toString()); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QUrl &t, const QString &format) + { + Q_UNUSED(format); + t = QUrl(j.toString()); + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QURL_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QVariantHash.h b/include/QxSerialize/QJson/QxSerializeQJson_QVariantHash.h new file mode 100644 index 0000000..f17c11f --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QVariantHash.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) +#ifndef _QX_SERIALIZE_QJSON_QVARIANTHASH_H_ +#define _QX_SERIALIZE_QJSON_QVARIANTHASH_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QVariantHash.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QVariantHash + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QVariantHash &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(QJsonObject::fromVariantHash(t)); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QVariantHash &t, const QString &format) + { + Q_UNUSED(format); + t = j.toObject().toVariantHash(); + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QVARIANTHASH_H_ +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QVariantMap.h b/include/QxSerialize/QJson/QxSerializeQJson_QVariantMap.h new file mode 100644 index 0000000..932c262 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QVariantMap.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QVARIANTMAP_H_ +#define _QX_SERIALIZE_QJSON_QVARIANTMAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QVariantMap.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QVariantMap + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QVariantMap &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(QJsonObject::fromVariantMap(t)); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QVariantMap &t, const QString &format) + { + Q_UNUSED(format); + t = j.toObject().toVariantMap(); + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QVARIANTMAP_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QVector.h b/include/QxSerialize/QJson/QxSerializeQJson_QVector.h new file mode 100644 index 0000000..e24accb --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QVector.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QVECTOR_H_ +#define _QX_SERIALIZE_QJSON_QVECTOR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QVector.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QVector + */ + +#include +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const QVector &t, const QString &format) + { + QJsonArray arr; + for (int i = 0; i < t.count(); i++) + { + arr.append(qx::cvt::to_json(t.at(i), format)); + } + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, QVector &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + t.reserve(arr.count()); + for (int i = 0; i < arr.count(); i++) + { + T tmp; + qx::cvt::from_json(arr.at(i), tmp, format); + t.append(tmp); + } + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QVECTOR_H_ +#endif // _QX_NO_JSON +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QWeakPointer.h b/include/QxSerialize/QJson/QxSerializeQJson_QWeakPointer.h new file mode 100644 index 0000000..0606485 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QWeakPointer.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QWEAKPOINTER_H_ +#define _QX_SERIALIZE_QJSON_QWEAKPOINTER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QWeakPointer.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type QWeakPointer + */ + +#include +#include +#include +#include + +#include +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const QWeakPointer &t, const QString &format) + { + QSharedPointer ptr = t.toStrongRef(); + return qx::cvt::to_json(ptr, format); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, QWeakPointer &t, const QString &format) + { + QSharedPointer ptr; + qx::cvt::from_json(j, ptr, format); + t = QWeakPointer(ptr); + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QWEAKPOINTER_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QxCollection.h b/include/QxSerialize/QJson/QxSerializeQJson_QxCollection.h new file mode 100644 index 0000000..ecbd814 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QxCollection.h @@ -0,0 +1,274 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QX_COLLECTION_H_ +#define _QX_SERIALIZE_QJSON_QX_COLLECTION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QxCollection.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type qx::QxCollection + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const qx::QxCollection &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + + for (long l = 0; l < t.count(); l++) + { + QJsonObject obj; + val = qx::cvt::to_json(t.getKeyByIndex(l), format); + obj.insert("key", val); + val = qx::cvt::to_json(t.getByIndex(l), format); + obj.insert("value", val); + arr.append(obj); + } + + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, qx::QxCollection &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + QJsonValue val; + QJsonObject obj; + t.reserve(static_cast(arr.count())); + + for (int i = 0; i < arr.count(); i++) + { + val = arr.at(i); + if (!val.isObject()) + { + continue; + } + obj = val.toObject(); + Key key; + Value value; + qx::cvt::from_json(obj.value("key"), key, format); + qx::cvt::from_json(obj.value("value"), value, format); + t.insert(key, value); + } + + return qx_bool(true); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const qx::QxCollection &t, const QString &format) + { + QJsonObject obj; + QJsonValue val; + + for (long l = 0; l < t.count(); l++) + { + val = qx::cvt::to_json(t.getByIndex(l), format); + obj.insert(t.getKeyByIndex(l), val); + } + + return QJsonValue(obj); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, qx::QxCollection &t, const QString &format) + { + t.clear(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + QJsonValue val; + t.reserve(static_cast(obj.count())); + + for (QJsonObject::const_iterator itr = obj.constBegin(); itr != obj.constEnd(); ++itr) + { + QString key = itr.key(); + Value value; + qx::cvt::from_json(itr.value(), value, format); + t.insert(key, value); + } + + return qx_bool(true); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const qx::QxCollection &t, const QString &format) + { + QJsonObject obj; + QJsonValue val; + + for (long l = 0; l < t.count(); l++) + { +#ifndef QT_NO_STL + QString key = QString::fromStdString(t.getKeyByIndex(l)); +#else // QT_NO_STL + std::string s = t.getKeyByIndex(l); + QString key = QString::fromLatin1(s.data(), int(s.size())); +#endif // QT_NO_STL + + val = qx::cvt::to_json(t.getByIndex(l), format); + obj.insert(key, val); + } + + return QJsonValue(obj); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, qx::QxCollection &t, const QString &format) + { + t.clear(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + QJsonValue val; + t.reserve(static_cast(obj.count())); + + for (QJsonObject::const_iterator itr = obj.constBegin(); itr != obj.constEnd(); ++itr) + { + QString key = itr.key(); + Value value; + qx::cvt::from_json(itr.value(), value, format); + +#ifndef QT_NO_STL + std::string s = key.toStdString(); +#else // QT_NO_STL + std::string s = key.toLatin1().constData(); +#endif // QT_NO_STL + + t.insert(s, value); + } + + return qx_bool(true); + } + }; + +#if ((!defined(QT_NO_STL)) && (!defined(QT_NO_STL_WCHAR))) + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const qx::QxCollection &t, const QString &format) + { + QJsonObject obj; + QJsonValue val; + + for (long l = 0; l < t.count(); l++) + { + val = qx::cvt::to_json(t.getByIndex(l), format); + obj.insert(QString::fromStdWString(t.getKeyByIndex(l)), val); + } + + return QJsonValue(obj); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, qx::QxCollection &t, const QString &format) + { + t.clear(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + QJsonValue val; + t.reserve(static_cast(obj.count())); + + for (QJsonObject::const_iterator itr = obj.constBegin(); itr != obj.constEnd(); ++itr) + { + QString key = itr.key(); + Value value; + qx::cvt::from_json(itr.value(), value, format); + t.insert(key.toStdWString(), value); + } + + return qx_bool(true); + } + }; + +#endif // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QX_COLLECTION_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QxDaoPointer.h b/include/QxSerialize/QJson/QxSerializeQJson_QxDaoPointer.h new file mode 100644 index 0000000..dc7cf11 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QxDaoPointer.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QX_DAO_POINTER_H_ +#define _QX_SERIALIZE_QJSON_QX_DAO_POINTER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QxDaoPointer.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type qx::dao::ptr + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const qx::dao::ptr &t, const QString &format) + { + QJsonArray arr; + arr.append(qx::cvt::to_json(t.data(), format)); + arr.append(qx::cvt::to_json(t.dataOriginal(), format)); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, qx::dao::ptr &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + if (arr.count() != 2) + { + return qx_bool(true); + } + T *ptr = NULL; + T *original = NULL; + qx::cvt::from_json(arr.at(0), ptr, format); + qx::cvt::from_json(arr.at(1), original, format); + t = qx::dao::ptr(ptr, original); + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QX_DAO_POINTER_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QxInvalidValue.h b/include/QxSerialize/QJson/QxSerializeQJson_QxInvalidValue.h new file mode 100644 index 0000000..8474bc6 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QxInvalidValue.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QX_INVALID_VALUE_H_ +#define _QX_SERIALIZE_QJSON_QX_INVALID_VALUE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QxInvalidValue.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type qx::QxInvalidValue + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const qx::QxInvalidValue &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, qx::QxInvalidValue &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QX_INVALID_VALUE_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QxInvalidValueX.h b/include/QxSerialize/QJson/QxSerializeQJson_QxInvalidValueX.h new file mode 100644 index 0000000..e316831 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QxInvalidValueX.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QX_INVALID_VALUE_X_H_ +#define _QX_SERIALIZE_QJSON_QX_INVALID_VALUE_X_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QxInvalidValueX.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type qx::QxInvalidValueX + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const qx::QxInvalidValueX &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, qx::QxInvalidValueX &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QX_INVALID_VALUE_X_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QxSqlQuery.h b/include/QxSerialize/QJson/QxSerializeQJson_QxSqlQuery.h new file mode 100644 index 0000000..c25a2a8 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QxSqlQuery.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QX_SQL_QUERY_H_ +#define _QX_SERIALIZE_QJSON_QX_SQL_QUERY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QxSqlQuery.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type qx::QxSqlQuery + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const qx::QxSqlQuery &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, qx::QxSqlQuery &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QX_SQL_QUERY_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_QxTransaction.h b/include/QxSerialize/QJson/QxSerializeQJson_QxTransaction.h new file mode 100644 index 0000000..1747897 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_QxTransaction.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _QX_SERIALIZE_QJSON_QX_TRANSACTION_H_ +#define _QX_SERIALIZE_QJSON_QX_TRANSACTION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_QxTransaction.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type qx::service::QxTransaction + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const qx::service::QxTransaction &t, const QString &format) + { + return QxConvert_ToJson_Helper(t, format); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, qx::service::QxTransaction &t, const QString &format) + { + return QxConvert_FromJson_Helper(j, t, format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_QX_TRANSACTION_H_ +#endif // _QX_ENABLE_QT_NETWORK +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_all_include.h b/include/QxSerialize/QJson/QxSerializeQJson_all_include.h new file mode 100644 index 0000000..01b1c7f --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_all_include.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_ALL_INCLUDE_H_ +#define _QX_SERIALIZE_QJSON_ALL_INCLUDE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_all_include.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Include all Qt QJson serialization method (save/load) provided by QxOrm library + */ + +#ifdef _QX_ENABLE_BOOST +#include +#include +#include +#include +#include +#endif // _QX_ENABLE_BOOST + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // _QX_SERIALIZE_QJSON_ALL_INCLUDE_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_boost_scoped_ptr.h b/include/QxSerialize/QJson/QxSerializeQJson_boost_scoped_ptr.h new file mode 100644 index 0000000..a2bb833 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_boost_scoped_ptr.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_BOOST +#ifndef _QX_SERIALIZE_QJSON_BOOST_SCOPED_PTR_H_ +#define _QX_SERIALIZE_QJSON_BOOST_SCOPED_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_boost_scoped_ptr.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type boost::scoped_ptr + */ + +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::scoped_ptr &t, const QString &format) + { + if (t) + { + return qx::cvt::to_json((*t), format); + } + return QJsonValue(); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::scoped_ptr &t, const QString &format) + { + if (j.isNull()) + { + t.reset(); + return qx_bool(true); + } + t.reset(new T()); + return qx::cvt::from_json(j, (*t), format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_BOOST_SCOPED_PTR_H_ +#endif // _QX_ENABLE_BOOST +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_boost_shared_ptr.h b/include/QxSerialize/QJson/QxSerializeQJson_boost_shared_ptr.h new file mode 100644 index 0000000..4898101 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_boost_shared_ptr.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_BOOST +#ifndef _QX_SERIALIZE_QJSON_BOOST_SHARED_PTR_H_ +#define _QX_SERIALIZE_QJSON_BOOST_SHARED_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_boost_shared_ptr.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type boost::shared_ptr + */ + +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::shared_ptr &t, const QString &format) + { + if (t) + { + return qx::cvt::to_json((*t), format); + } + return QJsonValue(); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::shared_ptr &t, const QString &format) + { + if (j.isNull()) + { + t.reset(); + return qx_bool(true); + } + t.reset(new T()); + return qx::cvt::from_json(j, (*t), format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_BOOST_SHARED_PTR_H_ +#endif // _QX_ENABLE_BOOST +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_boost_tuple.h b/include/QxSerialize/QJson/QxSerializeQJson_boost_tuple.h new file mode 100644 index 0000000..c281fe4 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_boost_tuple.h @@ -0,0 +1,455 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_BOOST +#ifndef _QX_SERIALIZE_QJSON_BOOST_TUPLE_H_ +#define _QX_SERIALIZE_QJSON_BOOST_TUPLE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_boost_tuple.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type boost::tuple + */ + +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::tuple &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + val = qx::cvt::to_json(boost::get<0>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<1>(t), format); + arr.append(val); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::tuple &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + val = qx::cvt::to_json(boost::get<0>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<1>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<2>(t), format); + arr.append(val); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::tuple &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + val = qx::cvt::to_json(boost::get<0>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<1>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<2>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<3>(t), format); + arr.append(val); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::tuple &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + val = qx::cvt::to_json(boost::get<0>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<1>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<2>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<3>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<4>(t), format); + arr.append(val); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::tuple &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + val = qx::cvt::to_json(boost::get<0>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<1>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<2>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<3>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<4>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<5>(t), format); + arr.append(val); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::tuple &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + val = qx::cvt::to_json(boost::get<0>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<1>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<2>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<3>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<4>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<5>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<6>(t), format); + arr.append(val); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::tuple &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + val = qx::cvt::to_json(boost::get<0>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<1>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<2>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<3>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<4>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<5>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<6>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<7>(t), format); + arr.append(val); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::tuple &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + val = qx::cvt::to_json(boost::get<0>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<1>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<2>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<3>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<4>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<5>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<6>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<7>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<8>(t), format); + arr.append(val); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::tuple &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + val = qx::cvt::to_json(boost::get<0>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<1>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<2>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<3>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<4>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<5>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<6>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<7>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<8>(t), format); + arr.append(val); + val = qx::cvt::to_json(boost::get<9>(t), format); + arr.append(val); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::tuple &t, const QString &format) + { + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + qx::cvt::from_json(arr.at(0), boost::get<0>(t), format); + qx::cvt::from_json(arr.at(1), boost::get<1>(t), format); + return qx_bool(true); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::tuple &t, const QString &format) + { + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + qx::cvt::from_json(arr.at(0), boost::get<0>(t), format); + qx::cvt::from_json(arr.at(1), boost::get<1>(t), format); + qx::cvt::from_json(arr.at(2), boost::get<2>(t), format); + return qx_bool(true); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::tuple &t, const QString &format) + { + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + qx::cvt::from_json(arr.at(0), boost::get<0>(t), format); + qx::cvt::from_json(arr.at(1), boost::get<1>(t), format); + qx::cvt::from_json(arr.at(2), boost::get<2>(t), format); + qx::cvt::from_json(arr.at(3), boost::get<3>(t), format); + return qx_bool(true); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::tuple &t, const QString &format) + { + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + qx::cvt::from_json(arr.at(0), boost::get<0>(t), format); + qx::cvt::from_json(arr.at(1), boost::get<1>(t), format); + qx::cvt::from_json(arr.at(2), boost::get<2>(t), format); + qx::cvt::from_json(arr.at(3), boost::get<3>(t), format); + qx::cvt::from_json(arr.at(4), boost::get<4>(t), format); + return qx_bool(true); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::tuple &t, const QString &format) + { + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + qx::cvt::from_json(arr.at(0), boost::get<0>(t), format); + qx::cvt::from_json(arr.at(1), boost::get<1>(t), format); + qx::cvt::from_json(arr.at(2), boost::get<2>(t), format); + qx::cvt::from_json(arr.at(3), boost::get<3>(t), format); + qx::cvt::from_json(arr.at(4), boost::get<4>(t), format); + qx::cvt::from_json(arr.at(5), boost::get<5>(t), format); + return qx_bool(true); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::tuple &t, const QString &format) + { + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + qx::cvt::from_json(arr.at(0), boost::get<0>(t), format); + qx::cvt::from_json(arr.at(1), boost::get<1>(t), format); + qx::cvt::from_json(arr.at(2), boost::get<2>(t), format); + qx::cvt::from_json(arr.at(3), boost::get<3>(t), format); + qx::cvt::from_json(arr.at(4), boost::get<4>(t), format); + qx::cvt::from_json(arr.at(5), boost::get<5>(t), format); + qx::cvt::from_json(arr.at(6), boost::get<6>(t), format); + return qx_bool(true); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::tuple &t, const QString &format) + { + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + qx::cvt::from_json(arr.at(0), boost::get<0>(t), format); + qx::cvt::from_json(arr.at(1), boost::get<1>(t), format); + qx::cvt::from_json(arr.at(2), boost::get<2>(t), format); + qx::cvt::from_json(arr.at(3), boost::get<3>(t), format); + qx::cvt::from_json(arr.at(4), boost::get<4>(t), format); + qx::cvt::from_json(arr.at(5), boost::get<5>(t), format); + qx::cvt::from_json(arr.at(6), boost::get<6>(t), format); + qx::cvt::from_json(arr.at(7), boost::get<7>(t), format); + return qx_bool(true); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::tuple &t, const QString &format) + { + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + qx::cvt::from_json(arr.at(0), boost::get<0>(t), format); + qx::cvt::from_json(arr.at(1), boost::get<1>(t), format); + qx::cvt::from_json(arr.at(2), boost::get<2>(t), format); + qx::cvt::from_json(arr.at(3), boost::get<3>(t), format); + qx::cvt::from_json(arr.at(4), boost::get<4>(t), format); + qx::cvt::from_json(arr.at(5), boost::get<5>(t), format); + qx::cvt::from_json(arr.at(6), boost::get<6>(t), format); + qx::cvt::from_json(arr.at(7), boost::get<7>(t), format); + qx::cvt::from_json(arr.at(8), boost::get<8>(t), format); + return qx_bool(true); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::tuple &t, const QString &format) + { + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + qx::cvt::from_json(arr.at(0), boost::get<0>(t), format); + qx::cvt::from_json(arr.at(1), boost::get<1>(t), format); + qx::cvt::from_json(arr.at(2), boost::get<2>(t), format); + qx::cvt::from_json(arr.at(3), boost::get<3>(t), format); + qx::cvt::from_json(arr.at(4), boost::get<4>(t), format); + qx::cvt::from_json(arr.at(5), boost::get<5>(t), format); + qx::cvt::from_json(arr.at(6), boost::get<6>(t), format); + qx::cvt::from_json(arr.at(7), boost::get<7>(t), format); + qx::cvt::from_json(arr.at(8), boost::get<8>(t), format); + qx::cvt::from_json(arr.at(9), boost::get<9>(t), format); + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_BOOST_TUPLE_H_ +#endif // _QX_ENABLE_BOOST +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_boost_unordered_map.h b/include/QxSerialize/QJson/QxSerializeQJson_boost_unordered_map.h new file mode 100644 index 0000000..8178b85 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_boost_unordered_map.h @@ -0,0 +1,334 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_BOOST +#ifndef _QX_SERIALIZE_QJSON_BOOST_UNORDERED_MAP_H_ +#define _QX_SERIALIZE_QJSON_BOOST_UNORDERED_MAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_boost_unordered_map.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type boost::unordered_map and boost::unordered_multimap + */ + +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::unordered_map &t, const QString &format) + { + typedef typename boost::unordered_map::const_iterator type_itr; + QJsonArray arr; + QJsonValue val; + + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + QJsonObject obj; + val = qx::cvt::to_json(itr->first, format); + obj.insert("key", val); + val = qx::cvt::to_json(itr->second, format); + obj.insert("value", val); + arr.append(obj); + } + + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::unordered_map &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + QJsonValue val; + QJsonObject obj; + t.reserve(static_cast::size_type>(arr.count())); + + for (int i = 0; i < arr.count(); i++) + { + val = arr.at(i); + if (!val.isObject()) + { + continue; + } + obj = val.toObject(); + Key key; + Value value; + qx::cvt::from_json(obj.value("key"), key, format); + qx::cvt::from_json(obj.value("value"), value, format); + t.insert(std::make_pair(key, value)); + } + + return qx_bool(true); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::unordered_multimap &t, const QString &format) + { + typedef typename boost::unordered_multimap::const_iterator type_itr; + QJsonArray arr; + QJsonValue val; + + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + QJsonObject obj; + val = qx::cvt::to_json(itr->first, format); + obj.insert("key", val); + val = qx::cvt::to_json(itr->second, format); + obj.insert("value", val); + arr.append(obj); + } + + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::unordered_multimap &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + QJsonValue val; + QJsonObject obj; + t.reserve(static_cast::size_type>(arr.count())); + + for (int i = 0; i < arr.count(); i++) + { + val = arr.at(i); + if (!val.isObject()) + { + continue; + } + obj = val.toObject(); + Key key; + Value value; + qx::cvt::from_json(obj.value("key"), key, format); + qx::cvt::from_json(obj.value("value"), value, format); + t.insert(std::make_pair(key, value)); + } + + return qx_bool(true); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::unordered_map &t, const QString &format) + { + typedef typename boost::unordered_map::const_iterator type_itr; + QJsonObject obj; + QJsonValue val; + + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + val = qx::cvt::to_json(itr->second, format); + obj.insert(itr->first, val); + } + + return QJsonValue(obj); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::unordered_map &t, const QString &format) + { + t.clear(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + QJsonValue val; + t.reserve(static_cast::size_type>(obj.count())); + + for (QJsonObject::const_iterator itr = obj.constBegin(); itr != obj.constEnd(); ++itr) + { + QString key = itr.key(); + Value value; + qx::cvt::from_json(itr.value(), value, format); + t.insert(std::make_pair(key, value)); + } + + return qx_bool(true); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::unordered_map &t, const QString &format) + { + typedef typename boost::unordered_map::const_iterator type_itr; + QJsonObject obj; + QJsonValue val; + + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { +#ifndef QT_NO_STL + QString key = QString::fromStdString(itr->first); +#else // QT_NO_STL + QString key = QString::fromLatin1(itr->first.data(), int(itr->first.size())); +#endif // QT_NO_STL + + val = qx::cvt::to_json(itr->second, format); + obj.insert(key, val); + } + + return QJsonValue(obj); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::unordered_map &t, const QString &format) + { + t.clear(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + QJsonValue val; + t.reserve(static_cast::size_type>(obj.count())); + + for (QJsonObject::const_iterator itr = obj.constBegin(); itr != obj.constEnd(); ++itr) + { + QString key = itr.key(); + Value value; + qx::cvt::from_json(itr.value(), value, format); + +#ifndef QT_NO_STL + std::string s = key.toStdString(); +#else // QT_NO_STL + std::string s = key.toLatin1().constData(); +#endif // QT_NO_STL + + t.insert(std::make_pair(s, value)); + } + + return qx_bool(true); + } + }; + +#if ((!defined(QT_NO_STL)) && (!defined(QT_NO_STL_WCHAR))) + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::unordered_map &t, const QString &format) + { + typedef typename boost::unordered_map::const_iterator type_itr; + QJsonObject obj; + QJsonValue val; + + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + val = qx::cvt::to_json(itr->second, format); + obj.insert(QString::fromStdWString(itr->first), val); + } + + return QJsonValue(obj); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::unordered_map &t, const QString &format) + { + t.clear(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + QJsonValue val; + t.reserve(static_cast::size_type>(obj.count())); + + for (QJsonObject::const_iterator itr = obj.constBegin(); itr != obj.constEnd(); ++itr) + { + QString key = itr.key(); + Value value; + qx::cvt::from_json(itr.value(), value, format); + t.insert(std::make_pair(key.toStdWString(), value)); + } + + return qx_bool(true); + } + }; + +#endif // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_BOOST_UNORDERED_MAP_H_ +#endif // _QX_ENABLE_BOOST +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_boost_unordered_set.h b/include/QxSerialize/QJson/QxSerializeQJson_boost_unordered_set.h new file mode 100644 index 0000000..58272d8 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_boost_unordered_set.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_BOOST +#ifndef _QX_SERIALIZE_QJSON_BOOST_UNORDERED_SET_H_ +#define _QX_SERIALIZE_QJSON_BOOST_UNORDERED_SET_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_boost_unordered_set.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type boost::unordered_set and boost::unordered_multiset + */ + +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::unordered_set &t, const QString &format) + { + QJsonArray arr; + typedef typename boost::unordered_set::const_iterator type_itr; + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + arr.append(qx::cvt::to_json((*itr), format)); + } + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::unordered_set &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + t.reserve(static_cast::size_type>(arr.count())); + for (int i = 0; i < arr.count(); i++) + { + T tmp; + qx::cvt::from_json(arr.at(i), tmp, format); + t.insert(tmp); + } + return qx_bool(true); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::unordered_multiset &t, const QString &format) + { + QJsonArray arr; + typedef typename boost::unordered_multiset::const_iterator type_itr; + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + arr.append(qx::cvt::to_json((*itr), format)); + } + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::unordered_multiset &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + t.reserve(static_cast::size_type>(arr.count())); + for (int i = 0; i < arr.count(); i++) + { + T tmp; + qx::cvt::from_json(arr.at(i), tmp, format); + t.insert(tmp); + } + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_BOOST_UNORDERED_SET_H_ +#endif // _QX_ENABLE_BOOST +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_qx_registered_class.h b/include/QxSerialize/QJson/QxSerializeQJson_qx_registered_class.h new file mode 100644 index 0000000..466fb91 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_qx_registered_class.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_QX_REGISTERED_CLASS_H_ +#define _QX_SERIALIZE_QJSON_QX_REGISTERED_CLASS_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_qx_registered_class.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a generic Qt QJson serialization method (save/load) for classes registered into QxOrm context (void qx::register_class() function), it is possible to specialize qx::cvt::detail::QxSerializeJsonRegistered template to implement your own serialization method for a specific class + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + struct QX_DLL_EXPORT QxSerializeJsonRegistered_Helper + { + + static QJsonValue save(IxClass *pClass, const void *pOwner, const QString &format); + static qx_bool load(const QJsonValue &j, IxClass *pClass, void *pOwner, const QString &format); + }; + + template + struct QxSerializeJsonRegistered + { + + enum + { + is_valid = qx::trait::is_qx_registered::value + }; + + static QJsonValue save(const T &t, const QString &format) + { + static_assert(is_valid, "is_valid"); + return qx::cvt::detail::QxSerializeJsonRegistered_Helper::save(qx::QxClass::getSingleton(), (&t), format); + } + + static qx_bool load(const QJsonValue &j, T &t, const QString &format) + { + static_assert(is_valid, "is_valid"); + return qx::cvt::detail::QxSerializeJsonRegistered_Helper::load(j, qx::QxClass::getSingleton(), (&t), format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#define QX_JSON_SERIALIZE_ONLY_ID "QX_JSON_SERIALIZE_ONLY_ID" + +#endif // _QX_SERIALIZE_QJSON_QX_REGISTERED_CLASS_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_std_list.h b/include/QxSerialize/QJson/QxSerializeQJson_std_list.h new file mode 100644 index 0000000..9bbd32a --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_std_list.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_STD_LIST_H_ +#define _QX_SERIALIZE_QJSON_STD_LIST_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_std_list.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type std::list + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::list &t, const QString &format) + { + QJsonArray arr; + typedef typename std::list::const_iterator type_itr; + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + arr.append(qx::cvt::to_json((*itr), format)); + } + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::list &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + for (int i = 0; i < arr.count(); i++) + { + T tmp; + qx::cvt::from_json(arr.at(i), tmp, format); + t.push_back(tmp); + } + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_STD_LIST_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_std_map.h b/include/QxSerialize/QJson/QxSerializeQJson_std_map.h new file mode 100644 index 0000000..79e424c --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_std_map.h @@ -0,0 +1,274 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_STD_MAP_H_ +#define _QX_SERIALIZE_QJSON_STD_MAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_std_map.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type std::map + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::map &t, const QString &format) + { + typedef typename std::map::const_iterator type_itr; + QJsonArray arr; + QJsonValue val; + + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + QJsonObject obj; + val = qx::cvt::to_json(itr->first, format); + obj.insert("key", val); + val = qx::cvt::to_json(itr->second, format); + obj.insert("value", val); + arr.append(obj); + } + + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::map &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + QJsonValue val; + QJsonObject obj; + + for (int i = 0; i < arr.count(); i++) + { + val = arr.at(i); + if (!val.isObject()) + { + continue; + } + obj = val.toObject(); + Key key; + Value value; + qx::cvt::from_json(obj.value("key"), key, format); + qx::cvt::from_json(obj.value("value"), value, format); + t.insert(std::make_pair(key, value)); + } + + return qx_bool(true); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::map &t, const QString &format) + { + typedef typename std::map::const_iterator type_itr; + QJsonObject obj; + QJsonValue val; + + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + val = qx::cvt::to_json(itr->second, format); + obj.insert(itr->first, val); + } + + return QJsonValue(obj); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::map &t, const QString &format) + { + t.clear(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + QJsonValue val; + + for (QJsonObject::const_iterator itr = obj.constBegin(); itr != obj.constEnd(); ++itr) + { + QString key = itr.key(); + Value value; + qx::cvt::from_json(itr.value(), value, format); + t.insert(std::make_pair(key, value)); + } + + return qx_bool(true); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::map &t, const QString &format) + { + typedef typename std::map::const_iterator type_itr; + QJsonObject obj; + QJsonValue val; + + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { +#ifndef QT_NO_STL + QString key = QString::fromStdString(itr->first); +#else // QT_NO_STL + std::string s = itr->first; + QString key = QString::fromLatin1(s.data(), int(s.size())); +#endif // QT_NO_STL + + val = qx::cvt::to_json(itr->second, format); + obj.insert(key, val); + } + + return QJsonValue(obj); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::map &t, const QString &format) + { + t.clear(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + QJsonValue val; + + for (QJsonObject::const_iterator itr = obj.constBegin(); itr != obj.constEnd(); ++itr) + { + QString key = itr.key(); + Value value; + qx::cvt::from_json(itr.value(), value, format); + +#ifndef QT_NO_STL + std::string s = key.toStdString(); +#else // QT_NO_STL + std::string s = key.toLatin1().constData(); +#endif // QT_NO_STL + + t.insert(std::make_pair(s, value)); + } + + return qx_bool(true); + } + }; + +#if ((!defined(QT_NO_STL)) && (!defined(QT_NO_STL_WCHAR))) + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::map &t, const QString &format) + { + typedef typename std::map::const_iterator type_itr; + QJsonObject obj; + QJsonValue val; + + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + val = qx::cvt::to_json(itr->second, format); + obj.insert(QString::fromStdWString(itr->first), val); + } + + return QJsonValue(obj); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::map &t, const QString &format) + { + t.clear(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + QJsonValue val; + + for (QJsonObject::const_iterator itr = obj.constBegin(); itr != obj.constEnd(); ++itr) + { + QString key = itr.key(); + Value value; + qx::cvt::from_json(itr.value(), value, format); + t.insert(std::make_pair(key.toStdWString(), value)); + } + + return qx_bool(true); + } + }; + +#endif // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_STD_MAP_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_std_pair.h b/include/QxSerialize/QJson/QxSerializeQJson_std_pair.h new file mode 100644 index 0000000..f9590eb --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_std_pair.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_STD_PAIR_H_ +#define _QX_SERIALIZE_QJSON_STD_PAIR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_std_pair.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type std::pair + */ + +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::pair &t, const QString &format) + { + QJsonArray arr; + arr.append(qx::cvt::to_json(t.first, format)); + arr.append(qx::cvt::to_json(t.second, format)); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::pair &t, const QString &format) + { + t = std::pair(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + if (arr.count() != 2) + { + return qx_bool(true); + } + T1 tmp1; + qx::cvt::from_json(arr.at(0), tmp1, format); + T2 tmp2; + qx::cvt::from_json(arr.at(1), tmp2, format); + t = std::make_pair(tmp1, tmp2); + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_STD_PAIR_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_std_set.h b/include/QxSerialize/QJson/QxSerializeQJson_std_set.h new file mode 100644 index 0000000..9594fe5 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_std_set.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_STD_SET_H_ +#define _QX_SERIALIZE_QJSON_STD_SET_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_std_set.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type std::set + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::set &t, const QString &format) + { + QJsonArray arr; + typedef typename std::set::const_iterator type_itr; + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + arr.append(qx::cvt::to_json((*itr), format)); + } + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::set &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + for (int i = 0; i < arr.count(); i++) + { + T tmp; + qx::cvt::from_json(arr.at(i), tmp, format); + t.insert(tmp); + } + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_STD_SET_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_std_shared_ptr.h b/include/QxSerialize/QJson/QxSerializeQJson_std_shared_ptr.h new file mode 100644 index 0000000..7531780 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_std_shared_ptr.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_STD_SHARED_PTR_H_ +#define _QX_SERIALIZE_QJSON_STD_SHARED_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_std_shared_ptr.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type std::shared_ptr (C++11 compilation option _QX_CPP_11_SMART_PTR must be defined) + */ + +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::shared_ptr &t, const QString &format) + { + if (t) + { + return qx::cvt::to_json((*t), format); + } + return QJsonValue(); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::shared_ptr &t, const QString &format) + { + if (j.isNull()) + { + t.reset(); + return qx_bool(true); + } + t = std::make_shared(); + return qx::cvt::from_json(j, (*t), format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_STD_SHARED_PTR_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_std_tuple.h b/include/QxSerialize/QJson/QxSerializeQJson_std_tuple.h new file mode 100644 index 0000000..2b47b08 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_std_tuple.h @@ -0,0 +1,453 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_STD_TUPLE_H_ +#define _QX_SERIALIZE_QJSON_STD_TUPLE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_std_tuple.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type std::tuple (C++11 compilation option _QX_CPP_11_TUPLE must be defined) + */ + +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::tuple &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + val = qx::cvt::to_json(std::get<0>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<1>(t), format); + arr.append(val); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::tuple &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + val = qx::cvt::to_json(std::get<0>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<1>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<2>(t), format); + arr.append(val); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::tuple &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + val = qx::cvt::to_json(std::get<0>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<1>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<2>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<3>(t), format); + arr.append(val); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::tuple &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + val = qx::cvt::to_json(std::get<0>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<1>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<2>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<3>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<4>(t), format); + arr.append(val); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::tuple &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + val = qx::cvt::to_json(std::get<0>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<1>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<2>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<3>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<4>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<5>(t), format); + arr.append(val); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::tuple &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + val = qx::cvt::to_json(std::get<0>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<1>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<2>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<3>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<4>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<5>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<6>(t), format); + arr.append(val); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::tuple &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + val = qx::cvt::to_json(std::get<0>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<1>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<2>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<3>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<4>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<5>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<6>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<7>(t), format); + arr.append(val); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::tuple &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + val = qx::cvt::to_json(std::get<0>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<1>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<2>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<3>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<4>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<5>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<6>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<7>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<8>(t), format); + arr.append(val); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::tuple &t, const QString &format) + { + QJsonArray arr; + QJsonValue val; + val = qx::cvt::to_json(std::get<0>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<1>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<2>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<3>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<4>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<5>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<6>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<7>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<8>(t), format); + arr.append(val); + val = qx::cvt::to_json(std::get<9>(t), format); + arr.append(val); + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::tuple &t, const QString &format) + { + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + qx::cvt::from_json(arr.at(0), std::get<0>(t), format); + qx::cvt::from_json(arr.at(1), std::get<1>(t), format); + return qx_bool(true); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::tuple &t, const QString &format) + { + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + qx::cvt::from_json(arr.at(0), std::get<0>(t), format); + qx::cvt::from_json(arr.at(1), std::get<1>(t), format); + qx::cvt::from_json(arr.at(2), std::get<2>(t), format); + return qx_bool(true); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::tuple &t, const QString &format) + { + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + qx::cvt::from_json(arr.at(0), std::get<0>(t), format); + qx::cvt::from_json(arr.at(1), std::get<1>(t), format); + qx::cvt::from_json(arr.at(2), std::get<2>(t), format); + qx::cvt::from_json(arr.at(3), std::get<3>(t), format); + return qx_bool(true); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::tuple &t, const QString &format) + { + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + qx::cvt::from_json(arr.at(0), std::get<0>(t), format); + qx::cvt::from_json(arr.at(1), std::get<1>(t), format); + qx::cvt::from_json(arr.at(2), std::get<2>(t), format); + qx::cvt::from_json(arr.at(3), std::get<3>(t), format); + qx::cvt::from_json(arr.at(4), std::get<4>(t), format); + return qx_bool(true); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::tuple &t, const QString &format) + { + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + qx::cvt::from_json(arr.at(0), std::get<0>(t), format); + qx::cvt::from_json(arr.at(1), std::get<1>(t), format); + qx::cvt::from_json(arr.at(2), std::get<2>(t), format); + qx::cvt::from_json(arr.at(3), std::get<3>(t), format); + qx::cvt::from_json(arr.at(4), std::get<4>(t), format); + qx::cvt::from_json(arr.at(5), std::get<5>(t), format); + return qx_bool(true); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::tuple &t, const QString &format) + { + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + qx::cvt::from_json(arr.at(0), std::get<0>(t), format); + qx::cvt::from_json(arr.at(1), std::get<1>(t), format); + qx::cvt::from_json(arr.at(2), std::get<2>(t), format); + qx::cvt::from_json(arr.at(3), std::get<3>(t), format); + qx::cvt::from_json(arr.at(4), std::get<4>(t), format); + qx::cvt::from_json(arr.at(5), std::get<5>(t), format); + qx::cvt::from_json(arr.at(6), std::get<6>(t), format); + return qx_bool(true); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::tuple &t, const QString &format) + { + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + qx::cvt::from_json(arr.at(0), std::get<0>(t), format); + qx::cvt::from_json(arr.at(1), std::get<1>(t), format); + qx::cvt::from_json(arr.at(2), std::get<2>(t), format); + qx::cvt::from_json(arr.at(3), std::get<3>(t), format); + qx::cvt::from_json(arr.at(4), std::get<4>(t), format); + qx::cvt::from_json(arr.at(5), std::get<5>(t), format); + qx::cvt::from_json(arr.at(6), std::get<6>(t), format); + qx::cvt::from_json(arr.at(7), std::get<7>(t), format); + return qx_bool(true); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::tuple &t, const QString &format) + { + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + qx::cvt::from_json(arr.at(0), std::get<0>(t), format); + qx::cvt::from_json(arr.at(1), std::get<1>(t), format); + qx::cvt::from_json(arr.at(2), std::get<2>(t), format); + qx::cvt::from_json(arr.at(3), std::get<3>(t), format); + qx::cvt::from_json(arr.at(4), std::get<4>(t), format); + qx::cvt::from_json(arr.at(5), std::get<5>(t), format); + qx::cvt::from_json(arr.at(6), std::get<6>(t), format); + qx::cvt::from_json(arr.at(7), std::get<7>(t), format); + qx::cvt::from_json(arr.at(8), std::get<8>(t), format); + return qx_bool(true); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::tuple &t, const QString &format) + { + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + qx::cvt::from_json(arr.at(0), std::get<0>(t), format); + qx::cvt::from_json(arr.at(1), std::get<1>(t), format); + qx::cvt::from_json(arr.at(2), std::get<2>(t), format); + qx::cvt::from_json(arr.at(3), std::get<3>(t), format); + qx::cvt::from_json(arr.at(4), std::get<4>(t), format); + qx::cvt::from_json(arr.at(5), std::get<5>(t), format); + qx::cvt::from_json(arr.at(6), std::get<6>(t), format); + qx::cvt::from_json(arr.at(7), std::get<7>(t), format); + qx::cvt::from_json(arr.at(8), std::get<8>(t), format); + qx::cvt::from_json(arr.at(9), std::get<9>(t), format); + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_STD_TUPLE_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_std_unique_ptr.h b/include/QxSerialize/QJson/QxSerializeQJson_std_unique_ptr.h new file mode 100644 index 0000000..611106b --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_std_unique_ptr.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_STD_UNIQUE_PTR_H_ +#define _QX_SERIALIZE_QJSON_STD_UNIQUE_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_std_unique_ptr.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type std::unique_ptr (C++11 compilation option _QX_CPP_11_SMART_PTR must be defined) + */ + +#include +#include +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::unique_ptr &t, const QString &format) + { + if (t) + { + return qx::cvt::to_json((*t), format); + } + return QJsonValue(); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::unique_ptr &t, const QString &format) + { + if (j.isNull()) + { + t.reset(); + return qx_bool(true); + } + t.reset(new T()); + return qx::cvt::from_json(j, (*t), format); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_STD_UNIQUE_PTR_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_std_unordered_map.h b/include/QxSerialize/QJson/QxSerializeQJson_std_unordered_map.h new file mode 100644 index 0000000..6a3b2db --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_std_unordered_map.h @@ -0,0 +1,334 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_STD_UNORDERED_MAP_H_ +#define _QX_SERIALIZE_QJSON_STD_UNORDERED_MAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_std_unordered_map.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type std::unordered_map and std::unordered_multimap (C++11 compilation option _QX_CPP_11_CONTAINER must be defined) + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::unordered_map &t, const QString &format) + { + typedef typename std::unordered_map::const_iterator type_itr; + QJsonArray arr; + QJsonValue val; + + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + QJsonObject obj; + val = qx::cvt::to_json(itr->first, format); + obj.insert("key", val); + val = qx::cvt::to_json(itr->second, format); + obj.insert("value", val); + arr.append(obj); + } + + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::unordered_map &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + QJsonValue val; + QJsonObject obj; + t.reserve(static_cast::size_type>(arr.count())); + + for (int i = 0; i < arr.count(); i++) + { + val = arr.at(i); + if (!val.isObject()) + { + continue; + } + obj = val.toObject(); + Key key; + Value value; + qx::cvt::from_json(obj.value("key"), key, format); + qx::cvt::from_json(obj.value("value"), value, format); + t.insert(std::make_pair(key, value)); + } + + return qx_bool(true); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::unordered_multimap &t, const QString &format) + { + typedef typename std::unordered_multimap::const_iterator type_itr; + QJsonArray arr; + QJsonValue val; + + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + QJsonObject obj; + val = qx::cvt::to_json(itr->first, format); + obj.insert("key", val); + val = qx::cvt::to_json(itr->second, format); + obj.insert("value", val); + arr.append(obj); + } + + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::unordered_multimap &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + QJsonValue val; + QJsonObject obj; + t.reserve(static_cast::size_type>(arr.count())); + + for (int i = 0; i < arr.count(); i++) + { + val = arr.at(i); + if (!val.isObject()) + { + continue; + } + obj = val.toObject(); + Key key; + Value value; + qx::cvt::from_json(obj.value("key"), key, format); + qx::cvt::from_json(obj.value("value"), value, format); + t.insert(std::make_pair(key, value)); + } + + return qx_bool(true); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::unordered_map &t, const QString &format) + { + typedef typename std::unordered_map::const_iterator type_itr; + QJsonObject obj; + QJsonValue val; + + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + val = qx::cvt::to_json(itr->second, format); + obj.insert(itr->first, val); + } + + return QJsonValue(obj); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::unordered_map &t, const QString &format) + { + t.clear(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + QJsonValue val; + t.reserve(static_cast::size_type>(obj.count())); + + for (QJsonObject::const_iterator itr = obj.constBegin(); itr != obj.constEnd(); ++itr) + { + QString key = itr.key(); + Value value; + qx::cvt::from_json(itr.value(), value, format); + t.insert(std::make_pair(key, value)); + } + + return qx_bool(true); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::unordered_map &t, const QString &format) + { + typedef typename std::unordered_map::const_iterator type_itr; + QJsonObject obj; + QJsonValue val; + + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { +#ifndef QT_NO_STL + QString key = QString::fromStdString(itr->first); +#else // QT_NO_STL + QString key = QString::fromLatin1(itr->first.data(), int(itr->first.size())); +#endif // QT_NO_STL + + val = qx::cvt::to_json(itr->second, format); + obj.insert(key, val); + } + + return QJsonValue(obj); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::unordered_map &t, const QString &format) + { + t.clear(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + QJsonValue val; + t.reserve(static_cast::size_type>(obj.count())); + + for (QJsonObject::const_iterator itr = obj.constBegin(); itr != obj.constEnd(); ++itr) + { + QString key = itr.key(); + Value value; + qx::cvt::from_json(itr.value(), value, format); + +#ifndef QT_NO_STL + std::string s = key.toStdString(); +#else // QT_NO_STL + std::string s = key.toLatin1().constData(); +#endif // QT_NO_STL + + t.insert(std::make_pair(s, value)); + } + + return qx_bool(true); + } + }; + +#if ((!defined(QT_NO_STL)) && (!defined(QT_NO_STL_WCHAR))) + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::unordered_map &t, const QString &format) + { + typedef typename std::unordered_map::const_iterator type_itr; + QJsonObject obj; + QJsonValue val; + + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + val = qx::cvt::to_json(itr->second, format); + obj.insert(QString::fromStdWString(itr->first), val); + } + + return QJsonValue(obj); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::unordered_map &t, const QString &format) + { + t.clear(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + QJsonValue val; + t.reserve(static_cast::size_type>(obj.count())); + + for (QJsonObject::const_iterator itr = obj.constBegin(); itr != obj.constEnd(); ++itr) + { + QString key = itr.key(); + Value value; + qx::cvt::from_json(itr.value(), value, format); + t.insert(std::make_pair(key.toStdWString(), value)); + } + + return qx_bool(true); + } + }; + +#endif // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_STD_UNORDERED_MAP_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_std_unordered_set.h b/include/QxSerialize/QJson/QxSerializeQJson_std_unordered_set.h new file mode 100644 index 0000000..9fab6c9 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_std_unordered_set.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_STD_UNORDERED_SET_H_ +#define _QX_SERIALIZE_QJSON_STD_UNORDERED_SET_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_std_unordered_set.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type std::unordered_set and std::unordered_multiset (C++11 compilation option _QX_CPP_11_CONTAINER must be defined) + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::unordered_set &t, const QString &format) + { + QJsonArray arr; + typedef typename std::unordered_set::const_iterator type_itr; + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + arr.append(qx::cvt::to_json((*itr), format)); + } + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::unordered_set &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + t.reserve(static_cast::size_type>(arr.count())); + for (int i = 0; i < arr.count(); i++) + { + T tmp; + qx::cvt::from_json(arr.at(i), tmp, format); + t.insert(tmp); + } + return qx_bool(true); + } + }; + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::unordered_multiset &t, const QString &format) + { + QJsonArray arr; + typedef typename std::unordered_multiset::const_iterator type_itr; + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + arr.append(qx::cvt::to_json((*itr), format)); + } + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::unordered_multiset &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + t.reserve(static_cast::size_type>(arr.count())); + for (int i = 0; i < arr.count(); i++) + { + T tmp; + qx::cvt::from_json(arr.at(i), tmp, format); + t.insert(tmp); + } + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_STD_UNORDERED_SET_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/QJson/QxSerializeQJson_std_vector.h b/include/QxSerialize/QJson/QxSerializeQJson_std_vector.h new file mode 100644 index 0000000..1ddb456 --- /dev/null +++ b/include/QxSerialize/QJson/QxSerializeQJson_std_vector.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_STD_VECTOR_H_ +#define _QX_SERIALIZE_QJSON_STD_VECTOR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson_std_vector.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a Qt QJson serialization method (save/load) for type std::vector + */ + +#include +#include +#include + +#include + +#include +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const std::vector &t, const QString &format) + { + QJsonArray arr; + typedef typename std::vector::const_iterator type_itr; + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + arr.append(qx::cvt::to_json((*itr), format)); + } + return QJsonValue(arr); + } + }; + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, std::vector &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + t.reserve(static_cast::size_type>(arr.count())); + for (int i = 0; i < arr.count(); i++) + { + T tmp; + qx::cvt::from_json(arr.at(i), tmp, format); + t.push_back(tmp); + } + return qx_bool(true); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_STD_VECTOR_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/Qt/QxSerialize_QBrush.h b/include/QxSerialize/Qt/QxSerialize_QBrush.h new file mode 100644 index 0000000..64711da --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QBrush.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifdef _QX_ENABLE_QT_GUI +#ifndef _QX_SERIALIZE_QBRUSH_H_ +#define _QX_SERIALIZE_QBRUSH_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +QX_CLASS_VERSION(QBrush, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QBrush) + +#endif // _QX_SERIALIZE_QBRUSH_H_ +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QByteArray.h b/include/QxSerialize/Qt/QxSerialize_QByteArray.h new file mode 100644 index 0000000..1abfb0a --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QByteArray.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QBYTEARRAY_H_ +#define _QX_SERIALIZE_QBYTEARRAY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include + +#include + +#include + +#include + +QX_CLASS_VERSION(QByteArray, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QByteArray) + +#endif // _QX_SERIALIZE_QBYTEARRAY_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QColor.h b/include/QxSerialize/Qt/QxSerialize_QColor.h new file mode 100644 index 0000000..559ea5e --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QColor.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifdef _QX_ENABLE_QT_GUI +#ifndef _QX_SERIALIZE_QCOLOR_H_ +#define _QX_SERIALIZE_QCOLOR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include + +#include + +QX_CLASS_VERSION(QColor, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QColor) + +#endif // _QX_SERIALIZE_QCOLOR_H_ +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QDate.h b/include/QxSerialize/Qt/QxSerialize_QDate.h new file mode 100644 index 0000000..73bf118 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QDate.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QDATE_H_ +#define _QX_SERIALIZE_QDATE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include +#include + +#include + +#define QX_SERIALIZE_QDATE_FORMAT "yyyyMMdd" + +QX_CLASS_VERSION(QDate, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QDate) + +#endif // _QX_SERIALIZE_QDATE_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QDateTime.h b/include/QxSerialize/Qt/QxSerialize_QDateTime.h new file mode 100644 index 0000000..7900752 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QDateTime.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QDATETIME_H_ +#define _QX_SERIALIZE_QDATETIME_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include +#include + +#include + +#define QX_SERIALIZE_QDATETIME_FORMAT "yyyyMMddhhmmsszzz" + +QX_CLASS_VERSION(QDateTime, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QDateTime) + +#endif // _QX_SERIALIZE_QDATETIME_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QFlags.h b/include/QxSerialize/Qt/QxSerialize_QFlags.h new file mode 100644 index 0000000..7d3f9f5 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QFlags.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QFLAGS_H_ +#define _QX_SERIALIZE_QFLAGS_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void save(Archive &ar, const QFlags &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + int iValue = static_cast(t); + ar << boost::serialization::make_nvp("value", iValue); + } + + template + inline void load(Archive &ar, QFlags &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + int iValue = 0; + ar >> boost::serialization::make_nvp("value", iValue); + t = QFlags(QFlag(iValue)); + } + + template + inline void serialize(Archive &ar, QFlags &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + } // namespace boost +} // namespace serialization + +#endif // _QX_SERIALIZE_QFLAGS_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QFont.h b/include/QxSerialize/Qt/QxSerialize_QFont.h new file mode 100644 index 0000000..522642f --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QFont.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifdef _QX_ENABLE_QT_GUI +#ifndef _QX_SERIALIZE_QFONT_H_ +#define _QX_SERIALIZE_QFONT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include +#include + +#include + +QX_CLASS_VERSION(QFont, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QFont) + +#endif // _QX_SERIALIZE_QFONT_H_ +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QHash.h b/include/QxSerialize/Qt/QxSerialize_QHash.h new file mode 100644 index 0000000..f8a33fd --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QHash.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QHASH_H_ +#define _QX_SERIALIZE_QHASH_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void save(Archive &ar, const QHash &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = t.count(); + ar << boost::serialization::make_nvp("count", lCount); + + QHashIterator itr(t); + while (itr.hasNext()) + { + itr.next(); + std::pair pair_key_value = std::make_pair(itr.key(), itr.value()); + ar << boost::serialization::make_nvp("item", pair_key_value); + } + } + + template + inline void load(Archive &ar, QHash &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = 0; + ar >> boost::serialization::make_nvp("count", lCount); + + t.clear(); + t.reserve(lCount); + std::pair pair_key_value; + + for (long l = 0; l < lCount; l++) + { + ar >> boost::serialization::make_nvp("item", pair_key_value); + t.insert(pair_key_value.first, pair_key_value.second); + } + } + + template + inline void serialize(Archive &ar, QHash &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + } // namespace boost +} // namespace serialization + +#endif // _QX_SERIALIZE_QHASH_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QImage.h b/include/QxSerialize/Qt/QxSerialize_QImage.h new file mode 100644 index 0000000..ffe4670 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QImage.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifdef _QX_ENABLE_QT_GUI +#ifndef _QX_SERIALIZE_QIMAGE_H_ +#define _QX_SERIALIZE_QIMAGE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +QX_CLASS_VERSION(QImage, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QImage) +QX_BOOST_EXPORT_SERIALIZATION_FAST_COMPIL_HPP(QImage) + +#endif // _QX_SERIALIZE_QIMAGE_H_ +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QLinkedList.h b/include/QxSerialize/Qt/QxSerialize_QLinkedList.h new file mode 100644 index 0000000..30103ba --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QLinkedList.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QLINKEDLIST_H_ +#define _QX_SERIALIZE_QLINKEDLIST_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void save(Archive &ar, const QLinkedList &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = t.count(); + ar << boost::serialization::make_nvp("count", lCount); + + QLinkedListIterator itr(t); + while (itr.hasNext()) + ar << boost::serialization::make_nvp("item", itr.next()); + } + + template + inline void load(Archive &ar, QLinkedList &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = 0; + ar >> boost::serialization::make_nvp("count", lCount); + + t.clear(); + T item; + + for (long l = 0; l < lCount; l++) + { + ar >> boost::serialization::make_nvp("item", item); + t.append(item); + } + } + + template + inline void serialize(Archive &ar, QLinkedList &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + } // namespace boost +} // namespace serialization + +#endif // _QX_SERIALIZE_QLINKEDLIST_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) diff --git a/include/QxSerialize/Qt/QxSerialize_QList.h b/include/QxSerialize/Qt/QxSerialize_QList.h new file mode 100644 index 0000000..a1a16d8 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QList.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QLIST_H_ +#define _QX_SERIALIZE_QLIST_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void save(Archive &ar, const QList &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = t.count(); + ar << boost::serialization::make_nvp("count", lCount); + + for (long l = 0; l < lCount; l++) + ar << boost::serialization::make_nvp("item", t.at(l)); + } + + template + inline void load(Archive &ar, QList &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = 0; + ar >> boost::serialization::make_nvp("count", lCount); + + t.clear(); + T item; + + for (long l = 0; l < lCount; l++) + { + ar >> boost::serialization::make_nvp("item", item); + t.append(item); + } + } + + template + inline void serialize(Archive &ar, QList &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + } // namespace boost +} // namespace serialization + +#endif // _QX_SERIALIZE_QLIST_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QMap.h b/include/QxSerialize/Qt/QxSerialize_QMap.h new file mode 100644 index 0000000..929adb3 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QMap.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QMAP_H_ +#define _QX_SERIALIZE_QMAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void save(Archive &ar, const QMap &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = t.count(); + ar << boost::serialization::make_nvp("count", lCount); + + QMapIterator itr(t); + while (itr.hasNext()) + { + itr.next(); + std::pair pair_key_value = std::make_pair(itr.key(), itr.value()); + ar << boost::serialization::make_nvp("item", pair_key_value); + } + } + + template + inline void load(Archive &ar, QMap &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = 0; + ar >> boost::serialization::make_nvp("count", lCount); + + t.clear(); + std::pair pair_key_value; + + for (long l = 0; l < lCount; l++) + { + ar >> boost::serialization::make_nvp("item", pair_key_value); + t.insert(pair_key_value.first, pair_key_value.second); + } + } + + template + inline void serialize(Archive &ar, QMap &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + } // namespace boost +} // namespace serialization + +#endif // _QX_SERIALIZE_QMAP_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QMatrix.h b/include/QxSerialize/Qt/QxSerialize_QMatrix.h new file mode 100644 index 0000000..2ac5f36 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QMatrix.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifdef _QX_ENABLE_QT_GUI +#ifndef _QX_SERIALIZE_QMATRIX_H_ +#define _QX_SERIALIZE_QMATRIX_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include + +#include + +QX_CLASS_VERSION(QMatrix, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QMatrix) + +#endif // _QX_SERIALIZE_QMATRIX_H_ +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_ENABLE_BOOST_SERIALIZATION +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) diff --git a/include/QxSerialize/Qt/QxSerialize_QMultiHash.h b/include/QxSerialize/Qt/QxSerialize_QMultiHash.h new file mode 100644 index 0000000..edd00f0 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QMultiHash.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QMULTIHASH_H_ +#define _QX_SERIALIZE_QMULTIHASH_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void save(Archive &ar, const QMultiHash &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = t.count(); + ar << boost::serialization::make_nvp("count", lCount); + + QHashIterator itr(t); + while (itr.hasNext()) + { + itr.next(); + std::pair pair_key_value = std::make_pair(itr.key(), itr.value()); + ar << boost::serialization::make_nvp("item", pair_key_value); + } + } + + template + inline void load(Archive &ar, QMultiHash &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = 0; + ar >> boost::serialization::make_nvp("count", lCount); + + t.clear(); + t.reserve(lCount); + std::pair pair_key_value; + + for (long l = 0; l < lCount; l++) + { + ar >> boost::serialization::make_nvp("item", pair_key_value); + t.insert(pair_key_value.first, pair_key_value.second); + } + } + + template + inline void serialize(Archive &ar, QMultiHash &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + } // namespace boost +} // namespace serialization + +#endif // _QX_SERIALIZE_QMULTIHASH_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QMultiMap.h b/include/QxSerialize/Qt/QxSerialize_QMultiMap.h new file mode 100644 index 0000000..fd6b03f --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QMultiMap.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QMULTIMAP_H_ +#define _QX_SERIALIZE_QMULTIMAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void save(Archive &ar, const QMultiMap &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = t.count(); + ar << boost::serialization::make_nvp("count", lCount); + + QMapIterator itr(t); + while (itr.hasNext()) + { + itr.next(); + std::pair pair_key_value = std::make_pair(itr.key(), itr.value()); + ar << boost::serialization::make_nvp("item", pair_key_value); + } + } + + template + inline void load(Archive &ar, QMultiMap &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = 0; + ar >> boost::serialization::make_nvp("count", lCount); + + t.clear(); + std::pair pair_key_value; + + for (long l = 0; l < lCount; l++) + { + ar >> boost::serialization::make_nvp("item", pair_key_value); + t.insert(pair_key_value.first, pair_key_value.second); + } + } + + template + inline void serialize(Archive &ar, QMultiMap &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + } // namespace boost +} // namespace serialization + +#endif // _QX_SERIALIZE_QMULTIMAP_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QObject.h b/include/QxSerialize/Qt/QxSerialize_QObject.h new file mode 100644 index 0000000..e40f2fe --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QObject.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QOBJECT_H_ +#define _QX_SERIALIZE_QOBJECT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include + +#include + +#include +#include + +#include + +QX_CLASS_VERSION(QObject, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QObject) +QX_BOOST_EXPORT_SERIALIZATION_FAST_COMPIL_HPP(QObject) +QX_REGISTER_BOOST_SERIALIZE_HELPER(QObject) + +#endif // _QX_SERIALIZE_QOBJECT_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QPair.h b/include/QxSerialize/Qt/QxSerialize_QPair.h new file mode 100644 index 0000000..bee092d --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QPair.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QPAIR_H_ +#define _QX_SERIALIZE_QPAIR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void serialize(Archive &ar, QPair &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("first", t.first); + ar &boost::serialization::make_nvp("second", t.second); + } + + } // namespace boost +} // namespace serialization + +#endif // _QX_SERIALIZE_QPAIR_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QPicture.h b/include/QxSerialize/Qt/QxSerialize_QPicture.h new file mode 100644 index 0000000..2b71382 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QPicture.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifdef _QX_ENABLE_QT_GUI +#ifndef _QX_SERIALIZE_QPICTURE_H_ +#define _QX_SERIALIZE_QPICTURE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +QX_CLASS_VERSION(QPicture, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QPicture) +QX_BOOST_EXPORT_SERIALIZATION_FAST_COMPIL_HPP(QPicture) + +#endif // _QX_SERIALIZE_QPICTURE_H_ +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QPixmap.h b/include/QxSerialize/Qt/QxSerialize_QPixmap.h new file mode 100644 index 0000000..0f6e463 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QPixmap.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifdef _QX_ENABLE_QT_GUI +#ifndef _QX_SERIALIZE_QPIXMAP_H_ +#define _QX_SERIALIZE_QPIXMAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +QX_CLASS_VERSION(QPixmap, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QPixmap) +QX_BOOST_EXPORT_SERIALIZATION_FAST_COMPIL_HPP(QPixmap) + +#endif // _QX_SERIALIZE_QPIXMAP_H_ +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QPoint.h b/include/QxSerialize/Qt/QxSerialize_QPoint.h new file mode 100644 index 0000000..66516fd --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QPoint.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QPOINT_H_ +#define _QX_SERIALIZE_QPOINT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include + +#include + +QX_CLASS_VERSION(QPoint, 0) + +QX_SERIALIZE_FAST_COMPIL_SERIALIZE_HPP(QX_DLL_EXPORT, QPoint) + +#endif // _QX_SERIALIZE_QPOINT_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QRect.h b/include/QxSerialize/Qt/QxSerialize_QRect.h new file mode 100644 index 0000000..f871658 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QRect.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QRECT_H_ +#define _QX_SERIALIZE_QRECT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include + +#include + +QX_CLASS_VERSION(QRect, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QRect) + +#endif // _QX_SERIALIZE_QRECT_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QRegExp.h b/include/QxSerialize/Qt/QxSerialize_QRegExp.h new file mode 100644 index 0000000..f856508 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QRegExp.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QREGEXP_H_ +#define _QX_SERIALIZE_QREGEXP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include +#include + +#include + +QX_CLASS_VERSION(QRegExp, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QRegExp) + +#endif // _QX_SERIALIZE_QREGEXP_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) diff --git a/include/QxSerialize/Qt/QxSerialize_QRegion.h b/include/QxSerialize/Qt/QxSerialize_QRegion.h new file mode 100644 index 0000000..3f4113c --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QRegion.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifdef _QX_ENABLE_QT_GUI +#ifndef _QX_SERIALIZE_QREGION_H_ +#define _QX_SERIALIZE_QREGION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +QX_CLASS_VERSION(QRegion, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QRegion) + +#endif // _QX_SERIALIZE_QREGION_H_ +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QScopedPointer.h b/include/QxSerialize/Qt/QxSerialize_QScopedPointer.h new file mode 100644 index 0000000..c619b4a --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QScopedPointer.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) +#ifndef _QX_SERIALIZE_QSCOPEDPOINTER_H_ +#define _QX_SERIALIZE_QSCOPEDPOINTER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void save(Archive &ar, const QScopedPointer &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + const T *ptr = t.data(); + ar << boost::serialization::make_nvp("qt_scoped_ptr", ptr); + } + + template + inline void load(Archive &ar, QScopedPointer &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + T *ptr = NULL; + ar >> boost::serialization::make_nvp("qt_scoped_ptr", ptr); + t.reset(ptr); + } + + template + inline void serialize(Archive &ar, QScopedPointer &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + } // namespace boost +} // namespace serialization + +#endif // _QX_SERIALIZE_QSCOPEDPOINTER_H_ +#endif // (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QSharedPointer.h b/include/QxSerialize/Qt/QxSerialize_QSharedPointer.h new file mode 100644 index 0000000..8d532c4 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QSharedPointer.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QSHAREDPOINTER_H_ +#define _QX_SERIALIZE_QSHAREDPOINTER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include +#include +#include + +namespace boost +{ + namespace serialization + { + + template + inline void save(Archive &ar, const QSharedPointer &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + const T *ptr = t.data(); + ar << boost::serialization::make_nvp("qt_shared_ptr", ptr); + } + + template + inline void load(Archive &ar, QSharedPointer &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + static QHash> listOfWeakPointers; + T *ptr = NULL; + ar >> boost::serialization::make_nvp("qt_shared_ptr", ptr); + if (!ptr) + { + t = QSharedPointer(); + return; + } + + void *pVoid = static_cast(ptr); + QSharedPointer pShared = (listOfWeakPointers.contains(pVoid) ? listOfWeakPointers.value(pVoid).toStrongRef() : QSharedPointer()); + if (pShared) + { + t = pShared; + return; + } + t = QSharedPointer(ptr); + QWeakPointer pWeak = t; + listOfWeakPointers.insert(pVoid, pWeak); + + static int iLoadCount = 0; + iLoadCount++; + if (iLoadCount <= 1000) + { + return; + } + QMutableHashIterator> itr(listOfWeakPointers); + while (itr.hasNext()) + { + itr.next(); + if (itr.value().isNull()) + { + itr.remove(); + } + } + iLoadCount = 0; + } + + template + inline void serialize(Archive &ar, QSharedPointer &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + } // namespace boost +} // namespace serialization + +#endif // _QX_SERIALIZE_QSHAREDPOINTER_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QSize.h b/include/QxSerialize/Qt/QxSerialize_QSize.h new file mode 100644 index 0000000..e8ff763 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QSize.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QSIZE_H_ +#define _QX_SERIALIZE_QSIZE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include + +#include + +QX_CLASS_VERSION(QSize, 0) + +QX_SERIALIZE_FAST_COMPIL_SERIALIZE_HPP(QX_DLL_EXPORT, QSize) + +#endif // _QX_SERIALIZE_QSIZE_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QSqlError.h b/include/QxSerialize/Qt/QxSerialize_QSqlError.h new file mode 100644 index 0000000..2b72417 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QSqlError.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QSQLERROR_H_ +#define _QX_SERIALIZE_QSQLERROR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include +#include + +#include + +QX_CLASS_VERSION(QSqlError, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QSqlError) + +#endif // _QX_SERIALIZE_QSQLERROR_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QString.h b/include/QxSerialize/Qt/QxSerialize_QString.h new file mode 100644 index 0000000..1005300 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QString.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QSTRING_H_ +#define _QX_SERIALIZE_QSTRING_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +QX_CLASS_VERSION(QString, 0) +BOOST_CLASS_TRACKING(QString, boost::serialization::track_never) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QString) + +#endif // _QX_SERIALIZE_QSTRING_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QStringList.h b/include/QxSerialize/Qt/QxSerialize_QStringList.h new file mode 100644 index 0000000..316214b --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QStringList.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QSTRINGLIST_H_ +#define _QX_SERIALIZE_QSTRINGLIST_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +QX_CLASS_VERSION(QStringList, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QStringList) + +#endif // _QX_SERIALIZE_QSTRINGLIST_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QTime.h b/include/QxSerialize/Qt/QxSerialize_QTime.h new file mode 100644 index 0000000..d676172 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QTime.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QTIME_H_ +#define _QX_SERIALIZE_QTIME_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include +#include + +#include + +#define QX_SERIALIZE_QTIME_FORMAT "hhmmsszzz" + +QX_CLASS_VERSION(QTime, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QTime) + +#endif // _QX_SERIALIZE_QTIME_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QUrl.h b/include/QxSerialize/Qt/QxSerialize_QUrl.h new file mode 100644 index 0000000..c005b29 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QUrl.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QURL_H_ +#define _QX_SERIALIZE_QURL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include +#include + +#include + +QX_CLASS_VERSION(QUrl, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QUrl) + +#endif // _QX_SERIALIZE_QURL_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QUuid.h b/include/QxSerialize/Qt/QxSerialize_QUuid.h new file mode 100644 index 0000000..d76a0c7 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QUuid.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QUUID_H_ +#define _QX_SERIALIZE_QUUID_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include + +#include + +QX_CLASS_VERSION(QUuid, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QUuid) + +#endif // _QX_SERIALIZE_QUUID_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QVariant.h b/include/QxSerialize/Qt/QxSerialize_QVariant.h new file mode 100644 index 0000000..c72d9bd --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QVariant.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QVARIANT_H_ +#define _QX_SERIALIZE_QVARIANT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +QX_CLASS_VERSION(QVariant, 0) + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(QX_DLL_EXPORT, QVariant) + +#endif // _QX_SERIALIZE_QVARIANT_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qt/QxSerialize_QVector.h b/include/QxSerialize/Qt/QxSerialize_QVector.h new file mode 100644 index 0000000..02ea22f --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QVector.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QVECTOR_H_ +#define _QX_SERIALIZE_QVECTOR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void save(Archive &ar, const QVector &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = t.count(); + ar << boost::serialization::make_nvp("count", lCount); + + for (long l = 0; l < lCount; l++) + ar << boost::serialization::make_nvp("item", t.at(l)); + } + + template + inline void load(Archive &ar, QVector &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = 0; + ar >> boost::serialization::make_nvp("count", lCount); + + t.clear(); + t.reserve(lCount); + T item; + + for (long l = 0; l < lCount; l++) + { + ar >> boost::serialization::make_nvp("item", item); + t.append(item); + } + } + + template + inline void serialize(Archive &ar, QVector &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + } // namespace boost +} // namespace serialization + +#endif // _QX_SERIALIZE_QVECTOR_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) diff --git a/include/QxSerialize/Qt/QxSerialize_QWeakPointer.h b/include/QxSerialize/Qt/QxSerialize_QWeakPointer.h new file mode 100644 index 0000000..28f4a15 --- /dev/null +++ b/include/QxSerialize/Qt/QxSerialize_QWeakPointer.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QWEAKPOINTER_H_ +#define _QX_SERIALIZE_QWEAKPOINTER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void save(Archive &ar, const QWeakPointer &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QSharedPointer ptr = t.toStrongRef(); + ar << boost::serialization::make_nvp("qt_weak_ptr", ptr); + } + + template + inline void load(Archive &ar, QWeakPointer &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QSharedPointer ptr; + ar >> boost::serialization::make_nvp("qt_weak_ptr", ptr); + t = QWeakPointer(ptr); + } + + template + inline void serialize(Archive &ar, QWeakPointer &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + } // namespace boost +} // namespace serialization + +#endif // _QX_SERIALIZE_QWEAKPOINTER_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qx/QxSerialize_QxCollection.h b/include/QxSerialize/Qx/QxSerialize_QxCollection.h new file mode 100644 index 0000000..4801fb4 --- /dev/null +++ b/include/QxSerialize/Qx/QxSerialize_QxCollection.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QX_COLLECTION_H_ +#define _QX_SERIALIZE_QX_COLLECTION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void save(Archive &ar, const qx::QxCollection &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = t.count(); + ar << boost::serialization::make_nvp("count", lCount); + + for (long l = 0; l < lCount; l++) + { + std::pair pair_key_value = std::make_pair(t.getKeyByIndex(l), t.getByIndex(l)); + ar << boost::serialization::make_nvp("item", pair_key_value); + } + } + + template + inline void load(Archive &ar, qx::QxCollection &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = 0; + ar >> boost::serialization::make_nvp("count", lCount); + + t.clear(); + t.reserve(lCount); + std::pair pair_key_value; + + for (long l = 0; l < lCount; l++) + { + ar >> boost::serialization::make_nvp("item", pair_key_value); + t.insert(pair_key_value.first, pair_key_value.second); + } + } + + template + inline void serialize(Archive &ar, qx::QxCollection &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + } // namespace boost +} // namespace serialization + +#endif // _QX_SERIALIZE_QX_COLLECTION_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qx/QxSerialize_QxDaoPointer.h b/include/QxSerialize/Qx/QxSerialize_QxDaoPointer.h new file mode 100644 index 0000000..1a9ed31 --- /dev/null +++ b/include/QxSerialize/Qx/QxSerialize_QxDaoPointer.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_QX_DAO_POINTER_H_ +#define _QX_SERIALIZE_QX_DAO_POINTER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void save(Archive &ar, const qx::dao::ptr &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + const T *ptr = t.data(); + const T *original = t.dataOriginal(); + ar << boost::serialization::make_nvp("qx_dao_ptr", ptr); + ar << boost::serialization::make_nvp("qx_dao_ptr_original", original); + } + + template + inline void load(Archive &ar, qx::dao::ptr &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + T *ptr = NULL; + T *original = NULL; + ar >> boost::serialization::make_nvp("qx_dao_ptr", ptr); + ar >> boost::serialization::make_nvp("qx_dao_ptr_original", original); + t = qx::dao::ptr(ptr, original); + } + + template + inline void serialize(Archive &ar, qx::dao::ptr &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + } // namespace boost +} // namespace serialization + +#endif // _QX_SERIALIZE_QX_DAO_POINTER_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qx/QxSerialize_QxXmlReader.h b/include/QxSerialize/Qx/QxSerialize_QxXmlReader.h new file mode 100644 index 0000000..6a41202 --- /dev/null +++ b/include/QxSerialize/Qx/QxSerialize_QxXmlReader.h @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/Qx/QxSerialize_QxXmlWriter.h b/include/QxSerialize/Qx/QxSerialize_QxXmlWriter.h new file mode 100644 index 0000000..6a41202 --- /dev/null +++ b/include/QxSerialize/Qx/QxSerialize_QxXmlWriter.h @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/QxArchive.h b/include/QxSerialize/QxArchive.h new file mode 100644 index 0000000..d955241 --- /dev/null +++ b/include/QxSerialize/QxArchive.h @@ -0,0 +1,307 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_ARCHIVE_H_ +#define _QX_ARCHIVE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxArchive.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide some tools to serialize a class registered into QxOrm context into/from binary/XML archive (file, string, etc.) + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#define QX_STR_SERIALIZATION_ERROR "Serialization error : '%ERR%'" +#define QX_STR_DESERIALIZATION_ERROR "Deserialization error : '%ERR%'" + +namespace qx +{ + + /*! + * \ingroup QxSerialize + * \brief qx::QxArchive_ToFile : provide some tools to serialize a class registered into QxOrm context into/from binary/text/XML archives (file, string, etc.) + */ + template + class QxArchive_ToFile + { + public: + static qx_bool toFile(const T &obj, const QString &sFileName, unsigned int flags = boost::archive::no_header); + }; + + /*! + * \ingroup QxSerialize + * \brief qx::QxArchive_FromFile : provide some tools to serialize a class registered into QxOrm context into/from binary/text/XML archives (file, string, etc.) + */ + template + class QxArchive_FromFile + { + public: + static qx_bool fromFile(T &obj, const QString &sFileName, unsigned int flags = boost::archive::no_header); + }; + + /*! + * \ingroup QxSerialize + * \brief qx::QxArchive_ToFileCompressed : provide some tools to serialize a class registered into QxOrm context into/from binary/text/XML archives (file, string, etc.) + */ + template + class QxArchive_ToFileCompressed + { + public: + static qx_bool toFileCompressed(const T &obj, const QString &sFileName, unsigned int flags = boost::archive::no_header, int iCompressionLevel = -1); + }; + + /*! + * \ingroup QxSerialize + * \brief qx::QxArchive_FromFileCompressed : provide some tools to serialize a class registered into QxOrm context into/from binary/text/XML archives (file, string, etc.) + */ + template + class QxArchive_FromFileCompressed + { + public: + static qx_bool fromFileCompressed(T &obj, const QString &sFileName, unsigned int flags = boost::archive::no_header); + }; + + /*! + * \ingroup QxSerialize + * \brief qx::QxArchive_ToString : provide some tools to serialize a class registered into QxOrm context into/from binary/text/XML archives (file, string, etc.) + */ + template + class QxArchive_ToString + { + public: + static QString toString(const T &obj, unsigned int flags = boost::archive::no_header); + }; + + /*! + * \ingroup QxSerialize + * \brief qx::QxArchive_FromString : provide some tools to serialize a class registered into QxOrm context into/from binary/text/XML archives (file, string, etc.) + */ + template + class QxArchive_FromString + { + public: + static qx_bool fromString(T &obj, const QString &sString, unsigned int flags = boost::archive::no_header); + }; + + /*! + * \ingroup QxSerialize + * \brief qx::QxArchive_ToByteArray : provide some tools to serialize a class registered into QxOrm context into/from binary/text/XML archives (file, string, etc.) + */ + template + class QxArchive_ToByteArray + { + public: + typedef typename qx::trait::archive_wide_traits::type_string type_string; + static QByteArray toByteArray(const T &obj, type_string *owner = NULL, unsigned int flags = boost::archive::no_header); + }; + + /*! + * \ingroup QxSerialize + * \brief qx::QxArchive_FromByteArray : provide some tools to serialize a class registered into QxOrm context into/from binary/text/XML archives (file, string, etc.) + */ + template + class QxArchive_FromByteArray + { + public: + static qx_bool fromByteArray(T &obj, const QByteArray &data, unsigned int flags = boost::archive::no_header); + }; + +} // namespace qx + +#include "../../inl/QxSerialize/QxArchive.inl" + +namespace qx +{ + namespace serialization + { + + QX_ARCHIVE_NAMESPACE_FCT_IMPL(T, QX_DEFAULT_ARCHIVE_INPUT, QX_DEFAULT_ARCHIVE_OUTPUT) + +#if _QX_SERIALIZE_POLYMORPHIC + + /*! + * \ingroup QxSerialize + * \brief serialize a class registered into QxOrm context into/from polymorphic_binary archive (file, string, etc.) : enable this feature defining _QX_ENABLE_BOOST_SERIALIZATION and _QX_ENABLE_BOOST_SERIALIZATION_POLYMORPHIC compilation options in QxOrm.pri configuration file + */ + namespace polymorphic_binary + { + QX_ARCHIVE_NAMESPACE_FCT_IMPL(T, boost::archive::polymorphic_binary_iarchive, boost::archive::polymorphic_binary_oarchive) + } // namespace polymorphic_binary + + /*! + * \ingroup QxSerialize + * \brief serialize a class registered into QxOrm context into/from polymorphic_text archive (file, string, etc.) : enable this feature defining _QX_ENABLE_BOOST_SERIALIZATION and _QX_ENABLE_BOOST_SERIALIZATION_POLYMORPHIC compilation options in QxOrm.pri configuration file + */ + namespace polymorphic_text + { + QX_ARCHIVE_NAMESPACE_FCT_IMPL(T, boost::archive::polymorphic_text_iarchive, boost::archive::polymorphic_text_oarchive) + } // namespace polymorphic_text + + /*! + * \ingroup QxSerialize + * \brief serialize a class registered into QxOrm context into/from polymorphic_xml archive (file, string, etc.) : enable this feature defining _QX_ENABLE_BOOST_SERIALIZATION and _QX_ENABLE_BOOST_SERIALIZATION_POLYMORPHIC compilation options in QxOrm.pri configuration file + */ + namespace polymorphic_xml + { + QX_ARCHIVE_NAMESPACE_FCT_IMPL(T, boost::archive::polymorphic_xml_iarchive, boost::archive::polymorphic_xml_oarchive) + } // namespace polymorphic_xml + +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY + + /*! + * \ingroup QxSerialize + * \brief serialize a class registered into QxOrm context into/from binary archive (file, string, etc.) : enable this feature defining _QX_ENABLE_BOOST_SERIALIZATION and _QX_ENABLE_BOOST_SERIALIZATION_BINARY compilation options in QxOrm.pri configuration file + */ + namespace binary + { + QX_ARCHIVE_NAMESPACE_FCT_IMPL(T, boost::archive::binary_iarchive, boost::archive::binary_oarchive) + } // namespace binary + +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT + + /*! + * \ingroup QxSerialize + * \brief serialize a class registered into QxOrm context into/from text archive (file, string, etc.) : enable this feature defining _QX_ENABLE_BOOST_SERIALIZATION and _QX_ENABLE_BOOST_SERIALIZATION_TEXT compilation options in QxOrm.pri configuration file + */ + namespace text + { + QX_ARCHIVE_NAMESPACE_FCT_IMPL(T, boost::archive::text_iarchive, boost::archive::text_oarchive) + } // namespace text + +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML + + /*! + * \ingroup QxSerialize + * \brief serialize a class registered into QxOrm context into/from XML archive (file, string, etc.) : enable this feature defining _QX_ENABLE_BOOST_SERIALIZATION and _QX_ENABLE_BOOST_SERIALIZATION_XML compilation options in QxOrm.pri configuration file + */ + namespace xml + { + QX_ARCHIVE_NAMESPACE_FCT_IMPL(T, boost::archive::xml_iarchive, boost::archive::xml_oarchive) + } // namespace xml + +#endif // _QX_SERIALIZE_XML + +#if _QX_SERIALIZE_PORTABLE_BINARY + + /*! + * \ingroup QxSerialize + * \brief serialize a class registered into QxOrm context into/from portable_binary archive (file, string, etc.) : enable this feature defining _QX_ENABLE_BOOST_SERIALIZATION and _QX_ENABLE_BOOST_SERIALIZATION_PORTABLE_BINARY compilation options in QxOrm.pri configuration file + */ + namespace portable_binary + { + QX_ARCHIVE_NAMESPACE_FCT_IMPL(T, eos::portable_iarchive, eos::portable_oarchive) + } // namespace portable_binary + +#endif // _QX_SERIALIZE_PORTABLE_BINARY + + namespace wide + { + +#if _QX_SERIALIZE_WIDE_BINARY + + /*! + * \ingroup QxSerialize + * \brief serialize a class registered into QxOrm context into/from wide binary archive (file, string, etc.) : enable this feature defining _QX_ENABLE_BOOST_SERIALIZATION and _QX_ENABLE_BOOST_SERIALIZATION_WIDE_BINARY compilation options in QxOrm.pri configuration file + */ + namespace binary + { + QX_ARCHIVE_NAMESPACE_FCT_IMPL(T, boost::archive::binary_wiarchive, boost::archive::binary_woarchive) + } // namespace binary + +#endif // _QX_SERIALIZE_WIDE_BINARY + +#if _QX_SERIALIZE_WIDE_TEXT + + /*! + * \ingroup QxSerialize + * \brief serialize a class registered into QxOrm context into/from wide text archive (file, string, etc.) : enable this feature defining _QX_ENABLE_BOOST_SERIALIZATION and _QX_ENABLE_BOOST_SERIALIZATION_WIDE_TEXT compilation options in QxOrm.pri configuration file + */ + namespace text + { + QX_ARCHIVE_NAMESPACE_FCT_IMPL(T, boost::archive::text_wiarchive, boost::archive::text_woarchive) + } // namespace text + +#endif // _QX_SERIALIZE_WIDE_TEXT + +#if _QX_SERIALIZE_WIDE_XML + + /*! + * \ingroup QxSerialize + * \brief serialize a class registered into QxOrm context into/from wide XML archive (file, string, etc.) : enable this feature defining _QX_ENABLE_BOOST_SERIALIZATION and _QX_ENABLE_BOOST_SERIALIZATION_WIDE_XML compilation options in QxOrm.pri configuration file + */ + namespace xml + { + QX_ARCHIVE_NAMESPACE_FCT_IMPL(T, boost::archive::xml_wiarchive, boost::archive::xml_woarchive) + } // namespace xml + +#endif // _QX_SERIALIZE_WIDE_XML + + } // namespace wide + } // namespace serialization +} // namespace qx + +#endif // _QX_ARCHIVE_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/QxBoostSerializeHelper/IxBoostSerializeRegisterHelper.h b/include/QxSerialize/QxBoostSerializeHelper/IxBoostSerializeRegisterHelper.h new file mode 100644 index 0000000..721e78d --- /dev/null +++ b/include/QxSerialize/QxBoostSerializeHelper/IxBoostSerializeRegisterHelper.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _IX_BOOST_SERIALIZE_REGISTER_HELPER_H_ +#define _IX_BOOST_SERIALIZE_REGISTER_HELPER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#include + +#define QX_IX_BOOST_SERIALIZE_REGISTER_HELPER_PURE_VIRTUAL_ARCHIVE(ArchiveIn, ArchiveOut) \ + virtual void helper(ArchiveIn &ar) const = 0; \ + virtual void helper(ArchiveOut &ar) const = 0; + +namespace qx +{ + + class QX_DLL_EXPORT IxBoostSerializeRegisterHelper + { + + private: + QString m_sKey; // Register key used by 'QxBoostSerializeRegisterHelperX' + + protected: + IxBoostSerializeRegisterHelper(const QString &sKey); + virtual ~IxBoostSerializeRegisterHelper() = 0; + + public: + virtual void helper() const = 0; + +#if _QX_SERIALIZE_POLYMORPHIC + QX_IX_BOOST_SERIALIZE_REGISTER_HELPER_PURE_VIRTUAL_ARCHIVE(boost::archive::polymorphic_iarchive, boost::archive::polymorphic_oarchive) +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY + QX_IX_BOOST_SERIALIZE_REGISTER_HELPER_PURE_VIRTUAL_ARCHIVE(boost::archive::binary_iarchive, boost::archive::binary_oarchive) +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT + QX_IX_BOOST_SERIALIZE_REGISTER_HELPER_PURE_VIRTUAL_ARCHIVE(boost::archive::text_iarchive, boost::archive::text_oarchive) +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML + QX_IX_BOOST_SERIALIZE_REGISTER_HELPER_PURE_VIRTUAL_ARCHIVE(boost::archive::xml_iarchive, boost::archive::xml_oarchive) +#endif // _QX_SERIALIZE_XML + +#if _QX_SERIALIZE_PORTABLE_BINARY + QX_IX_BOOST_SERIALIZE_REGISTER_HELPER_PURE_VIRTUAL_ARCHIVE(eos::portable_iarchive, eos::portable_oarchive) +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#if _QX_SERIALIZE_WIDE_BINARY + QX_IX_BOOST_SERIALIZE_REGISTER_HELPER_PURE_VIRTUAL_ARCHIVE(boost::archive::binary_wiarchive, boost::archive::binary_woarchive) +#endif // _QX_SERIALIZE_WIDE_BINARY + +#if _QX_SERIALIZE_WIDE_TEXT + QX_IX_BOOST_SERIALIZE_REGISTER_HELPER_PURE_VIRTUAL_ARCHIVE(boost::archive::text_wiarchive, boost::archive::text_woarchive) +#endif // _QX_SERIALIZE_WIDE_TEXT + +#if _QX_SERIALIZE_WIDE_XML + QX_IX_BOOST_SERIALIZE_REGISTER_HELPER_PURE_VIRTUAL_ARCHIVE(boost::archive::xml_wiarchive, boost::archive::xml_woarchive) +#endif // _QX_SERIALIZE_WIDE_XML + + private: + IxBoostSerializeRegisterHelper(const IxBoostSerializeRegisterHelper &other) { Q_UNUSED(other); } + IxBoostSerializeRegisterHelper &operator=(const IxBoostSerializeRegisterHelper &other) + { + Q_UNUSED(other); + return (*this); + } + }; + +} // namespace qx + +#endif // _IX_BOOST_SERIALIZE_REGISTER_HELPER_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/QxBoostSerializeHelper/QxBoostInitGuid.h b/include/QxSerialize/QxBoostSerializeHelper/QxBoostInitGuid.h new file mode 100644 index 0000000..e09bc09 --- /dev/null +++ b/include/QxSerialize/QxBoostSerializeHelper/QxBoostInitGuid.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_BOOST_INIT_GUID_H_ +#define _QX_BOOST_INIT_GUID_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include +#include + +#include + +namespace qx +{ + + template + class QxBoostInitGuid + { + + public: + static inline void init() + { + boost::serialization::singleton>::get_mutable_instance().guid(qx::trait::get_class_name::get()); + } + + private: + inline void guid(const char *key, std::false_type) + { + Q_UNUSED(key); /* boost::archive::detail::instantiate_ptr_serialization((T *)0, 0, boost::archive::detail::adl_tag()); */ + } + + inline void guid(const char *key, std::true_type) + { + Q_UNUSED(key); + } + + inline void guid(const char *key) + { + qAssert(NULL != key); + boost::serialization::singleton::type>::get_mutable_instance().key_register(key); + guid(key, boost::serialization::is_abstract()); + } + }; + +} // namespace qx + +#endif // _QX_BOOST_INIT_GUID_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeHelper.h b/include/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeHelper.h new file mode 100644 index 0000000..ffa7786 --- /dev/null +++ b/include/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeHelper.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_BOOST_SERIALIZE_HELPER_H_ +#define _QX_BOOST_SERIALIZE_HELPER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#include + +#include + +namespace qx +{ + + template + class QxBoostSerializeHelper + { + + private: + typedef T type_derived; + typedef typename qx::trait::get_base_class::type type_base; + + public: + template + static inline void register_type_helper(Archive &ar, const T *t) + { + Q_UNUSED(t); + ar.template register_type(); + } + + static inline void void_cast_register_helper(const T *t) + { + Q_UNUSED(t); + QxBoostVoidCastRegisterHelper::value, 0>::helper(); + } + + static inline void init_guid(const T *t) + { + Q_UNUSED(t); + QxBoostInitGuid::init(); + } + + private: + template + struct QxBoostVoidCastRegisterHelper + { + static inline void helper() { boost::serialization::void_cast_register(static_cast(NULL), static_cast(NULL)); } + }; + + template + struct QxBoostVoidCastRegisterHelper + { + static inline void helper() { /* Nothing */ ; } + }; + }; + +} // namespace qx + +#endif // _QX_BOOST_SERIALIZE_HELPER_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeRegisterHelper.h b/include/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeRegisterHelper.h new file mode 100644 index 0000000..d4a145b --- /dev/null +++ b/include/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeRegisterHelper.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_BOOST_SERIALIZE_REGISTER_HELPER_H_ +#define _QX_BOOST_SERIALIZE_REGISTER_HELPER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include + +#define QX_BOOST_SERIALIZE_REGISTER_HELPER_IMPLEMENT_VIRTUAL_ARCHIVE(ArchiveIn, ArchiveOut) \ + virtual void helper(ArchiveIn &ar) const { private_helper(ar); } \ + virtual void helper(ArchiveOut &ar) const { private_helper(ar); } + +namespace qx +{ + + template + class QxBoostSerializeRegisterHelper : public IxBoostSerializeRegisterHelper + { + + public: + QxBoostSerializeRegisterHelper(const QString &sKey) : IxBoostSerializeRegisterHelper(sKey) { ; } + virtual ~QxBoostSerializeRegisterHelper() { ; } + + virtual void helper() const { private_helper(); } + +#if _QX_SERIALIZE_POLYMORPHIC + QX_BOOST_SERIALIZE_REGISTER_HELPER_IMPLEMENT_VIRTUAL_ARCHIVE(boost::archive::polymorphic_iarchive, boost::archive::polymorphic_oarchive) +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY + QX_BOOST_SERIALIZE_REGISTER_HELPER_IMPLEMENT_VIRTUAL_ARCHIVE(boost::archive::binary_iarchive, boost::archive::binary_oarchive) +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT + QX_BOOST_SERIALIZE_REGISTER_HELPER_IMPLEMENT_VIRTUAL_ARCHIVE(boost::archive::text_iarchive, boost::archive::text_oarchive) +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML + QX_BOOST_SERIALIZE_REGISTER_HELPER_IMPLEMENT_VIRTUAL_ARCHIVE(boost::archive::xml_iarchive, boost::archive::xml_oarchive) +#endif // _QX_SERIALIZE_XML + +#if _QX_SERIALIZE_PORTABLE_BINARY + QX_BOOST_SERIALIZE_REGISTER_HELPER_IMPLEMENT_VIRTUAL_ARCHIVE(eos::portable_iarchive, eos::portable_oarchive) +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#if _QX_SERIALIZE_WIDE_BINARY + QX_BOOST_SERIALIZE_REGISTER_HELPER_IMPLEMENT_VIRTUAL_ARCHIVE(boost::archive::binary_wiarchive, boost::archive::binary_woarchive) +#endif // _QX_SERIALIZE_WIDE_BINARY + +#if _QX_SERIALIZE_WIDE_TEXT + QX_BOOST_SERIALIZE_REGISTER_HELPER_IMPLEMENT_VIRTUAL_ARCHIVE(boost::archive::text_wiarchive, boost::archive::text_woarchive) +#endif // _QX_SERIALIZE_WIDE_TEXT + +#if _QX_SERIALIZE_WIDE_XML + QX_BOOST_SERIALIZE_REGISTER_HELPER_IMPLEMENT_VIRTUAL_ARCHIVE(boost::archive::xml_wiarchive, boost::archive::xml_woarchive) +#endif // _QX_SERIALIZE_WIDE_XML + + private: + template + inline void private_helper(Archive &ar) const + { + qx::QxBoostSerializeHelper::register_type_helper(ar, static_cast(NULL)); + qx::QxBoostSerializeHelper::void_cast_register_helper(static_cast(NULL)); + qx::QxBoostSerializeHelper::init_guid(static_cast(NULL)); + } + + inline void private_helper() const + { + qx::QxBoostSerializeHelper::void_cast_register_helper(static_cast(NULL)); + qx::QxBoostSerializeHelper::init_guid(static_cast(NULL)); + } + }; + +} // namespace qx + +#if _QX_USE_BOOST_SERIALIZE_REGISTER_HELPER +#define QX_REGISTER_BOOST_SERIALIZE_HELPER_COMPLEX_CLASS_NAME(className, classNameFormatted) \ + extern qx::QxBoostSerializeRegisterHelper G_QX_REGISTER_BOOST_SERIALIZE_HELPER_##classNameFormatted; +#define QX_REGISTER_BOOST_SERIALIZE_HELPER(className) \ + QX_REGISTER_BOOST_SERIALIZE_HELPER_COMPLEX_CLASS_NAME(className, className) +#else +#define QX_REGISTER_BOOST_SERIALIZE_HELPER_COMPLEX_CLASS_NAME(className, classNameFormatted) /* Nothing */ +#define QX_REGISTER_BOOST_SERIALIZE_HELPER(className) /* Nothing */ +#endif // _QX_USE_BOOST_SERIALIZE_REGISTER_HELPER + +#if _QX_USE_BOOST_SERIALIZE_REGISTER_HELPER +#define QX_REGISTER_BOOST_SERIALIZE_HELPER_COMPLEX_CLASS_NAME_CPP(className, classNameFormatted) \ + qx::QxBoostSerializeRegisterHelper G_QX_REGISTER_BOOST_SERIALIZE_HELPER_##classNameFormatted(QString(#className)); +#define QX_REGISTER_BOOST_SERIALIZE_HELPER_CPP(className) \ + QX_REGISTER_BOOST_SERIALIZE_HELPER_COMPLEX_CLASS_NAME_CPP(className, className) +#else +#define QX_REGISTER_BOOST_SERIALIZE_HELPER_COMPLEX_CLASS_NAME_CPP(className, classNameFormatted) /* Nothing */ +#define QX_REGISTER_BOOST_SERIALIZE_HELPER_CPP(className) /* Nothing */ +#endif // _QX_USE_BOOST_SERIALIZE_REGISTER_HELPER + +#endif // _QX_BOOST_SERIALIZE_REGISTER_HELPER_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeRegisterHelperX.h b/include/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeRegisterHelperX.h new file mode 100644 index 0000000..2f73d7e --- /dev/null +++ b/include/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeRegisterHelperX.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_BOOST_SERIALIZE_REGISTER_HELPER_X_H_ +#define _QX_BOOST_SERIALIZE_REGISTER_HELPER_X_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#include + +#include + +#include + +#include +#include + +namespace qx +{ + + class QX_DLL_EXPORT QxBoostSerializeRegisterHelperX : public QxSingleton + { + + friend class QxSingleton; + friend class IxBoostSerializeRegisterHelper; + + private: + QxCollection m_colHelperX; // Collection of all 'IxBoostSerializeRegisterHelper' pointer + QMutex m_oMutexHelperX; // Mutex -> 'QxBoostSerializeRegisterHelperX' is thread-safe + + QxBoostSerializeRegisterHelperX() : QxSingleton("qx::QxBoostSerializeRegisterHelperX") { ; } + virtual ~QxBoostSerializeRegisterHelperX() { ; } + + public: + void add(const QString &sKey, IxBoostSerializeRegisterHelper *p); + void remove(const QString &sKey); + + template + static inline void helper(Archive &ar) +#if _QX_USE_BOOST_SERIALIZE_REGISTER_HELPER + { + QxBoostSerializeRegisterHelperX::getSingleton()->private_helper(ar); + } +#else + { + Q_UNUSED(ar); + } +#endif // _QX_USE_BOOST_SERIALIZE_REGISTER_HELPER + + static inline void helper() +#if _QX_USE_BOOST_SERIALIZE_REGISTER_HELPER + { + QxBoostSerializeRegisterHelperX::getSingleton()->private_helper(); + } +#else + { + ; + } +#endif // _QX_USE_BOOST_SERIALIZE_REGISTER_HELPER + + private: + template + inline void private_helper(Archive &ar) const + { + static bool bDone = false; + if (bDone) + { + return; + } + QMutexLocker locker(const_cast(&m_oMutexHelperX)); + if (bDone) + { + return; + } + _foreach(IxBoostSerializeRegisterHelper * p, m_colHelperX) { p->helper(ar); } + bDone = true; + } + + inline void private_helper() const + { + static bool bDone = false; + if (bDone) + { + return; + } + QMutexLocker locker(const_cast(&m_oMutexHelperX)); + if (bDone) + { + return; + } + _foreach(IxBoostSerializeRegisterHelper * p, m_colHelperX) { p->helper(); } + bDone = true; + } + }; + +} // namespace qx + +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::QxBoostSerializeRegisterHelperX) + +#endif // _QX_BOOST_SERIALIZE_REGISTER_HELPER_X_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/QxClone.h b/include/QxSerialize/QxClone.h new file mode 100644 index 0000000..87d1dbe --- /dev/null +++ b/include/QxSerialize/QxClone.h @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_CLONE_H_ +#define _QX_CLONE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxClone.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Clone all classes registered into QxOrm context using QxOrm library serialization engine + */ + +#include +#include +#include +#include + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#include +#include +#include + +#else // _QX_ENABLE_BOOST_SERIALIZATION + +#include +#include + +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#define QX_STR_CLONE_SERIALIZATION_ERROR "[QxOrm] qx::clone() serialization error : '%s'" +#define QX_STR_CLONE_DESERIALIZATION_ERROR "[QxOrm] qx::clone() deserialization error : '%s'" + +namespace qx +{ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + + /*! + * \ingroup QxSerialize + * \brief qx::clone_to_nude_ptr(const T & obj) : return a nude pointer (be careful with memory leak) of a new instance of type T cloned from obj + */ + template + T *clone_to_nude_ptr(const T &obj) + { + QX_CLONE_STRING_STREAM ioss(std::ios_base::binary | std::ios_base::in | std::ios_base::out); + QX_CLONE_BINARY_OUTPUT_ARCHIVE oar(ioss, boost::archive::no_header); + QxBoostSerializeRegisterHelperX::helper(oar); + bool bSerializeOk = false; + + try + { + oar << obj; + bSerializeOk = ioss.good(); + } + catch (const boost::archive::archive_exception &e) + { + qDebug(QX_STR_CLONE_SERIALIZATION_ERROR, e.what()); + } + catch (const std::exception &e) + { + qDebug(QX_STR_CLONE_SERIALIZATION_ERROR, e.what()); + } + catch (...) + { + qDebug(QX_STR_CLONE_SERIALIZATION_ERROR, "unknown error"); + } + if (!bSerializeOk) + { + qAssert(false); + return NULL; + } + + T *pClone = new T(); + QX_CLONE_BINARY_INPUT_ARCHIVE iar(ioss, boost::archive::no_header); + QxBoostSerializeRegisterHelperX::helper(iar); + bool bDeserializeOk = false; + + try + { + iar >> (*pClone); + bDeserializeOk = ioss.good(); + } + catch (const boost::archive::archive_exception &e) + { + qDebug(QX_STR_CLONE_DESERIALIZATION_ERROR, e.what()); + } + catch (const std::exception &e) + { + qDebug(QX_STR_CLONE_DESERIALIZATION_ERROR, e.what()); + } + catch (...) + { + qDebug(QX_STR_CLONE_DESERIALIZATION_ERROR, "unknown error"); + } + qAssert(bDeserializeOk); + + return (bDeserializeOk ? pClone : NULL); + } + +#else // _QX_ENABLE_BOOST_SERIALIZATION + + /*! + * \ingroup QxSerialize + * \brief qx::clone_to_nude_ptr(const T & obj) : return a nude pointer (be careful with memory leak) of a new instance of type T cloned from obj (this is a limited clone version which uses Qt QDataStream engine compared to boost::serialization engine) + */ + template + T *clone_to_nude_ptr(const T &obj) + { + QByteArray baClone = qx::serialization::qt::to_byte_array(obj); + if (baClone.isEmpty()) + { + qAssertMsg(false, "[QxOrm] qx::clone_to_nude_ptr", "an error occurred during QDataStream serialization process"); + return NULL; + } + T *pClone = new T(); + qx_bool bOk = qx::serialization::qt::from_byte_array((*pClone), baClone); + return (bOk ? pClone : NULL); + } + +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + /*! + * \ingroup QxSerialize + * \brief qx::clone(const T & obj) : return a boost smart-pointer (std::shared_ptr) of a new instance of type T cloned from obj + */ + template + std::shared_ptr clone(const T &obj) + { + T *ptr = qx::clone_to_nude_ptr(obj); + return std::shared_ptr(ptr); + } + +#ifdef _QX_ENABLE_BOOST + + /*! + * \ingroup QxSerialize + * \brief qx::clone_to_boost_shared_ptr(const T & obj) : return a boost smart-pointer (boost::shared_ptr) of a new instance of type T cloned from obj + */ + template + boost::shared_ptr clone_to_boost_shared_ptr(const T &obj) + { + T *ptr = qx::clone_to_nude_ptr(obj); + return boost::shared_ptr(ptr); + } + +#endif // _QX_ENABLE_BOOST + + /*! + * \ingroup QxSerialize + * \brief qx::clone_to_qt_shared_ptr(const T & obj) : return a Qt smart-pointer (QSharedPointer) of a new instance of type T cloned from obj + */ + template + QSharedPointer clone_to_qt_shared_ptr(const T &obj) + { + T *ptr = qx::clone_to_nude_ptr(obj); + return QSharedPointer(ptr); + } + + /*! + * \ingroup QxSerialize + * \brief qx::clone_to_std_shared_ptr(const T & obj) : return a C++11 std smart-pointer (std::shared_ptr) of a new instance of type T cloned from obj + */ + template + std::shared_ptr clone_to_std_shared_ptr(const T &obj) + { + T *ptr = qx::clone_to_nude_ptr(obj); + return std::shared_ptr(ptr); + } + +} // namespace qx + +#endif // _QX_CLONE_H_ diff --git a/include/QxSerialize/QxDump.h b/include/QxSerialize/QxDump.h new file mode 100644 index 0000000..54c5b17 --- /dev/null +++ b/include/QxSerialize/QxDump.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_DUMP_H_ +#define _QX_DUMP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxDump.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Dump all classes registered into QxOrm context using XML and JSON serialization engine + */ + +#include + +#include +#include +#include + +#include + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +namespace qx +{ + + /*! + * \ingroup QxSerialize + * \brief qx::dump(const T & t, bool bJsonFormat) : dump class of type T registered into QxOrm context using XML and JSON serialization engine + */ + template + void dump(const T &t, bool bJsonFormat = false) + { + QString sDump; + + if (bJsonFormat) + { +#ifndef _QX_NO_JSON + sDump = qx::serialization::json::to_string(t); +#else // _QX_NO_JSON + sDump = "Unable to dump element in JSON format : you must work with Qt5 and not define _QX_NO_JSON compilation option in 'QxOrm.pri' configuration file"; +#endif // _QX_NO_JSON + } + else + { +#if _QX_SERIALIZE_POLYMORPHIC + sDump = qx::serialization::polymorphic_xml::to_string(t); +#elif _QX_SERIALIZE_XML + sDump = qx::serialization::xml::to_string(t); +#elif _QX_SERIALIZE_WIDE_XML + sDump = qx::serialization::wide::xml::to_string(t); +#else // _QX_SERIALIZE_POLYMORPHIC + sDump = "Unable to dump element : you must define '_QX_ENABLE_BOOST_SERIALIZATION' and '_QX_ENABLE_BOOST_SERIALIZATION_XML' (or '_QX_ENABLE_BOOST_SERIALIZATION_WIDE_XML') compilation options in 'QxOrm.pri' configuration file"; +#endif // _QX_SERIALIZE_POLYMORPHIC + } + + QString sName = qx::QxClassName::get(); + qDebug("[QxOrm] start dump '%s'", qPrintable(sName)); + qDebug("%s", qPrintable(sDump)); + qDebug("[QxOrm] end dump '%s'", qPrintable(sName)); + } + +} // namespace qx + +#else // _QX_ENABLE_BOOST_SERIALIZATION + +namespace qx +{ + + template + void dump(const T &t, bool bJsonFormat = true) + { +#ifdef _QX_NO_JSON + qDebug("[QxOrm] qx::dump() : %s", "not implemented when _QX_ENABLE_BOOST_SERIALIZATION compilation option is not defined (XML format) and _QX_NO_JSON compilation option is defined (JSON format)"); + Q_UNUSED(t); + Q_UNUSED(bJsonFormat); +#else // _QX_NO_JSON + if (!bJsonFormat) + { + return; + } + QString sDump = qx::serialization::json::to_string(t); + QString sName = qx::QxClassName::get(); + qDebug("[QxOrm] start dump '%s'", qPrintable(sName)); + qDebug("%s", qPrintable(sDump)); + qDebug("[QxOrm] end dump '%s'", qPrintable(sName)); +#endif // _QX_NO_JSON + } + +} // namespace qx + +#endif // _QX_ENABLE_BOOST_SERIALIZATION +#endif // _QX_DUMP_H_ diff --git a/include/QxSerialize/QxSerialize.h b/include/QxSerialize/QxSerialize.h new file mode 100644 index 0000000..a41e3cc --- /dev/null +++ b/include/QxSerialize/QxSerialize.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_H_ +#define _QX_SERIALIZE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerialize.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Include all files necessary to serialize most used classes defined into stl, boost and Qt libraries + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4189) +#endif // _MSC_VER + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* +#include +#include +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +#endif // _QX_SERIALIZE_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/QxSerializeCheckInstance.h b/include/QxSerialize/QxSerializeCheckInstance.h new file mode 100644 index 0000000..55285e5 --- /dev/null +++ b/include/QxSerialize/QxSerializeCheckInstance.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_CHECK_INSTANCE_H_ +#define _QX_SERIALIZE_CHECK_INSTANCE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeCheckInstance.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Check instance during serialization process to avoid infinite loop with circular references (using RAII) + */ + +#include +#include +#include + +namespace qx +{ + + class QxSqlRelationLinked; + class IxClass; + +} // namespace qx + +namespace qx +{ + namespace serialization + { + namespace helper + { + + /*! + * \ingroup QxSerialize + * \brief qx::serialization::helper::QxSerializeCheckInstance : check instance during serialization process to avoid infinite loop with circular references (using RAII) + */ + class QX_DLL_EXPORT QxSerializeCheckInstance + { + + public: + typedef QPair, QString> type_hierarchy; + + protected: + static QSet>> m_lstInstanceByThread; //!< List of all instances currently used by a serialization process + static QHash m_hashLevelByThread; //!< Manage how deep level is serialization process + static QHash m_hashHierarchyByThread; //!< Store current hierarchy used by serialization process + static QMutex m_mutex; //!< Mutex => qx::serialization::helper::QxSerializeCheckInstance is thread-safe + + qptrdiff m_pInstance; //!< Instance associated to this helper class + Qt::HANDLE m_lThreadId; //!< Thread id associated to this helper class + qx::IxClass *m_pClass; //!< Class associated to this helper class + + public: + QxSerializeCheckInstance(const void *pInstance, qx::IxClass *pClass); + virtual ~QxSerializeCheckInstance(); + + static bool contains(const void *pInstance, qx::IxClass *pClass); + static bool isRoot(); + + static type_hierarchy getHierarchy(); + static void setHierarchy(const type_hierarchy &hierarchy); + }; + + } // namespace helper + } // namespace serialization +} // namespace qx + +#endif // _QX_SERIALIZE_CHECK_INSTANCE_H_ diff --git a/include/QxSerialize/QxSerializeFastCompil.h b/include/QxSerialize/QxSerializeFastCompil.h new file mode 100644 index 0000000..1249151 --- /dev/null +++ b/include/QxSerialize/QxSerializeFastCompil.h @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_FAST_COMPIL_H_ +#define _QX_SERIALIZE_FAST_COMPIL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#include +#include +#include +#include +#include + +#if _QX_BUILDING_QX_ORM +#define QX_BOOST_EXPORT_SERIALIZATION_FAST_COMPIL_HPP(T) /* Nothing */ +#define QX_BOOST_EXPORT_SERIALIZATION_FAST_COMPIL_CPP(T) QX_BOOST_EXPORT_SERIALIZATION_CPP(T) +#else // _QX_BUILDING_QX_ORM +#define QX_BOOST_EXPORT_SERIALIZATION_FAST_COMPIL_HPP(T) QX_BOOST_EXPORT_SERIALIZATION_HPP(T) +#define QX_BOOST_EXPORT_SERIALIZATION_FAST_COMPIL_CPP(T) /* Nothing */ +#endif // _QX_BUILDING_QX_ORM + +#define QX_SERIALIZE_FAST_COMPIL_SAVE_HPP(dllImportExport, Archive, className) \ + dllImportExport void save(Archive &ar, const className &t, const unsigned int file_version); + +#define QX_SERIALIZE_FAST_COMPIL_LOAD_HPP(dllImportExport, Archive, className) \ + dllImportExport void load(Archive &ar, className &t, const unsigned int file_version); + +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_HPP(dllImportExport, Archive, className) \ + dllImportExport void serialize(Archive &ar, className &t, const unsigned int file_version); + +#define QX_SERIALIZE_FAST_COMPIL_SAVE_CPP(Archive, className) \ + void save(Archive &ar, const className &t, const unsigned int file_version) \ + { \ + qx_save(ar, t, file_version); \ + } + +#define QX_SERIALIZE_FAST_COMPIL_LOAD_CPP(Archive, className) \ + void load(Archive &ar, className &t, const unsigned int file_version) \ + { \ + qx_load(ar, t, file_version); \ + } + +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_CPP(Archive, className) \ + void serialize(Archive &ar, className &t, const unsigned int file_version) \ + { \ + qx_serialize(ar, t, file_version); \ + } + +#if _QX_SERIALIZE_POLYMORPHIC +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_POLYMORPHIC_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_HPP(dllImportExport, boost::archive::polymorphic_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_LOAD_HPP(dllImportExport, boost::archive::polymorphic_iarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_POLYMORPHIC_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_HPP(dllImportExport, boost::archive::polymorphic_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_HPP(dllImportExport, boost::archive::polymorphic_iarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_POLYMORPHIC_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_CPP(boost::archive::polymorphic_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_LOAD_CPP(boost::archive::polymorphic_iarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_POLYMORPHIC_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_CPP(boost::archive::polymorphic_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_CPP(boost::archive::polymorphic_iarchive, className) +#else // _QX_SERIALIZE_POLYMORPHIC +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_POLYMORPHIC_HPP(dllImportExport, className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_POLYMORPHIC_HPP(dllImportExport, className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_POLYMORPHIC_CPP(className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_POLYMORPHIC_CPP(className) /* Nothing */ +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_BINARY_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_HPP(dllImportExport, boost::archive::binary_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_LOAD_HPP(dllImportExport, boost::archive::binary_iarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_BINARY_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_HPP(dllImportExport, boost::archive::binary_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_HPP(dllImportExport, boost::archive::binary_iarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_BINARY_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_CPP(boost::archive::binary_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_LOAD_CPP(boost::archive::binary_iarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_BINARY_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_CPP(boost::archive::binary_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_CPP(boost::archive::binary_iarchive, className) +#else // _QX_SERIALIZE_BINARY +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_BINARY_HPP(dllImportExport, className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_BINARY_HPP(dllImportExport, className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_BINARY_CPP(className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_BINARY_CPP(className) /* Nothing */ +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_TEXT_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_HPP(dllImportExport, boost::archive::text_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_LOAD_HPP(dllImportExport, boost::archive::text_iarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_TEXT_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_HPP(dllImportExport, boost::archive::text_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_HPP(dllImportExport, boost::archive::text_iarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_TEXT_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_CPP(boost::archive::text_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_LOAD_CPP(boost::archive::text_iarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_TEXT_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_CPP(boost::archive::text_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_CPP(boost::archive::text_iarchive, className) +#else // _QX_SERIALIZE_TEXT +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_TEXT_HPP(dllImportExport, className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_TEXT_HPP(dllImportExport, className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_TEXT_CPP(className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_TEXT_CPP(className) /* Nothing */ +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_XML_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_HPP(dllImportExport, boost::archive::xml_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_LOAD_HPP(dllImportExport, boost::archive::xml_iarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_XML_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_HPP(dllImportExport, boost::archive::xml_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_HPP(dllImportExport, boost::archive::xml_iarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_XML_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_CPP(boost::archive::xml_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_LOAD_CPP(boost::archive::xml_iarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_XML_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_CPP(boost::archive::xml_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_CPP(boost::archive::xml_iarchive, className) +#else // _QX_SERIALIZE_XML +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_XML_HPP(dllImportExport, className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_XML_HPP(dllImportExport, className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_XML_CPP(className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_XML_CPP(className) /* Nothing */ +#endif // _QX_SERIALIZE_XML + +#if _QX_SERIALIZE_PORTABLE_BINARY +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_PORTABLE_BINARY_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_HPP(dllImportExport, eos::portable_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_LOAD_HPP(dllImportExport, eos::portable_iarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_PORTABLE_BINARY_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_HPP(dllImportExport, eos::portable_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_HPP(dllImportExport, eos::portable_iarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_PORTABLE_BINARY_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_CPP(eos::portable_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_LOAD_CPP(eos::portable_iarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_PORTABLE_BINARY_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_CPP(eos::portable_oarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_CPP(eos::portable_iarchive, className) +#else // _QX_SERIALIZE_PORTABLE_BINARY +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_PORTABLE_BINARY_HPP(dllImportExport, className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_PORTABLE_BINARY_HPP(dllImportExport, className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_PORTABLE_BINARY_CPP(className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_PORTABLE_BINARY_CPP(className) /* Nothing */ +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#if _QX_SERIALIZE_WIDE_BINARY +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_WIDE_BINARY_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_HPP(dllImportExport, boost::archive::binary_woarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_LOAD_HPP(dllImportExport, boost::archive::binary_wiarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_WIDE_BINARY_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_HPP(dllImportExport, boost::archive::binary_woarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_HPP(dllImportExport, boost::archive::binary_wiarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_WIDE_BINARY_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_CPP(boost::archive::binary_woarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_LOAD_CPP(boost::archive::binary_wiarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_WIDE_BINARY_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_CPP(boost::archive::binary_woarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_CPP(boost::archive::binary_wiarchive, className) +#else // _QX_SERIALIZE_WIDE_BINARY +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_WIDE_BINARY_HPP(dllImportExport, className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_WIDE_BINARY_HPP(dllImportExport, className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_WIDE_BINARY_CPP(className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_WIDE_BINARY_CPP(className) /* Nothing */ +#endif // _QX_SERIALIZE_WIDE_BINARY + +#if _QX_SERIALIZE_WIDE_TEXT +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_WIDE_TEXT_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_HPP(dllImportExport, boost::archive::text_woarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_LOAD_HPP(dllImportExport, boost::archive::text_wiarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_WIDE_TEXT_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_HPP(dllImportExport, boost::archive::text_woarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_HPP(dllImportExport, boost::archive::text_wiarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_WIDE_TEXT_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_CPP(boost::archive::text_woarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_LOAD_CPP(boost::archive::text_wiarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_WIDE_TEXT_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_CPP(boost::archive::text_woarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_CPP(boost::archive::text_wiarchive, className) +#else // _QX_SERIALIZE_WIDE_TEXT +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_WIDE_TEXT_HPP(dllImportExport, className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_WIDE_TEXT_HPP(dllImportExport, className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_WIDE_TEXT_CPP(className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_WIDE_TEXT_CPP(className) /* Nothing */ +#endif // _QX_SERIALIZE_WIDE_TEXT + +#if _QX_SERIALIZE_WIDE_XML +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_WIDE_XML_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_HPP(dllImportExport, boost::archive::xml_woarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_LOAD_HPP(dllImportExport, boost::archive::xml_wiarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_WIDE_XML_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_HPP(dllImportExport, boost::archive::xml_woarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_HPP(dllImportExport, boost::archive::xml_wiarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_WIDE_XML_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_CPP(boost::archive::xml_woarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_LOAD_CPP(boost::archive::xml_wiarchive, className) +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_WIDE_XML_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_CPP(boost::archive::xml_woarchive, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_IMPL_CPP(boost::archive::xml_wiarchive, className) +#else // _QX_SERIALIZE_WIDE_XML +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_WIDE_XML_HPP(dllImportExport, className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_WIDE_XML_HPP(dllImportExport, className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_WIDE_XML_CPP(className) /* Nothing */ +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_WIDE_XML_CPP(className) /* Nothing */ +#endif // _QX_SERIALIZE_WIDE_XML + +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_HPP(dllImportExport, className) \ + namespace boost \ + { \ + namespace serialization \ + { \ + \ + QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_POLYMORPHIC_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_BINARY_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_TEXT_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_XML_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_PORTABLE_BINARY_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_WIDE_BINARY_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_WIDE_TEXT_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_WIDE_XML_HPP(dllImportExport, className) \ + \ + template \ + inline void serialize(Archive &ar, className &t, const unsigned int file_version) \ + { \ + boost::serialization::split_free(ar, t, file_version); \ + } \ + } \ + } // namespace boost::serialization + +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_HPP(dllImportExport, className) \ + namespace boost \ + { \ + namespace serialization \ + { \ + \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_POLYMORPHIC_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_BINARY_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_TEXT_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_XML_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_PORTABLE_BINARY_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_WIDE_BINARY_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_WIDE_TEXT_HPP(dllImportExport, className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_WIDE_XML_HPP(dllImportExport, className) \ + } \ + } // namespace boost::serialization + +#define QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(className) \ + namespace boost \ + { \ + namespace serialization \ + { \ + \ + QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_POLYMORPHIC_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_BINARY_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_TEXT_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_XML_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_PORTABLE_BINARY_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_WIDE_BINARY_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_WIDE_TEXT_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_WIDE_XML_CPP(className) \ + } \ + } // namespace boost::serialization + +#define QX_SERIALIZE_FAST_COMPIL_SERIALIZE_CPP(className) \ + namespace boost \ + { \ + namespace serialization \ + { \ + \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_POLYMORPHIC_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_BINARY_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_TEXT_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_XML_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_PORTABLE_BINARY_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_WIDE_BINARY_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_WIDE_TEXT_CPP(className) \ + QX_SERIALIZE_FAST_COMPIL_SERIALIZE_WIDE_XML_CPP(className) \ + } \ + } // namespace boost::serialization + +#endif // _QX_SERIALIZE_FAST_COMPIL_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/QxSerializeInvoker.h b/include/QxSerialize/QxSerializeInvoker.h new file mode 100644 index 0000000..8d89ba4 --- /dev/null +++ b/include/QxSerialize/QxSerializeInvoker.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_INVOKER_H_ +#define _QX_SERIALIZE_INVOKER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) +#pragma warning(disable : 4094) +#endif // _MSC_VER + +#include +#include +#include + +#include +#include + +#include + +namespace qx +{ + namespace serialization + { + namespace detail + { + + template + struct base_class + { + template + static inline void save(Archive &ar, const T &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + const char *sTag = QxClass::getSingleton()->getNamePtr(); + ar << boost::serialization::make_nvp(sTag, boost::serialization::base_object(t)); + } + template + static inline void load(Archive &ar, T &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + const char *sTag = QxClass::getSingleton()->getNamePtr(); + ar >> boost::serialization::make_nvp(sTag, boost::serialization::base_object(t)); + } + }; + + template <> + struct base_class + { + template + static inline void save(Archive &ar, const T &t, const unsigned int file_version) + { + Q_UNUSED(ar); + Q_UNUSED(t); + Q_UNUSED(file_version); + } + template + static inline void load(Archive &ar, T &t, const unsigned int file_version) + { + Q_UNUSED(ar); + Q_UNUSED(t); + Q_UNUSED(file_version); + } + }; + + template + void save(Archive &ar, const T &t, const unsigned int file_version) + { + typedef typename qx::trait::get_base_class::type qx_type_base_class_tmp; + qx::serialization::detail::base_class::save(ar, t, file_version); + QxClass::getSingleton()->dataMemberX()->toArchive(&t, ar, file_version); + } + + template + void load(Archive &ar, T &t, const unsigned int file_version) + { + typedef typename qx::trait::get_base_class::type qx_type_base_class_tmp; + qx::serialization::detail::base_class::load(ar, t, file_version); + QxClass::getSingleton()->dataMemberX()->fromArchive(&t, ar, file_version); + } + + template + struct saver + { + static inline void invoke(Archive &ar, const T &t, const unsigned int file_version) + { + qx::serialization::detail::save(ar, t, file_version); + } + }; + + template + struct loader + { + static inline void invoke(Archive &ar, T &t, const unsigned int file_version) + { + qx::serialization::detail::load(ar, t, file_version); + } + }; + + } // namespace detail + } // namespace serialization +} // namespace qx + +#include "../../inl/QxSerialize/QxSerializeInvoker.inl" + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +#endif // _QX_SERIALIZE_INVOKER_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/QxSerializeMacro.h b/include/QxSerialize/QxSerializeMacro.h new file mode 100644 index 0000000..98904ca --- /dev/null +++ b/include/QxSerialize/QxSerializeMacro.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_MACRO_H_ +#define _QX_SERIALIZE_MACRO_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#define QX_ARCHIVE_NAMESPACE_FCT_IMPL(T, INPUT, OUTPUT) \ + \ + template \ + inline qx_bool to_file(const T &obj, const QString &sFileName, unsigned int flags = boost::archive::no_header) { return qx::QxArchive_ToFile::toFile(obj, sFileName, flags); }; \ + \ + template \ + inline qx_bool from_file(T &obj, const QString &sFileName, unsigned int flags = boost::archive::no_header) { return qx::QxArchive_FromFile::fromFile(obj, sFileName, flags); }; \ + \ + template \ + inline qx_bool to_file_compressed(const T &obj, const QString &sFileName, unsigned int flags = boost::archive::no_header, int iCompressionLevel = -1) { return qx::QxArchive_ToFileCompressed::toFileCompressed(obj, sFileName, flags, iCompressionLevel); }; \ + \ + template \ + inline qx_bool from_file_compressed(T &obj, const QString &sFileName, unsigned int flags = boost::archive::no_header) { return qx::QxArchive_FromFileCompressed::fromFileCompressed(obj, sFileName, flags); }; \ + \ + template \ + inline QString to_string(const T &obj, unsigned int flags = boost::archive::no_header) { return qx::QxArchive_ToString::toString(obj, flags); }; \ + \ + template \ + inline qx_bool from_string(T &obj, const QString &sString, unsigned int flags = boost::archive::no_header) { return qx::QxArchive_FromString::fromString(obj, sString, flags); }; \ + \ + template \ + inline QByteArray to_byte_array(const T &obj, typename qx::trait::archive_wide_traits::type_string *owner = NULL, unsigned int flags = boost::archive::no_header) { return qx::QxArchive_ToByteArray::toByteArray(obj, owner, flags); }; \ + \ + template \ + inline qx_bool from_byte_array(T &obj, const QByteArray &data, unsigned int flags = boost::archive::no_header) { return qx::QxArchive_FromByteArray::fromByteArray(obj, data, flags); }; + +#endif // _QX_SERIALIZE_MACRO_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/QxSerializeQDataStream.h b/include/QxSerialize/QxSerializeQDataStream.h new file mode 100644 index 0000000..35c3383 --- /dev/null +++ b/include/QxSerialize/QxSerializeQDataStream.h @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERIALIZE_QDATASTREAM_H_ +#define _QX_SERIALIZE_QDATASTREAM_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQDataStream.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Used when _QX_ENABLE_BOOST_SERIALIZATION compilation option is not defined to provide serialization engine with Qt QDataStream class + */ + +#include + +#include +#include + +#include + +namespace qx +{ + namespace serialization + { + + /*! + * \ingroup QxSerialize + * \brief serialize a class registered into QxOrm context using Qt QDataStream serialization engine + */ + namespace qt + { + + template + inline QByteArray to_byte_array(const T &obj, void *owner = NULL, unsigned int flags = 1 /* boost::archive::no_header */) + { + Q_UNUSED(flags); + Q_UNUSED(owner); + QByteArray ba; + QString err; + QDataStream stream((&ba), QIODevice::WriteOnly); + stream << (quint32)(9438); + try + { + stream << obj; + } + catch (const std::exception &e) + { + err = QString("serialization error '%ERR%'").replace("%ERR%", e.what()); + } + catch (...) + { + err = QString("serialization error '%ERR%'").replace("%ERR%", "unknown error"); + } + if (!err.isEmpty()) + { + qDebug("[QxOrm] qx::serialization::qt::to_byte_array() : %s", qPrintable(err)); + ba.clear(); + } + return ba; + } + + template + inline qx_bool from_byte_array(T &obj, const QByteArray &data, unsigned int flags = 1 /* boost::archive::no_header */) + { + Q_UNUSED(flags); + qx_bool result = false; + if (data.isEmpty()) + { + return qx_bool(false, "input binary data is empty"); + } + QDataStream stream(data); + quint32 magic = 0; + stream >> magic; + if (magic != 9438) + { + return qx_bool(false, "input binary data is not valid"); + } + try + { + stream >> obj; + result = true; + } + catch (const std::exception &e) + { + result.setDesc(QString("deserialization error '%ERR%'").replace("%ERR%", e.what())); + } + catch (...) + { + result.setDesc(QString("deserialization error '%ERR%'").replace("%ERR%", "unknown error")); + } + if (!result.getDesc().isEmpty()) + { + QString msg = result.getDesc(); + qDebug("[QxOrm] qx::serialization::qt::from_byte_array() : %s", qPrintable(msg)); + } + return result; + } + + template + inline QString to_string(const T &obj, unsigned int flags = 1 /* boost::archive::no_header */) + { + return qx::serialization::qt::to_byte_array(obj, NULL, flags).toBase64(); + } + + template + inline qx_bool from_string(T &obj, const QString &sString, unsigned int flags = 1 /* boost::archive::no_header */) + { + QByteArray data = QByteArray::fromBase64(sString.toLatin1()); + return qx::serialization::qt::from_byte_array(obj, data, flags); + } + + template + inline qx_bool to_file(const T &obj, const QString &sFileName, unsigned int flags = 1 /* boost::archive::no_header */) + { + QByteArray data = qx::serialization::qt::to_byte_array(obj, NULL, flags); + QFile file(sFileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + { + return qx_bool(false, "cannot open file : " + sFileName); + } + file.write(data); + file.close(); + return qx_bool(true); + } + + template + inline qx_bool from_file(T &obj, const QString &sFileName, unsigned int flags = 1 /* boost::archive::no_header */) + { + QFile file(sFileName); + if (!file.open(QIODevice::ReadOnly)) + { + return qx_bool(false, "cannot open file : " + sFileName); + } + QByteArray data = file.readAll(); + file.close(); + return qx::serialization::qt::from_byte_array(obj, data, flags); + } + + template + inline qx_bool to_file_compressed(const T &obj, const QString &sFileName, unsigned int flags = 1 /* boost::archive::no_header */, int iCompressionLevel = -1) + { + QByteArray data = qx::serialization::qt::to_byte_array(obj, NULL, flags); + QByteArray compressed = qCompress(data, iCompressionLevel); + QFile file(sFileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + { + return qx_bool(false, "cannot open file : " + sFileName); + } + file.write(compressed); + file.close(); + return qx_bool(true); + } + + template + inline qx_bool from_file_compressed(T &obj, const QString &sFileName, unsigned int flags = 1 /* boost::archive::no_header */) + { + QFile file(sFileName); + if (!file.open(QIODevice::ReadOnly)) + { + return qx_bool(false, "cannot open file : " + sFileName); + } + QByteArray data = file.readAll(); + file.close(); + QByteArray uncompressed = qUncompress(data); + return qx::serialization::qt::from_byte_array(obj, uncompressed, flags); + } + + } // namespace qt + } // namespace serialization +} // namespace qx + +#endif // _QX_SERIALIZE_QDATASTREAM_H_ diff --git a/include/QxSerialize/QxSerializeQJson.h b/include/QxSerialize/QxSerializeQJson.h new file mode 100644 index 0000000..3db4783 --- /dev/null +++ b/include/QxSerialize/QxSerializeQJson.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON +#ifndef _QX_SERIALIZE_QJSON_H_ +#define _QX_SERIALIZE_QJSON_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSerializeQJson.h + * \author XDL Team + * \ingroup QxSerialize + * \brief Provide a serialization engine with Qt QJson classes (this feature requires Qt5) + */ + +#include +#include +#include +#include +#include + +#include + +#include + +namespace qx +{ + namespace serialization + { + + /*! + * \ingroup QxSerialize + * \brief serialize a class registered into QxOrm context using Qt QJson serialization engine (this feature requires Qt5) + */ + namespace json + { + + template + inline QByteArray to_byte_array(const T &obj, void *owner = NULL, unsigned int flags = 1 /* boost::archive::no_header */, const QString &format = QString()) + { + Q_UNUSED(flags); + Q_UNUSED(owner); + QJsonValue val = qx::cvt::to_json(obj, format); + QJsonDocument doc = (val.isArray() ? QJsonDocument(val.toArray()) : QJsonDocument(val.toObject())); + return doc.toJson(); + } + + template + inline qx_bool from_byte_array(T &obj, const QByteArray &data, unsigned int flags = 1 /* boost::archive::no_header */, const QString &format = QString()) + { + Q_UNUSED(flags); + QJsonParseError err; + QJsonDocument doc = QJsonDocument::fromJson(data, (&err)); + if (err.error != QJsonParseError::NoError) + { + return qx_bool(false, static_cast(err.error), err.errorString()); + } + QJsonValue val = (doc.isArray() ? QJsonValue(doc.array()) : QJsonValue(doc.object())); + return qx::cvt::from_json(val, obj, format); + } + + template + inline QString to_string(const T &obj, unsigned int flags = 1 /* boost::archive::no_header */, const QString &format = QString()) + { + return QString::fromUtf8(qx::serialization::json::to_byte_array(obj, NULL, flags, format)); + } + + template + inline qx_bool from_string(T &obj, const QString &sString, unsigned int flags = 1 /* boost::archive::no_header */, const QString &format = QString()) + { + return qx::serialization::json::from_byte_array(obj, sString.toUtf8(), flags, format); + } + + template + inline qx_bool to_file(const T &obj, const QString &sFileName, unsigned int flags = 1 /* boost::archive::no_header */, const QString &format = QString()) + { + QByteArray data = qx::serialization::json::to_byte_array(obj, NULL, flags, format); + QFile file(sFileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + { + return qx_bool(false, "cannot open file : " + sFileName); + } + file.write(data); + file.close(); + return qx_bool(true); + } + + template + inline qx_bool from_file(T &obj, const QString &sFileName, unsigned int flags = 1 /* boost::archive::no_header */, const QString &format = QString()) + { + QFile file(sFileName); + if (!file.open(QIODevice::ReadOnly)) + { + return qx_bool(false, "cannot open file : " + sFileName); + } + QByteArray data = file.readAll(); + file.close(); + return qx::serialization::json::from_byte_array(obj, data, flags, format); + } + + template + inline qx_bool to_file_compressed(const T &obj, const QString &sFileName, unsigned int flags = 1 /* boost::archive::no_header */, int iCompressionLevel = -1, const QString &format = QString()) + { + QByteArray data = qx::serialization::json::to_byte_array(obj, NULL, flags, format); + QByteArray compressed = qCompress(data, iCompressionLevel); + QFile file(sFileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + { + return qx_bool(false, "cannot open file : " + sFileName); + } + file.write(compressed); + file.close(); + return qx_bool(true); + } + + template + inline qx_bool from_file_compressed(T &obj, const QString &sFileName, unsigned int flags = 1 /* boost::archive::no_header */, const QString &format = QString()) + { + QFile file(sFileName); + if (!file.open(QIODevice::ReadOnly)) + { + return qx_bool(false, "cannot open file : " + sFileName); + } + QByteArray data = file.readAll(); + file.close(); + QByteArray uncompressed = qUncompress(data); + return qx::serialization::json::from_byte_array(obj, uncompressed, flags, format); + } + + } // namespace json + } // namespace serialization +} // namespace qx + +#endif // _QX_SERIALIZE_QJSON_H_ +#endif // _QX_NO_JSON diff --git a/include/QxSerialize/boost/QxExportDllBoostArchive.h b/include/QxSerialize/boost/QxExportDllBoostArchive.h new file mode 100644 index 0000000..59d120a --- /dev/null +++ b/include/QxSerialize/boost/QxExportDllBoostArchive.h @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_EXPORT_DLL_BOOST_ARCHIVE_H_ +#define _QX_EXPORT_DLL_BOOST_ARCHIVE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#if _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON +#if (!_QX_BUILDING_QX_ORM) + +#include + +#if (BOOST_VERSION > 103800) +#define QX_BOOST_EXPORT_ARCHIVE_SERIALIZER_MAP_SINGLETON_HPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_HPP(class, boost::serialization::singleton, boost::archive::detail::archive_serializer_map, Archive) +#else // (BOOST_VERSION > 103800) +#define QX_BOOST_EXPORT_ARCHIVE_SERIALIZER_MAP_SINGLETON_HPP(Archive) /* Nothing */ +#endif // (BOOST_VERSION > 103800) + +#if (BOOST_VERSION > 103800) +#define QX_BOOST_EXPORT_ARCHIVE_SERIALIZER_MAP_HPP(ArchiveIn, ArchiveOut) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(class, boost::archive::detail::archive_serializer_map, ArchiveIn) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(class, boost::archive::detail::archive_serializer_map, ArchiveOut) +#else // (BOOST_VERSION > 103800) +#define QX_BOOST_EXPORT_ARCHIVE_SERIALIZER_MAP_HPP(ArchiveIn, ArchiveOut) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(class, boost::archive::detail::archive_pointer_iserializer, ArchiveIn) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(class, boost::archive::detail::archive_pointer_oserializer, ArchiveOut) +#endif // (BOOST_VERSION > 103800) + +#if (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_SAVE_POINTER_TYPE_HPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, boost::archive::detail::save_pointer_type, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_HPP(class, boost::serialization::singleton, boost::archive::detail::save_pointer_type, Archive) +#else // (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_SAVE_POINTER_TYPE_HPP(Archive) /* Nothing */ +#endif // (BOOST_VERSION >= 104100) + +#if (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_SAVE_NON_POINTER_TYPE_HPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, boost::archive::detail::save_non_pointer_type, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_HPP(class, boost::serialization::singleton, boost::archive::detail::save_non_pointer_type, Archive) +#else // (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_SAVE_NON_POINTER_TYPE_HPP(Archive) /* Nothing */ +#endif // (BOOST_VERSION >= 104100) + +#if (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_SAVE_ENUM_TYPE_HPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, boost::archive::detail::save_enum_type, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_HPP(class, boost::serialization::singleton, boost::archive::detail::save_enum_type, Archive) +#else // (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_SAVE_ENUM_TYPE_HPP(Archive) /* Nothing */ +#endif // (BOOST_VERSION >= 104100) + +#if (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_SAVE_ARRAY_TYPE_HPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, boost::archive::detail::save_array_type, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_HPP(class, boost::serialization::singleton, boost::archive::detail::save_array_type, Archive) +#else // (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_SAVE_ARRAY_TYPE_HPP(Archive) /* Nothing */ +#endif // (BOOST_VERSION >= 104100) + +#if (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_LOAD_POINTER_TYPE_HPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, boost::archive::detail::load_pointer_type, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_HPP(class, boost::serialization::singleton, boost::archive::detail::load_pointer_type, Archive) +#else // (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_LOAD_POINTER_TYPE_HPP(Archive) /* Nothing */ +#endif // (BOOST_VERSION >= 104100) + +#if (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_LOAD_NON_POINTER_TYPE_HPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, boost::archive::detail::load_non_pointer_type, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_HPP(class, boost::serialization::singleton, boost::archive::detail::load_non_pointer_type, Archive) +#else // (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_LOAD_NON_POINTER_TYPE_HPP(Archive) /* Nothing */ +#endif // (BOOST_VERSION >= 104100) + +#if (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_LOAD_ENUM_TYPE_HPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, boost::archive::detail::load_enum_type, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_HPP(class, boost::serialization::singleton, boost::archive::detail::load_enum_type, Archive) +#else // (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_LOAD_ENUM_TYPE_HPP(Archive) /* Nothing */ +#endif // (BOOST_VERSION >= 104100) + +#if (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_LOAD_ARRAY_TYPE_HPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, boost::archive::detail::load_array_type, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_HPP(class, boost::serialization::singleton, boost::archive::detail::load_array_type, Archive) +#else // (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_LOAD_ARRAY_TYPE_HPP(Archive) /* Nothing */ +#endif // (BOOST_VERSION >= 104100) + +#define QX_BOOST_EXPORT_ARCHIVE_INTERFACE_IARCHIVE_HPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(class, boost::archive::detail::interface_iarchive, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_HPP(class, boost::serialization::singleton, boost::archive::detail::interface_iarchive, Archive) + +#define QX_BOOST_EXPORT_ARCHIVE_INTERFACE_OARCHIVE_HPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(class, boost::archive::detail::interface_oarchive, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_HPP(class, boost::serialization::singleton, boost::archive::detail::interface_oarchive, Archive) + +#define QX_BOOST_EXPORT_ARCHIVE_COMMON_IARCHIVE_HPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(class, boost::archive::detail::common_iarchive, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_HPP(class, boost::serialization::singleton, boost::archive::detail::common_iarchive, Archive) + +#define QX_BOOST_EXPORT_ARCHIVE_COMMON_OARCHIVE_HPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(class, boost::archive::detail::common_oarchive, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_HPP(class, boost::serialization::singleton, boost::archive::detail::common_oarchive, Archive) + +#define QX_BOOST_EXPORT_ARCHIVE_INTERFACE_HPP(ArchiveIn, ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_INTERFACE_IARCHIVE_HPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_INTERFACE_OARCHIVE_HPP(ArchiveOut) + +#define QX_BOOST_EXPORT_ARCHIVE_COMMON_HPP(ArchiveIn, ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_COMMON_IARCHIVE_HPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_COMMON_OARCHIVE_HPP(ArchiveOut) + +#define QX_BOOST_EXPORT_ARCHIVE_HPP(ArchiveIn, ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_SERIALIZER_MAP_SINGLETON_HPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_SERIALIZER_MAP_SINGLETON_HPP(ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_SAVE_NON_POINTER_TYPE_HPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_SAVE_NON_POINTER_TYPE_HPP(ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_SAVE_POINTER_TYPE_HPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_SAVE_POINTER_TYPE_HPP(ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_SAVE_ENUM_TYPE_HPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_SAVE_ENUM_TYPE_HPP(ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_SAVE_ARRAY_TYPE_HPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_SAVE_ARRAY_TYPE_HPP(ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_LOAD_NON_POINTER_TYPE_HPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_LOAD_NON_POINTER_TYPE_HPP(ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_LOAD_POINTER_TYPE_HPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_LOAD_POINTER_TYPE_HPP(ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_LOAD_ENUM_TYPE_HPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_LOAD_ENUM_TYPE_HPP(ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_LOAD_ARRAY_TYPE_HPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_LOAD_ARRAY_TYPE_HPP(ArchiveOut) + +#define QX_BOOST_EXPORT_ARCHIVE_ALL_HPP(ArchiveIn, ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_HPP(ArchiveIn, ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_INTERFACE_HPP(ArchiveIn, ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_COMMON_HPP(ArchiveIn, ArchiveOut) + +#if _QX_SERIALIZE_POLYMORPHIC +QX_BOOST_EXPORT_ARCHIVE_HPP(boost::archive::polymorphic_iarchive, boost::archive::polymorphic_oarchive) +QX_BOOST_EXPORT_ARCHIVE_HPP(boost::archive::polymorphic_binary_iarchive, boost::archive::polymorphic_binary_oarchive) +QX_BOOST_EXPORT_ARCHIVE_HPP(boost::archive::polymorphic_text_iarchive, boost::archive::polymorphic_text_oarchive) +QX_BOOST_EXPORT_ARCHIVE_HPP(boost::archive::polymorphic_xml_iarchive, boost::archive::polymorphic_xml_oarchive) +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY +QX_BOOST_EXPORT_ARCHIVE_ALL_HPP(boost::archive::binary_iarchive, boost::archive::binary_oarchive) +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT +QX_BOOST_EXPORT_ARCHIVE_ALL_HPP(boost::archive::text_iarchive, boost::archive::text_oarchive) +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML +QX_BOOST_EXPORT_ARCHIVE_ALL_HPP(boost::archive::xml_iarchive, boost::archive::xml_oarchive) +#endif // _QX_SERIALIZE_XML + +#if _QX_SERIALIZE_PORTABLE_BINARY +QX_BOOST_EXPORT_ARCHIVE_SERIALIZER_MAP_HPP(eos::portable_iarchive, eos::portable_oarchive) +QX_BOOST_EXPORT_ARCHIVE_ALL_HPP(eos::portable_iarchive, eos::portable_oarchive) +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#if _QX_SERIALIZE_WIDE_BINARY +QX_BOOST_EXPORT_ARCHIVE_ALL_HPP(boost::archive::binary_wiarchive, boost::archive::binary_woarchive) +#endif // _QX_SERIALIZE_WIDE_BINARY + +#if _QX_SERIALIZE_WIDE_TEXT +QX_BOOST_EXPORT_ARCHIVE_ALL_HPP(boost::archive::text_wiarchive, boost::archive::text_woarchive) +#endif // _QX_SERIALIZE_WIDE_TEXT + +#if _QX_SERIALIZE_WIDE_XML +QX_BOOST_EXPORT_ARCHIVE_ALL_HPP(boost::archive::xml_wiarchive, boost::archive::xml_woarchive) +#endif // _QX_SERIALIZE_WIDE_XML + +#if _QX_USE_SERIALIZE_POLYMORPHIC_PATCH +namespace boost +{ + namespace archive + { + template <> + inline void xml_iarchive_impl::load(std::basic_string, std::allocator> &s) + { + Q_UNUSED(s); + qAssert(false); + } + template <> + inline void xml_oarchive_impl::save(std::basic_string, std::allocator> const &s) + { + Q_UNUSED(s); + qAssert(false); + } + } +} // namespace boost::archive +#endif // _QX_USE_SERIALIZE_POLYMORPHIC_PATCH + +#endif // (! _QX_BUILDING_QX_ORM) +#endif // _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON +#endif // _QX_EXPORT_DLL_BOOST_ARCHIVE_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/boost/QxExportDllMacroCpp.h b/include/QxSerialize/boost/QxExportDllMacroCpp.h new file mode 100644 index 0000000..8eacd98 --- /dev/null +++ b/include/QxSerialize/boost/QxExportDllMacroCpp.h @@ -0,0 +1,252 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_EXPORT_DLL_MACRO_CPP_H_ +#define _QX_EXPORT_DLL_MACRO_CPP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#include + +#if _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON + +#if (BOOST_VERSION < 106600) +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_ISERIALIZER_CPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_CPP(class, boost::archive::detail::pointer_iserializer, ArchiveIn, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_CPP(class, boost::serialization::singleton, boost::archive::detail::pointer_iserializer, ArchiveIn, T) + +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_OSERIALIZER_CPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_CPP(class, boost::archive::detail::pointer_oserializer, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_CPP(class, boost::serialization::singleton, boost::archive::detail::pointer_oserializer, ArchiveOut, T) +#else // (BOOST_VERSION < 106600) +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_ISERIALIZER_CPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_CPP(class, boost::archive::detail::pointer_iserializer, ArchiveIn, T) + +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_OSERIALIZER_CPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_CPP(class, boost::archive::detail::pointer_oserializer, ArchiveOut, T) +#endif // (BOOST_VERSION < 106600) + +#if _QX_SUPPORT_BOOST_SERIALIZE_SHARED_PTR_132 +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_ISERIALIZER_BOOST_132_HELPER_CPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_CPP(class, boost::archive::detail::pointer_iserializer, ArchiveIn, QX_BOOST_132_SHARED_PTR_HELPER(T)) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_CPP(class, boost::serialization::singleton, boost::archive::detail::pointer_iserializer, ArchiveIn, QX_BOOST_132_SHARED_PTR_HELPER(T)) +#else // _QX_SUPPORT_BOOST_SERIALIZE_SHARED_PTR_132 +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_ISERIALIZER_BOOST_132_HELPER_CPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // _QX_SUPPORT_BOOST_SERIALIZE_SHARED_PTR_132 + +#if _QX_SUPPORT_BOOST_SERIALIZE_SHARED_PTR_132 +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_OSERIALIZER_BOOST_132_HELPER_CPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_CPP(class, boost::archive::detail::pointer_oserializer, ArchiveOut, QX_BOOST_132_SHARED_PTR_HELPER(T)) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_CPP(class, boost::serialization::singleton, boost::archive::detail::pointer_oserializer, ArchiveOut, QX_BOOST_132_SHARED_PTR_HELPER(T)) +#else // _QX_SUPPORT_BOOST_SERIALIZE_SHARED_PTR_132 +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_OSERIALIZER_BOOST_132_HELPER_CPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // _QX_SUPPORT_BOOST_SERIALIZE_SHARED_PTR_132 + +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_ISERIALIZER_CPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_CPP(class, boost::archive::detail::iserializer, ArchiveIn, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_CPP(class, boost::serialization::singleton, boost::archive::detail::iserializer, ArchiveIn, T) + +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_OSERIALIZER_CPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_CPP(class, boost::archive::detail::oserializer, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_CPP(class, boost::serialization::singleton, boost::archive::detail::oserializer, ArchiveOut, T) + +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_EXTENDED_TYPE_ID_CPP(T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(class, boost::serialization::extended_type_info_typeid, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_CPP(class, boost::serialization::singleton, boost::serialization::extended_type_info_typeid, T) + +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_VOID_CAST_PRIMITIVE_CPP(T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_CPP(class, boost::serialization::void_cast_detail::void_caster_primitive, T, QX_GET_BASE_CLASS_2(T)) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_CPP(class, boost::serialization::singleton, boost::serialization::void_cast_detail::void_caster_primitive, T, QX_GET_BASE_CLASS_2(T)) + +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_TYPE_INFO_IMPL_CPP(T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, boost::serialization::type_info_implementation, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_CPP(class, boost::serialization::singleton, boost::serialization::type_info_implementation, T) + +#if (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_SAVE_POINTER_TYPE_CPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_CPP(struct, boost::archive::detail::save_pointer_type, ArchiveOut, T *) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_CPP(class, boost::serialization::singleton, boost::archive::detail::save_pointer_type, ArchiveOut, T *) +#else // (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_SAVE_POINTER_TYPE_CPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // (BOOST_VERSION < 104100) + +#if (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_SAVE_NON_POINTER_TYPE_CPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_CPP(struct, boost::archive::detail::save_non_pointer_type, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_CPP(class, boost::serialization::singleton, boost::archive::detail::save_non_pointer_type, ArchiveOut, T) +#else // (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_SAVE_NON_POINTER_TYPE_CPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // (BOOST_VERSION < 104100) + +#if (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_SAVE_ENUM_TYPE_CPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_CPP(struct, boost::archive::detail::save_enum_type, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_CPP(class, boost::serialization::singleton, boost::archive::detail::save_enum_type, ArchiveOut, T) +#else // (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_SAVE_ENUM_TYPE_CPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // (BOOST_VERSION < 104100) + +#if (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_SAVE_ARRAY_TYPE_CPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_CPP(struct, boost::archive::detail::save_array_type, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_CPP(class, boost::serialization::singleton, boost::archive::detail::save_array_type, ArchiveOut, T) +#else // (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_SAVE_ARRAY_TYPE_CPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // (BOOST_VERSION < 104100) + +#if (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_LOAD_POINTER_TYPE_CPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_CPP(struct, boost::archive::detail::load_pointer_type, ArchiveIn, T *) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_CPP(class, boost::serialization::singleton, boost::archive::detail::load_pointer_type, ArchiveIn, T *) +#else // (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_LOAD_POINTER_TYPE_CPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // (BOOST_VERSION < 104100) + +#if (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_LOAD_NON_POINTER_TYPE_CPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_CPP(struct, boost::archive::detail::load_non_pointer_type, ArchiveIn, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_CPP(class, boost::serialization::singleton, boost::archive::detail::load_non_pointer_type, ArchiveIn, T) +#else // (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_LOAD_NON_POINTER_TYPE_CPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // (BOOST_VERSION < 104100) + +#if (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_LOAD_ENUM_TYPE_CPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_CPP(struct, boost::archive::detail::load_enum_type, ArchiveIn, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_CPP(class, boost::serialization::singleton, boost::archive::detail::load_enum_type, ArchiveIn, T) +#else // (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_LOAD_ENUM_TYPE_CPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // (BOOST_VERSION < 104100) + +#if (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_LOAD_ARRAY_TYPE_CPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_CPP(struct, boost::archive::detail::load_array_type, ArchiveIn, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_CPP(class, boost::serialization::singleton, boost::archive::detail::load_array_type, ArchiveIn, T) +#else // (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_LOAD_ARRAY_TYPE_CPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // (BOOST_VERSION < 104100) + +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_WITH_ARCHIVE_CPP(ArchiveIn, ArchiveOut, T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_ISERIALIZER_CPP(ArchiveIn, ArchiveOut, T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_OSERIALIZER_CPP(ArchiveIn, ArchiveOut, T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_ISERIALIZER_CPP(ArchiveIn, ArchiveOut, T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_OSERIALIZER_CPP(ArchiveIn, ArchiveOut, T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_ISERIALIZER_BOOST_132_HELPER_CPP(ArchiveIn, ArchiveOut, T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_OSERIALIZER_BOOST_132_HELPER_CPP(ArchiveIn, ArchiveOut, T) \ + QX_BOOST_EXPORT_SERIALIZATION_SAVE_POINTER_TYPE_CPP(ArchiveIn, ArchiveOut, T) \ + QX_BOOST_EXPORT_SERIALIZATION_LOAD_POINTER_TYPE_CPP(ArchiveIn, ArchiveOut, T) + +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_CPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_EXTENDED_TYPE_ID_CPP(T) \ + /* \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_VOID_CAST_PRIMITIVE_CPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_TYPE_INFO_IMPL_CPP(T) \ + */ + +#if _QX_SERIALIZE_POLYMORPHIC +#define QX_BOOST_EXPORT_SERIALIZATION_POLYMORPHIC_CPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_WITH_ARCHIVE_CPP(boost::archive::polymorphic_iarchive, boost::archive::polymorphic_oarchive, T) +#else // _QX_SERIALIZE_POLYMORPHIC +#define QX_BOOST_EXPORT_SERIALIZATION_POLYMORPHIC_CPP(T) /* Nothing */ +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY +#define QX_BOOST_EXPORT_SERIALIZATION_BINARY_CPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_WITH_ARCHIVE_CPP(boost::archive::binary_iarchive, boost::archive::binary_oarchive, T) +#else // _QX_SERIALIZE_BINARY +#define QX_BOOST_EXPORT_SERIALIZATION_BINARY_CPP(T) /* Nothing */ +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT +#define QX_BOOST_EXPORT_SERIALIZATION_TEXT_CPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_WITH_ARCHIVE_CPP(boost::archive::text_iarchive, boost::archive::text_oarchive, T) +#else // _QX_SERIALIZE_TEXT +#define QX_BOOST_EXPORT_SERIALIZATION_TEXT_CPP(T) /* Nothing */ +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML +#define QX_BOOST_EXPORT_SERIALIZATION_XML_CPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_WITH_ARCHIVE_CPP(boost::archive::xml_iarchive, boost::archive::xml_oarchive, T) +#else // _QX_SERIALIZE_XML +#define QX_BOOST_EXPORT_SERIALIZATION_XML_CPP(T) /* Nothing */ +#endif // _QX_SERIALIZE_XML + +#if _QX_SERIALIZE_PORTABLE_BINARY +#define QX_BOOST_EXPORT_SERIALIZATION_PORTABLE_BINARY_CPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_WITH_ARCHIVE_CPP(eos::portable_iarchive, eos::portable_oarchive, T) +#else // _QX_SERIALIZE_PORTABLE_BINARY +#define QX_BOOST_EXPORT_SERIALIZATION_PORTABLE_BINARY_CPP(T) /* Nothing */ +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#if _QX_SERIALIZE_WIDE_BINARY +#define QX_BOOST_EXPORT_SERIALIZATION_WIDE_BINARY_CPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_WITH_ARCHIVE_CPP(boost::archive::binary_wiarchive, boost::archive::binary_woarchive, T) +#else // _QX_SERIALIZE_WIDE_BINARY +#define QX_BOOST_EXPORT_SERIALIZATION_WIDE_BINARY_CPP(T) /* Nothing */ +#endif // _QX_SERIALIZE_WIDE_BINARY + +#if _QX_SERIALIZE_WIDE_TEXT +#define QX_BOOST_EXPORT_SERIALIZATION_WIDE_TEXT_CPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_WITH_ARCHIVE_CPP(boost::archive::text_wiarchive, boost::archive::text_woarchive, T) +#else // _QX_SERIALIZE_WIDE_TEXT +#define QX_BOOST_EXPORT_SERIALIZATION_WIDE_TEXT_CPP(T) /* Nothing */ +#endif // _QX_SERIALIZE_WIDE_TEXT + +#if _QX_SERIALIZE_WIDE_XML +#define QX_BOOST_EXPORT_SERIALIZATION_WIDE_XML_CPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_WITH_ARCHIVE_CPP(boost::archive::xml_wiarchive, boost::archive::xml_woarchive, T) +#else // _QX_SERIALIZE_WIDE_XML +#define QX_BOOST_EXPORT_SERIALIZATION_WIDE_XML_CPP(T) /* Nothing */ +#endif // _QX_SERIALIZE_WIDE_XML + +#define QX_BOOST_EXPORT_SERIALIZATION_CPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_CPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_POLYMORPHIC_CPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_BINARY_CPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_TEXT_CPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_XML_CPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_PORTABLE_BINARY_CPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_WIDE_BINARY_CPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_WIDE_TEXT_CPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_WIDE_XML_CPP(T) + +#else // _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON + +#define QX_BOOST_EXPORT_SERIALIZATION_CPP(T) /* Nothing */ + +#endif // _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON +#endif // _QX_EXPORT_DLL_MACRO_CPP_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/boost/QxExportDllMacroHpp.h b/include/QxSerialize/boost/QxExportDllMacroHpp.h new file mode 100644 index 0000000..ed302a3 --- /dev/null +++ b/include/QxSerialize/boost/QxExportDllMacroHpp.h @@ -0,0 +1,264 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_EXPORT_DLL_MACRO_HPP_H_ +#define _QX_EXPORT_DLL_MACRO_HPP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#include + +#include + +#if _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON + +#if _QX_SUPPORT_BOOST_SERIALIZE_SHARED_PTR_132 +#if (BOOST_VERSION >= 104100) +#define QX_BOOST_132_SHARED_PTR_HELPER(T) \ + boost_132::detail::sp_counted_base_impl +#else // (BOOST_VERSION >= 104100) +#define QX_BOOST_132_SHARED_PTR_HELPER(T) \ + boost_132::detail::sp_counted_base_impl +#endif // (BOOST_VERSION >= 104100) +#endif // _QX_SUPPORT_BOOST_SERIALIZE_SHARED_PTR_132 + +#if (BOOST_VERSION < 106600) +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_ISERIALIZER_HPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_HPP(class, boost::archive::detail::pointer_iserializer, ArchiveIn, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_HPP(class, boost::serialization::singleton, boost::archive::detail::pointer_iserializer, ArchiveIn, T) + +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_OSERIALIZER_HPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_HPP(class, boost::archive::detail::pointer_oserializer, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_HPP(class, boost::serialization::singleton, boost::archive::detail::pointer_oserializer, ArchiveOut, T) +#else // (BOOST_VERSION < 106600) +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_ISERIALIZER_HPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_HPP(class, boost::archive::detail::pointer_iserializer, ArchiveIn, T) + +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_OSERIALIZER_HPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_HPP(class, boost::archive::detail::pointer_oserializer, ArchiveOut, T) +#endif // (BOOST_VERSION < 106600) + +#if _QX_SUPPORT_BOOST_SERIALIZE_SHARED_PTR_132 +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_ISERIALIZER_BOOST_132_HELPER_HPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_HPP(class, boost::archive::detail::pointer_iserializer, ArchiveIn, QX_BOOST_132_SHARED_PTR_HELPER(T)) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_HPP(class, boost::serialization::singleton, boost::archive::detail::pointer_iserializer, ArchiveIn, QX_BOOST_132_SHARED_PTR_HELPER(T)) +#else // _QX_SUPPORT_BOOST_SERIALIZE_SHARED_PTR_132 +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_ISERIALIZER_BOOST_132_HELPER_HPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // _QX_SUPPORT_BOOST_SERIALIZE_SHARED_PTR_132 + +#if _QX_SUPPORT_BOOST_SERIALIZE_SHARED_PTR_132 +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_OSERIALIZER_BOOST_132_HELPER_HPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_HPP(class, boost::archive::detail::pointer_oserializer, ArchiveOut, QX_BOOST_132_SHARED_PTR_HELPER(T)) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_HPP(class, boost::serialization::singleton, boost::archive::detail::pointer_oserializer, ArchiveOut, QX_BOOST_132_SHARED_PTR_HELPER(T)) +#else // _QX_SUPPORT_BOOST_SERIALIZE_SHARED_PTR_132 +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_OSERIALIZER_BOOST_132_HELPER_HPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // _QX_SUPPORT_BOOST_SERIALIZE_SHARED_PTR_132 + +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_ISERIALIZER_HPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_HPP(class, boost::archive::detail::iserializer, ArchiveIn, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_HPP(class, boost::serialization::singleton, boost::archive::detail::iserializer, ArchiveIn, T) + +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_OSERIALIZER_HPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_HPP(class, boost::archive::detail::oserializer, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_HPP(class, boost::serialization::singleton, boost::archive::detail::oserializer, ArchiveOut, T) + +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_EXTENDED_TYPE_ID_HPP(T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(class, boost::serialization::extended_type_info_typeid, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_HPP(class, boost::serialization::singleton, boost::serialization::extended_type_info_typeid, T) + +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_VOID_CAST_PRIMITIVE_HPP(T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_HPP(class, boost::serialization::void_cast_detail::void_caster_primitive, T, QX_GET_BASE_CLASS_2(T)) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_HPP(class, boost::serialization::singleton, boost::serialization::void_cast_detail::void_caster_primitive, T, QX_GET_BASE_CLASS_2(T)) + +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_TYPE_INFO_IMPL_HPP(T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(struct, boost::serialization::type_info_implementation, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_HPP(class, boost::serialization::singleton, boost::serialization::type_info_implementation, T) + +#if (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_SAVE_POINTER_TYPE_HPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_HPP(struct, boost::archive::detail::save_pointer_type, ArchiveOut, T *) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_HPP(class, boost::serialization::singleton, boost::archive::detail::save_pointer_type, ArchiveOut, T *) +#else // (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_SAVE_POINTER_TYPE_HPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // (BOOST_VERSION < 104100) + +#if (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_SAVE_NON_POINTER_TYPE_HPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_HPP(struct, boost::archive::detail::save_non_pointer_type, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_HPP(class, boost::serialization::singleton, boost::archive::detail::save_non_pointer_type, ArchiveOut, T) +#else // (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_SAVE_NON_POINTER_TYPE_HPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // (BOOST_VERSION < 104100) + +#if (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_SAVE_ENUM_TYPE_HPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_HPP(struct, boost::archive::detail::save_enum_type, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_HPP(class, boost::serialization::singleton, boost::archive::detail::save_enum_type, ArchiveOut, T) +#else // (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_SAVE_ENUM_TYPE_HPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // (BOOST_VERSION < 104100) + +#if (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_SAVE_ARRAY_TYPE_HPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_HPP(struct, boost::archive::detail::save_array_type, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_HPP(class, boost::serialization::singleton, boost::archive::detail::save_array_type, ArchiveOut, T) +#else // (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_SAVE_ARRAY_TYPE_HPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // (BOOST_VERSION < 104100) + +#if (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_LOAD_POINTER_TYPE_HPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_HPP(struct, boost::archive::detail::load_pointer_type, ArchiveIn, T *) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_HPP(class, boost::serialization::singleton, boost::archive::detail::load_pointer_type, ArchiveIn, T *) +#else // (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_LOAD_POINTER_TYPE_HPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // (BOOST_VERSION < 104100) + +#if (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_LOAD_NON_POINTER_TYPE_HPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_HPP(struct, boost::archive::detail::load_non_pointer_type, ArchiveIn, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_HPP(class, boost::serialization::singleton, boost::archive::detail::load_non_pointer_type, ArchiveIn, T) +#else // (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_LOAD_NON_POINTER_TYPE_HPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // (BOOST_VERSION < 104100) + +#if (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_LOAD_ENUM_TYPE_HPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_HPP(struct, boost::archive::detail::load_enum_type, ArchiveIn, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_HPP(class, boost::serialization::singleton, boost::archive::detail::load_enum_type, ArchiveIn, T) +#else // (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_LOAD_ENUM_TYPE_HPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // (BOOST_VERSION < 104100) + +#if (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_LOAD_ARRAY_TYPE_HPP(ArchiveIn, ArchiveOut, T) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_P2_HPP(struct, boost::archive::detail::load_array_type, ArchiveIn, T) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_P2_HPP(class, boost::serialization::singleton, boost::archive::detail::load_array_type, ArchiveIn, T) +#else // (BOOST_VERSION < 104100) +#define QX_BOOST_EXPORT_SERIALIZATION_LOAD_ARRAY_TYPE_HPP(ArchiveIn, ArchiveOut, T) /* Nothing */ +#endif // (BOOST_VERSION < 104100) + +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_WITH_ARCHIVE_HPP(ArchiveIn, ArchiveOut, T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_ISERIALIZER_HPP(ArchiveIn, ArchiveOut, T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_OSERIALIZER_HPP(ArchiveIn, ArchiveOut, T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_ISERIALIZER_HPP(ArchiveIn, ArchiveOut, T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_OSERIALIZER_HPP(ArchiveIn, ArchiveOut, T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_ISERIALIZER_BOOST_132_HELPER_HPP(ArchiveIn, ArchiveOut, T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_POINTER_OSERIALIZER_BOOST_132_HELPER_HPP(ArchiveIn, ArchiveOut, T) \ + QX_BOOST_EXPORT_SERIALIZATION_SAVE_POINTER_TYPE_HPP(ArchiveIn, ArchiveOut, T) \ + QX_BOOST_EXPORT_SERIALIZATION_LOAD_POINTER_TYPE_HPP(ArchiveIn, ArchiveOut, T) + +#define QX_BOOST_EXPORT_SERIALIZATION_IMPL_HPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_EXTENDED_TYPE_ID_HPP(T) \ + /* \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_VOID_CAST_PRIMITIVE_HPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_TYPE_INFO_IMPL_HPP(T) \ + */ + +#if _QX_SERIALIZE_POLYMORPHIC +#define QX_BOOST_EXPORT_SERIALIZATION_POLYMORPHIC_HPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_WITH_ARCHIVE_HPP(boost::archive::polymorphic_iarchive, boost::archive::polymorphic_oarchive, T) +#else // _QX_SERIALIZE_POLYMORPHIC +#define QX_BOOST_EXPORT_SERIALIZATION_POLYMORPHIC_HPP(T) /* Nothing */ +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY +#define QX_BOOST_EXPORT_SERIALIZATION_BINARY_HPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_WITH_ARCHIVE_HPP(boost::archive::binary_iarchive, boost::archive::binary_oarchive, T) +#else // _QX_SERIALIZE_BINARY +#define QX_BOOST_EXPORT_SERIALIZATION_BINARY_HPP(T) /* Nothing */ +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT +#define QX_BOOST_EXPORT_SERIALIZATION_TEXT_HPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_WITH_ARCHIVE_HPP(boost::archive::text_iarchive, boost::archive::text_oarchive, T) +#else // _QX_SERIALIZE_TEXT +#define QX_BOOST_EXPORT_SERIALIZATION_TEXT_HPP(T) /* Nothing */ +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML +#define QX_BOOST_EXPORT_SERIALIZATION_XML_HPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_WITH_ARCHIVE_HPP(boost::archive::xml_iarchive, boost::archive::xml_oarchive, T) +#else // _QX_SERIALIZE_XML +#define QX_BOOST_EXPORT_SERIALIZATION_XML_HPP(T) /* Nothing */ +#endif // _QX_SERIALIZE_XML + +#if _QX_SERIALIZE_PORTABLE_BINARY +#define QX_BOOST_EXPORT_SERIALIZATION_PORTABLE_BINARY_HPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_WITH_ARCHIVE_HPP(eos::portable_iarchive, eos::portable_oarchive, T) +#else // _QX_SERIALIZE_PORTABLE_BINARY +#define QX_BOOST_EXPORT_SERIALIZATION_PORTABLE_BINARY_HPP(T) /* Nothing */ +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#if _QX_SERIALIZE_WIDE_BINARY +#define QX_BOOST_EXPORT_SERIALIZATION_WIDE_BINARY_HPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_WITH_ARCHIVE_HPP(boost::archive::binary_wiarchive, boost::archive::binary_woarchive, T) +#else // _QX_SERIALIZE_WIDE_BINARY +#define QX_BOOST_EXPORT_SERIALIZATION_WIDE_BINARY_HPP(T) /* Nothing */ +#endif // _QX_SERIALIZE_WIDE_BINARY + +#if _QX_SERIALIZE_WIDE_TEXT +#define QX_BOOST_EXPORT_SERIALIZATION_WIDE_TEXT_HPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_WITH_ARCHIVE_HPP(boost::archive::text_wiarchive, boost::archive::text_woarchive, T) +#else // _QX_SERIALIZE_WIDE_TEXT +#define QX_BOOST_EXPORT_SERIALIZATION_WIDE_TEXT_HPP(T) /* Nothing */ +#endif // _QX_SERIALIZE_WIDE_TEXT + +#if _QX_SERIALIZE_WIDE_XML +#define QX_BOOST_EXPORT_SERIALIZATION_WIDE_XML_HPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_WITH_ARCHIVE_HPP(boost::archive::xml_wiarchive, boost::archive::xml_woarchive, T) +#else // _QX_SERIALIZE_WIDE_XML +#define QX_BOOST_EXPORT_SERIALIZATION_WIDE_XML_HPP(T) /* Nothing */ +#endif // _QX_SERIALIZE_WIDE_XML + +#define QX_BOOST_EXPORT_SERIALIZATION_HPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_IMPL_HPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_POLYMORPHIC_HPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_BINARY_HPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_TEXT_HPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_XML_HPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_PORTABLE_BINARY_HPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_WIDE_BINARY_HPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_WIDE_TEXT_HPP(T) \ + QX_BOOST_EXPORT_SERIALIZATION_WIDE_XML_HPP(T) + +#else // _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON + +#define QX_BOOST_EXPORT_SERIALIZATION_HPP(T) /* Nothing */ + +#endif // _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON +#endif // _QX_EXPORT_DLL_MACRO_HPP_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/boost/QxImportDllBoostArchive.h b/include/QxSerialize/boost/QxImportDllBoostArchive.h new file mode 100644 index 0000000..cb4bc97 --- /dev/null +++ b/include/QxSerialize/boost/QxImportDllBoostArchive.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_IMPORT_DLL_BOOST_ARCHIVE_H_ +#define _QX_IMPORT_DLL_BOOST_ARCHIVE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#if _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON + +#if (BOOST_VERSION > 103800) +#define QX_BOOST_IMPORT_ARCHIVE_SERIALIZER_MAP_HPP(ArchiveIn, ArchiveOut) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(class, boost::archive::detail::archive_serializer_map, ArchiveIn) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(class, boost::archive::detail::archive_serializer_map, ArchiveOut) +#else // (BOOST_VERSION > 103800) +#define QX_BOOST_IMPORT_ARCHIVE_SERIALIZER_MAP_HPP(ArchiveIn, ArchiveOut) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(class, boost::archive::detail::archive_pointer_iserializer, ArchiveIn) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_HPP(class, boost::archive::detail::archive_pointer_oserializer, ArchiveOut) +#endif // (BOOST_VERSION > 103800) + +#if _QX_SERIALIZE_POLYMORPHIC +QX_BOOST_IMPORT_ARCHIVE_SERIALIZER_MAP_HPP(boost::archive::polymorphic_iarchive, boost::archive::polymorphic_oarchive) +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY +QX_BOOST_IMPORT_ARCHIVE_SERIALIZER_MAP_HPP(boost::archive::binary_iarchive, boost::archive::binary_oarchive) +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT +QX_BOOST_IMPORT_ARCHIVE_SERIALIZER_MAP_HPP(boost::archive::text_iarchive, boost::archive::text_oarchive) +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML +QX_BOOST_IMPORT_ARCHIVE_SERIALIZER_MAP_HPP(boost::archive::xml_iarchive, boost::archive::xml_oarchive) +#endif // _QX_SERIALIZE_XML + +#if _QX_SERIALIZE_WIDE_BINARY +QX_BOOST_IMPORT_ARCHIVE_SERIALIZER_MAP_HPP(boost::archive::binary_wiarchive, boost::archive::binary_woarchive) +#endif // _QX_SERIALIZE_WIDE_BINARY + +#if _QX_SERIALIZE_WIDE_TEXT +QX_BOOST_IMPORT_ARCHIVE_SERIALIZER_MAP_HPP(boost::archive::text_wiarchive, boost::archive::text_woarchive) +#endif // _QX_SERIALIZE_WIDE_TEXT + +#if _QX_SERIALIZE_WIDE_XML +QX_BOOST_IMPORT_ARCHIVE_SERIALIZER_MAP_HPP(boost::archive::xml_wiarchive, boost::archive::xml_woarchive) +#endif // _QX_SERIALIZE_WIDE_XML + +#endif // _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON +#endif // _QX_IMPORT_DLL_BOOST_ARCHIVE_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/boost/QxSerializeInclude.h b/include/QxSerialize/boost/QxSerializeInclude.h new file mode 100644 index 0000000..4dede56 --- /dev/null +++ b/include/QxSerialize/boost/QxSerializeInclude.h @@ -0,0 +1,195 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_INCLUDE_H_ +#define _QX_SERIALIZE_INCLUDE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4189) +#pragma warning(disable : 4996) +#endif // _MSC_VER + +#if _QX_SERIALIZE_POLYMORPHIC +#include +#include +#include +#include +#include +#include +#include +#include +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY +#include +#include +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT +#include +#include +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML +#include +#include +#endif // _QX_SERIALIZE_XML + +#if _QX_SERIALIZE_WIDE_BINARY +#include +#include +#endif // _QX_SERIALIZE_WIDE_BINARY + +#if _QX_SERIALIZE_WIDE_TEXT +#include +#include +#endif // _QX_SERIALIZE_WIDE_TEXT + +#if _QX_SERIALIZE_WIDE_XML +#include +#include +#endif // _QX_SERIALIZE_WIDE_XML + +#if _QX_SERIALIZE_PORTABLE_BINARY +#include +#include +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#ifndef _QX_BOOST_ARCHIVE_SERIALIZER_IMPL_DEFINED_ +#define _QX_BOOST_ARCHIVE_SERIALIZER_IMPL_DEFINED_ +#if (BOOST_VERSION > 103800) +#include +#else // (BOOST_VERSION > 103800) +#include +#include +#endif // (BOOST_VERSION > 103800) +#include +#include +#include +#include +#if _QX_SERIALIZE_TEXT +#include +#include +#include +#include +#endif // _QX_SERIALIZE_TEXT +#if (BOOST_VERSION > 103800) +#else // (BOOST_VERSION > 103800) +#if _QX_INCLUDE_BOOST_SERIALIZE_ARCHIVE_IMPL_IPP +#if _QX_SERIALIZE_TEXT +#include +#include +#endif // _QX_SERIALIZE_TEXT +#if _QX_SERIALIZE_XML +#include +#include +#endif // _QX_SERIALIZE_XML +#if _QX_SERIALIZE_WIDE_TEXT +#include +#include +#endif // _QX_SERIALIZE_WIDE_TEXT +#if _QX_SERIALIZE_WIDE_XML +#include +#include +#endif // _QX_SERIALIZE_WIDE_XML +#endif // _QX_INCLUDE_BOOST_SERIALIZE_ARCHIVE_IMPL_IPP +#endif // (BOOST_VERSION > 103800) +#endif // _QX_BOOST_ARCHIVE_SERIALIZER_IMPL_DEFINED_ + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +#ifndef QX_DEFAULT_ARCHIVE_INPUT // && QX_DEFAULT_ARCHIVE_OUTPUT +#if _QX_SERIALIZE_POLYMORPHIC +#define QX_DEFAULT_ARCHIVE_INPUT boost::archive::polymorphic_binary_iarchive +#define QX_DEFAULT_ARCHIVE_OUTPUT boost::archive::polymorphic_binary_oarchive +#elif _QX_SERIALIZE_BINARY +#define QX_DEFAULT_ARCHIVE_INPUT boost::archive::binary_iarchive +#define QX_DEFAULT_ARCHIVE_OUTPUT boost::archive::binary_oarchive +#elif _QX_SERIALIZE_TEXT +#define QX_DEFAULT_ARCHIVE_INPUT boost::archive::text_iarchive +#define QX_DEFAULT_ARCHIVE_OUTPUT boost::archive::text_oarchive +#elif _QX_SERIALIZE_XML +#define QX_DEFAULT_ARCHIVE_INPUT boost::archive::xml_iarchive +#define QX_DEFAULT_ARCHIVE_OUTPUT boost::archive::xml_oarchive +#elif _QX_SERIALIZE_PORTABLE_BINARY +#define QX_DEFAULT_ARCHIVE_INPUT eos::portable_iarchive +#define QX_DEFAULT_ARCHIVE_OUTPUT eos::portable_oarchive +#elif _QX_SERIALIZE_WIDE_BINARY +#define QX_DEFAULT_ARCHIVE_INPUT boost::archive::binary_wiarchive +#define QX_DEFAULT_ARCHIVE_OUTPUT boost::archive::binary_woarchive +#elif _QX_SERIALIZE_WIDE_TEXT +#define QX_DEFAULT_ARCHIVE_INPUT boost::archive::text_wiarchive +#define QX_DEFAULT_ARCHIVE_OUTPUT boost::archive::text_woarchive +#elif _QX_SERIALIZE_WIDE_XML +#define QX_DEFAULT_ARCHIVE_INPUT boost::archive::xml_wiarchive +#define QX_DEFAULT_ARCHIVE_OUTPUT boost::archive::xml_woarchive +#endif // _QX_SERIALIZE_BINARY +#endif // QX_DEFAULT_ARCHIVE_INPUT + +#ifndef QX_CLONE_STRING_STREAM // && QX_CLONE_BINARY_OUTPUT_ARCHIVE && QX_CLONE_BINARY_INPUT_ARCHIVE +#if _QX_SERIALIZE_POLYMORPHIC +#define QX_CLONE_STRING_STREAM std::stringstream +#define QX_CLONE_BINARY_OUTPUT_ARCHIVE boost::archive::polymorphic_binary_oarchive +#define QX_CLONE_BINARY_INPUT_ARCHIVE boost::archive::polymorphic_binary_iarchive +#elif _QX_SERIALIZE_WIDE_BINARY +#define QX_CLONE_STRING_STREAM std::wstringstream +#define QX_CLONE_BINARY_OUTPUT_ARCHIVE boost::archive::binary_woarchive +#define QX_CLONE_BINARY_INPUT_ARCHIVE boost::archive::binary_wiarchive +#elif _QX_SERIALIZE_BINARY +#define QX_CLONE_STRING_STREAM std::stringstream +#define QX_CLONE_BINARY_OUTPUT_ARCHIVE boost::archive::binary_oarchive +#define QX_CLONE_BINARY_INPUT_ARCHIVE boost::archive::binary_iarchive +#elif _QX_SERIALIZE_PORTABLE_BINARY +#define QX_CLONE_STRING_STREAM std::stringstream +#define QX_CLONE_BINARY_OUTPUT_ARCHIVE eos::portable_oarchive +#define QX_CLONE_BINARY_INPUT_ARCHIVE eos::portable_iarchive +#endif // _QX_SERIALIZE_WIDE_BINARY +#else +#define QX_CLONE_NEED_BINARY_TO_INCREASE_PERFORMANCE "qx::clone() need binary archive for best performance" +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__DMC__) +#pragma message(QX_CLONE_NEED_BINARY_TO_INCREASE_PERFORMANCE) +#elif defined(__GNUC__) || defined(__HP_aCC) || defined(__SUNPRO_CC) || defined(__IBMCPP__) +#warning QX_CLONE_NEED_BINARY_TO_INCREASE_PERFORMANCE +#endif // defined(_MSC_VER) || defined(__BORLANDC__) || defined(__DMC__) +#endif // QX_CLONE_STRING_STREAM + +#endif // _QX_SERIALIZE_INCLUDE_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/boost/QxSerialize_shared_ptr.h b/include/QxSerialize/boost/QxSerialize_shared_ptr.h new file mode 100644 index 0000000..77c2cbf --- /dev/null +++ b/include/QxSerialize/boost/QxSerialize_shared_ptr.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_BOOST_SHARED_PTR_H_ +#define _QX_SERIALIZE_BOOST_SHARED_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4100) +#endif // _MSC_VER + +#if _QX_SUPPORT_BOOST_SERIALIZE_SHARED_PTR_132 +#include +#else // _QX_SUPPORT_BOOST_SERIALIZE_SHARED_PTR_132 +#ifdef BOOST_SERIALIZATION_SHARED_PTR_132_HPP +#undef BOOST_SERIALIZATION_SHARED_PTR_132_HPP +#endif // BOOST_SERIALIZATION_SHARED_PTR_132_HPP +#endif // _QX_SUPPORT_BOOST_SERIALIZE_SHARED_PTR_132 + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +#endif // _QX_SERIALIZE_BOOST_SHARED_PTR_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/boost/QxSerialize_tuple.h b/include/QxSerialize/boost/QxSerialize_tuple.h new file mode 100644 index 0000000..751f344 --- /dev/null +++ b/include/QxSerialize/boost/QxSerialize_tuple.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_BOOST_TUPLE_H_ +#define _QX_SERIALIZE_BOOST_TUPLE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +namespace boost +{ + namespace serialization + { + + template + inline void serialize(Archive &ar, boost::tuple &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("tuple_1", boost::get<0>(t)); + ar &boost::serialization::make_nvp("tuple_2", boost::get<1>(t)); + } + + template + inline void serialize(Archive &ar, boost::tuple &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("tuple_1", boost::get<0>(t)); + ar &boost::serialization::make_nvp("tuple_2", boost::get<1>(t)); + ar &boost::serialization::make_nvp("tuple_3", boost::get<2>(t)); + } + + template + inline void serialize(Archive &ar, boost::tuple &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("tuple_1", boost::get<0>(t)); + ar &boost::serialization::make_nvp("tuple_2", boost::get<1>(t)); + ar &boost::serialization::make_nvp("tuple_3", boost::get<2>(t)); + ar &boost::serialization::make_nvp("tuple_4", boost::get<3>(t)); + } + + template + inline void serialize(Archive &ar, boost::tuple &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("tuple_1", boost::get<0>(t)); + ar &boost::serialization::make_nvp("tuple_2", boost::get<1>(t)); + ar &boost::serialization::make_nvp("tuple_3", boost::get<2>(t)); + ar &boost::serialization::make_nvp("tuple_4", boost::get<3>(t)); + ar &boost::serialization::make_nvp("tuple_5", boost::get<4>(t)); + } + + template + inline void serialize(Archive &ar, boost::tuple &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("tuple_1", boost::get<0>(t)); + ar &boost::serialization::make_nvp("tuple_2", boost::get<1>(t)); + ar &boost::serialization::make_nvp("tuple_3", boost::get<2>(t)); + ar &boost::serialization::make_nvp("tuple_4", boost::get<3>(t)); + ar &boost::serialization::make_nvp("tuple_5", boost::get<4>(t)); + ar &boost::serialization::make_nvp("tuple_6", boost::get<5>(t)); + } + + template + inline void serialize(Archive &ar, boost::tuple &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("tuple_1", boost::get<0>(t)); + ar &boost::serialization::make_nvp("tuple_2", boost::get<1>(t)); + ar &boost::serialization::make_nvp("tuple_3", boost::get<2>(t)); + ar &boost::serialization::make_nvp("tuple_4", boost::get<3>(t)); + ar &boost::serialization::make_nvp("tuple_5", boost::get<4>(t)); + ar &boost::serialization::make_nvp("tuple_6", boost::get<5>(t)); + ar &boost::serialization::make_nvp("tuple_7", boost::get<6>(t)); + } + + template + inline void serialize(Archive &ar, boost::tuple &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("tuple_1", boost::get<0>(t)); + ar &boost::serialization::make_nvp("tuple_2", boost::get<1>(t)); + ar &boost::serialization::make_nvp("tuple_3", boost::get<2>(t)); + ar &boost::serialization::make_nvp("tuple_4", boost::get<3>(t)); + ar &boost::serialization::make_nvp("tuple_5", boost::get<4>(t)); + ar &boost::serialization::make_nvp("tuple_6", boost::get<5>(t)); + ar &boost::serialization::make_nvp("tuple_7", boost::get<6>(t)); + ar &boost::serialization::make_nvp("tuple_8", boost::get<7>(t)); + } + + template + inline void serialize(Archive &ar, boost::tuple &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("tuple_1", boost::get<0>(t)); + ar &boost::serialization::make_nvp("tuple_2", boost::get<1>(t)); + ar &boost::serialization::make_nvp("tuple_3", boost::get<2>(t)); + ar &boost::serialization::make_nvp("tuple_4", boost::get<3>(t)); + ar &boost::serialization::make_nvp("tuple_5", boost::get<4>(t)); + ar &boost::serialization::make_nvp("tuple_6", boost::get<5>(t)); + ar &boost::serialization::make_nvp("tuple_7", boost::get<6>(t)); + ar &boost::serialization::make_nvp("tuple_8", boost::get<7>(t)); + ar &boost::serialization::make_nvp("tuple_9", boost::get<8>(t)); + } + + template + inline void serialize(Archive &ar, boost::tuple &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("tuple_1", boost::get<0>(t)); + ar &boost::serialization::make_nvp("tuple_2", boost::get<1>(t)); + ar &boost::serialization::make_nvp("tuple_3", boost::get<2>(t)); + ar &boost::serialization::make_nvp("tuple_4", boost::get<3>(t)); + ar &boost::serialization::make_nvp("tuple_5", boost::get<4>(t)); + ar &boost::serialization::make_nvp("tuple_6", boost::get<5>(t)); + ar &boost::serialization::make_nvp("tuple_7", boost::get<6>(t)); + ar &boost::serialization::make_nvp("tuple_8", boost::get<7>(t)); + ar &boost::serialization::make_nvp("tuple_9", boost::get<8>(t)); + ar &boost::serialization::make_nvp("tuple_10", boost::get<9>(t)); + } + + } // namespace boost +} // namespace serialization + +#endif // _QX_SERIALIZE_BOOST_TUPLE_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/boost/QxSerialize_unordered_map.h b/include/QxSerialize/boost/QxSerialize_unordered_map.h new file mode 100644 index 0000000..e8f0fc1 --- /dev/null +++ b/include/QxSerialize/boost/QxSerialize_unordered_map.h @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZATION_BOOST_UNORDERED_MAP_H_ +#define _QX_SERIALIZATION_BOOST_UNORDERED_MAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include +#include +#include + +namespace boost +{ + namespace serialization + { + +#if (BOOST_VERSION > 105700) + + template + inline void save(Archive &ar, const boost::unordered_map &t, const unsigned int /* file_version */) + { + long lSize = static_cast(t.size()); + ar << boost::serialization::make_nvp("size", lSize); + + typedef typename boost::unordered_map::const_iterator type_itr; + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + std::pair pair_key_value = std::make_pair(itr->first, itr->second); + ar << boost::serialization::make_nvp("item", pair_key_value); + } + } + + template + inline void load(Archive &ar, boost::unordered_map &t, const unsigned int /* file_version */) + { + long lSize = 0; + ar >> boost::serialization::make_nvp("size", lSize); + + t.clear(); + t.reserve(lSize); + std::pair pair_key_value; + + for (long l = 0; l < lSize; l++) + { + ar >> boost::serialization::make_nvp("item", pair_key_value); + t.insert(pair_key_value); + } + } + +#else // (BOOST_VERSION > 105700) + + template + inline void save(Archive &ar, const boost::unordered_map &t, const unsigned int /* file_version */) + { + boost::serialization::stl::save_collection>(ar, t); + } + + template + inline void load(Archive &ar, boost::unordered_map &t, const unsigned int /* file_version */) + { + boost::serialization::stl::load_collection, + boost::serialization::stl::archive_input_map>, + boost::serialization::stl::no_reserve_imp>>(ar, t); + } + +#endif // (BOOST_VERSION > 105700) + + template + inline void serialize(Archive &ar, boost::unordered_map &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + +#if (BOOST_VERSION > 105700) + + template + inline void save(Archive &ar, const boost::unordered_multimap &t, const unsigned int /* file_version */) + { + long lSize = static_cast(t.size()); + ar << boost::serialization::make_nvp("size", lSize); + + typedef typename boost::unordered_multimap::const_iterator type_itr; + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + std::pair pair_key_value = std::make_pair(itr->first, itr->second); + ar << boost::serialization::make_nvp("item", pair_key_value); + } + } + + template + inline void load(Archive &ar, boost::unordered_multimap &t, const unsigned int /* file_version */) + { + long lSize = 0; + ar >> boost::serialization::make_nvp("size", lSize); + + t.clear(); + t.reserve(lSize); + std::pair pair_key_value; + + for (long l = 0; l < lSize; l++) + { + ar >> boost::serialization::make_nvp("item", pair_key_value); + t.insert(pair_key_value); + } + } + +#else // (BOOST_VERSION > 105700) + + template + inline void save(Archive &ar, const boost::unordered_multimap &t, const unsigned int /* file_version */) + { + boost::serialization::stl::save_collection>(ar, t); + } + + template + inline void load(Archive &ar, boost::unordered_multimap &t, const unsigned int /* file_version */) + { +#if (BOOST_VERSION >= 104200) + boost::serialization::stl::load_collection, + boost::serialization::stl::archive_input_map>, + boost::serialization::stl::no_reserve_imp>>(ar, t); +#else // (BOOST_VERSION >= 104200) + boost::serialization::stl::load_collection, + boost::serialization::stl::archive_input_multimap>, + boost::serialization::stl::no_reserve_imp>>(ar, t); +#endif // (BOOST_VERSION >= 104200) + } + +#endif // (BOOST_VERSION > 105700) + + template + inline void serialize(Archive &ar, boost::unordered_multimap &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + } // namespace serialization +} // namespace boost + +#endif // _QX_SERIALIZATION_BOOST_UNORDERED_MAP_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/boost/QxSerialize_unordered_set.h b/include/QxSerialize/boost/QxSerialize_unordered_set.h new file mode 100644 index 0000000..de0ac87 --- /dev/null +++ b/include/QxSerialize/boost/QxSerialize_unordered_set.h @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZATION_BOOST_UNORDERED_SET_H_ +#define _QX_SERIALIZATION_BOOST_UNORDERED_SET_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include +#include +#include + +namespace boost +{ + namespace serialization + { + +#if (BOOST_VERSION > 105700) + + template + inline void save(Archive &ar, const boost::unordered_set &t, const unsigned int /* file_version */) + { + long lSize = static_cast(t.size()); + ar << boost::serialization::make_nvp("size", lSize); + + typedef typename boost::unordered_set::const_iterator type_itr; + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + ar << boost::serialization::make_nvp("item", (*itr)); + } + } + + template + inline void load(Archive &ar, boost::unordered_set &t, const unsigned int /* file_version */) + { + long lSize = 0; + ar >> boost::serialization::make_nvp("size", lSize); + + t.clear(); + t.reserve(lSize); + + for (long l = 0; l < lSize; l++) + { + Key item; + ar >> boost::serialization::make_nvp("item", item); + t.insert(item); + } + } + +#else // (BOOST_VERSION > 105700) + + template + inline void save(Archive &ar, const boost::unordered_set &t, const unsigned int /* file_version */) + { + boost::serialization::stl::save_collection>(ar, t); + } + + template + inline void load(Archive &ar, boost::unordered_set &t, const unsigned int /* file_version */) + { + boost::serialization::stl::load_collection, + boost::serialization::stl::archive_input_set>, + boost::serialization::stl::no_reserve_imp>>(ar, t); + } + +#endif // (BOOST_VERSION > 105700) + + template + inline void serialize(Archive &ar, boost::unordered_set &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + +#if (BOOST_VERSION > 105700) + + template + inline void save(Archive &ar, const boost::unordered_multiset &t, const unsigned int /* file_version */) + { + long lSize = static_cast(t.size()); + ar << boost::serialization::make_nvp("size", lSize); + + typedef typename boost::unordered_multiset::const_iterator type_itr; + for (type_itr itr = t.begin(); itr != t.end(); ++itr) + { + ar << boost::serialization::make_nvp("item", (*itr)); + } + } + + template + inline void load(Archive &ar, boost::unordered_multiset &t, const unsigned int /* file_version */) + { + long lSize = 0; + ar >> boost::serialization::make_nvp("size", lSize); + + t.clear(); + t.reserve(lSize); + + for (long l = 0; l < lSize; l++) + { + Key item; + ar >> boost::serialization::make_nvp("item", item); + t.insert(item); + } + } + +#else // (BOOST_VERSION > 105700) + + template + inline void save(Archive &ar, const boost::unordered_multiset &t, const unsigned int /* file_version */) + { + boost::serialization::stl::save_collection>(ar, t); + } + + template + inline void load(Archive &ar, boost::unordered_multiset &t, const unsigned int /* file_version */) + { +#if (BOOST_VERSION >= 104200) + boost::serialization::stl::load_collection, + boost::serialization::stl::archive_input_set>, + boost::serialization::stl::no_reserve_imp>>(ar, t); +#else // (BOOST_VERSION >= 104200) + boost::serialization::stl::load_collection, + boost::serialization::stl::archive_input_multiset>, + boost::serialization::stl::no_reserve_imp>>(ar, t); +#endif // (BOOST_VERSION >= 104200) + } + +#endif // (BOOST_VERSION > 105700) + + template + inline void serialize(Archive &ar, boost::unordered_multiset &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + } // namespace serialization +} // namespace boost + +#endif // _QX_SERIALIZATION_BOOST_UNORDERED_SET_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/boost/class_export/qx_boost_class_export.h b/include/QxSerialize/boost/class_export/qx_boost_class_export.h new file mode 100644 index 0000000..50a3af8 --- /dev/null +++ b/include/QxSerialize/boost/class_export/qx_boost_class_export.h @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_BOOST_SERIALIZATION_EXPORT_HPP_ +#define _QX_BOOST_SERIALIZATION_EXPORT_HPP_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#if _QX_USE_MODIFY_BOOST_SERIALIZATION_EXPORT_HPP + +#include +#include // NULL + +#include +#include + +#ifndef BOOST_SERIALIZATION_DEFAULT_TYPE_INFO +#include +#endif // BOOST_SERIALIZATION_DEFAULT_TYPE_INFO + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace boost +{ + namespace archive + { + namespace detail + { + + class basic_pointer_iserializer; + class basic_pointer_oserializer; + + template + class pointer_iserializer; + template + class pointer_oserializer; + + template + struct export_impl + { + static const basic_pointer_iserializer & + enable_load(mpl::true_) + { + return boost::serialization::singleton< + pointer_iserializer>::get_const_instance(); + } + + static const basic_pointer_oserializer & + enable_save(mpl::true_) + { + return boost::serialization::singleton< + pointer_oserializer>::get_const_instance(); + } + inline static void enable_load(mpl::false_) {} + inline static void enable_save(mpl::false_) {} + }; + + // On many platforms, naming a specialization of this template is + // enough to cause its argument to be instantiated. + template + struct instantiate_function + { + }; + + template + struct ptr_serialization_support + { +#if defined(BOOST_MSVC) + virtual BOOST_DLLEXPORT void instantiate() QX_USED; +#elif defined(__BORLANDC__) + static BOOST_DLLEXPORT void instantiate() QX_USED; + enum + { + x = sizeof(instantiate(), 3) + }; +#else + static BOOST_DLLEXPORT void instantiate() QX_USED; + typedef instantiate_function< + &ptr_serialization_support::instantiate> + x; +#endif + }; + + template + BOOST_DLLEXPORT void + ptr_serialization_support::instantiate() + { + export_impl::enable_save( +#if !defined(__BORLANDC__) + BOOST_DEDUCED_TYPENAME +#endif + Archive::is_saving()); + + export_impl::enable_load( +#if !defined(__BORLANDC__) + BOOST_DEDUCED_TYPENAME +#endif + Archive::is_loading()); + } + + template + struct guid_initializer + { + const guid_initializer &export_guid(char const * /* key */, mpl::false_) + { + // generates the statically-initialized objects whose constructors + // register the information allowing serialization of T objects + // through pointers to their base classes. + instantiate_ptr_serialization((T *)0, 0, adl_tag()); + return *this; + } + const guid_initializer &export_guid(char const * /*key*/, mpl::true_) + { + return *this; + } + const guid_initializer &export_guid(char const *key) + { + BOOST_STATIC_WARNING(boost::is_polymorphic::value); + assert(NULL != key); + boost::serialization::singleton< + BOOST_DEDUCED_TYPENAME + boost::serialization::type_info_implementation::type>::get_mutable_instance() + .key_register(key); + // note: exporting an abstract base class will have no effect + // and cannot be used to instantitiate serialization code + // (one might be using this in a DLL to instantiate code) + // BOOST_STATIC_WARNING(! boost::serialization::is_abstract::value); + return export_guid(key, boost::serialization::is_abstract()); + } + }; + + } // namespace detail + } // namespace archive +} // namespace boost + +#define BOOST_CLASS_EXPORT_GUID(T, K) \ + namespace \ + { \ + ::boost::archive::detail::guid_initializer const & \ + boost_serialization_guid_initializer_##T = ::boost::serialization::singleton< \ + ::boost::archive::detail::guid_initializer>::get_mutable_instance() \ + .export_guid(K); \ + } + +// check for unnecessary export. T isn't polymorphic so there is no +// need to export it. +#define BOOST_CLASS_EXPORT_CHECK(T) \ + BOOST_STATIC_WARNING( \ + boost::is_polymorphic::value); \ + /**/ + +// the default exportable class identifier is the class name +// the default list of archives types for which code id generated +// are the originally included with this serialization system +#define BOOST_CLASS_EXPORT(T) \ + BOOST_CLASS_EXPORT_GUID( \ + T, \ + BOOST_PP_STRINGIZE(T) ) \ + /**/ + +#endif // _QX_USE_MODIFY_BOOST_SERIALIZATION_EXPORT_HPP +#endif // _QX_BOOST_SERIALIZATION_EXPORT_HPP_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/boost/portable_binary/portable_archive_exception.hpp b/include/QxSerialize/boost/portable_binary/portable_archive_exception.hpp new file mode 100644 index 0000000..bb0d352 --- /dev/null +++ b/include/QxSerialize/boost/portable_binary/portable_archive_exception.hpp @@ -0,0 +1,104 @@ +/*****************************************************************************/ +/** + * \file portable_archive_exception.hpp + * \brief Provides error handling and constants. + * \author christian.pfligersdorffer@gmx.at + * + * Portable archive exceptions derive from the boost archive exceptions + * and add failure causes specific to the portable binary usecase. + * + * Additionally this header serves as common include for important + * constants or typedefs. + */ +/****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#if _QX_SERIALIZE_PORTABLE_BINARY +#ifndef _QX_PORTABLE_ARCHIVE_EXCEPTION_H_ +#define _QX_PORTABLE_ARCHIVE_EXCEPTION_H_ + +#ifdef _MSC_VER +#pragma once +#endif // _MSC_VER + +#include +#include +#include + +namespace eos { + + // this value is written to the top of the stream + const signed char magic_byte = 'e' | 'o' | 's'; + + // flag for fp serialization + const unsigned no_infnan = 64; + + // integral type for the archive version + #if BOOST_VERSION < 104400 + typedef boost::archive::version_type archive_version_type; + #else + typedef boost::archive::library_version_type archive_version_type; + #endif + + // version of the linked boost archive library + const archive_version_type archive_version( + #if BOOST_VERSION < 103700 + boost::archive::ARCHIVE_VERSION() + #else + boost::archive::BOOST_ARCHIVE_VERSION() + #endif + ); + + /** + * \brief Exception being thrown when serialization cannot proceed. + * + * There are several situations in which the portable archives may fail and + * hence throw an exception: + * -# deserialization of an integer value that exceeds the range of the type + * -# (de)serialization of inf/nan through an archive with no_infnan flag set + * -# deserialization of a denormalized value without the floating point type + * supporting denormalized numbers + * + * Note that this exception will also be thrown if you mixed up your stream + * position and accidentially interpret some value for size data (in this case + * the reported size will be totally amiss most of the time). + */ + class portable_archive_exception : public boost::archive::archive_exception + { + std::string msg; + + public: + //! type size is not large enough for deserialized number + portable_archive_exception(signed char invalid_size) + : boost::archive::archive_exception(other_exception) + , msg("requested integer size exceeds type size: ") + { + msg += boost::lexical_cast(invalid_size); + } + + //! negative number in unsigned type + portable_archive_exception() + : boost::archive::archive_exception(other_exception) + , msg("cannot read a negative number into an unsigned type") + { + } + + //! serialization of inf, nan and denormals + template + portable_archive_exception(const T& abnormal) + : boost::archive::archive_exception(other_exception) + , msg("serialization of illegal floating point value: ") + { + msg += boost::lexical_cast(abnormal); + } + + //! override the base class function with our message + const char* what() const throw() { return msg.c_str(); } + ~portable_archive_exception() throw() {} + }; + +} // namespace eos + +#endif // _QX_PORTABLE_ARCHIVE_EXCEPTION_H_ +#endif // _QX_SERIALIZE_PORTABLE_BINARY +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/boost/portable_binary/portable_iarchive.hpp b/include/QxSerialize/boost/portable_binary/portable_iarchive.hpp new file mode 100644 index 0000000..fce4296 --- /dev/null +++ b/include/QxSerialize/boost/portable_binary/portable_iarchive.hpp @@ -0,0 +1,505 @@ +/*****************************************************************************/ +/** + * \file portable_iarchive.hpp + * \brief Provides an archive to read from portable binary files. + * \author christian.pfligersdorffer@gmx.at + * \version 5.1 + * + * This pair of archives brings the advantages of binary streams to the cross + * platform boost::serialization user. While being almost as fast as the native + * binary archive it allows its files to be exchanged between cpu architectures + * using different byte order (endianness). Speaking of speed: in serializing + * numbers the (portable) binary approach is approximately ten times faster than + * the ascii implementation (that is inherently portable)! + * + * Based on the portable archive example by Robert Ramey this implementation + * uses Beman Dawes endian library and fp_utilities from Johan Rade, both being + * in boost since 1.36. Prior to that you need to add them both (header only) + * to your boost directory before you're able to use the archives provided. + * Our archives have been tested successfully for boost versions 1.33 to 1.49! + * + * \note Correct behaviour has so far been confirmed using PowerPC-32, x86-32 + * and x86-64 platforms featuring different byte order. So there is a good + * chance it will instantly work for your specific setup. If you encounter + * problems or have suggestions please contact the author. + * + * \note Version 5.1 is now compatible with boost up to version 1.59. Thanks to + * ecotax for pointing to the issue with shared_ptr_helper. + * + * \note Version 5.0 is now compatible with boost up to version 1.49 and enables + * serialization of std::wstring by converting it to/from utf8 (thanks to + * Arash Abghari for this suggestion). With that all unit tests from the + * serialization library pass again with the notable exception of user + * defined primitive types. Those are not supported and as a result any + * user defined type to be used with the portable archives are required + * to be at least object_serializable. + * + * \note Version 4.2 maintains compatibility with the latest boost 1.45 and adds + * serialization of special floating point values inf and NaN as proposed + * by Francois Mauger. + * + * \note Version 4.1 makes the archives work together with boost 1.40 and 1.41. + * Thanks to Francois Mauger for his suggestions. + * + * \note Version 4 removes one level of the inheritance hierarchy and directly + * builds upon binary primitive and basic binary archive, thereby fixing + * the last open issue regarding array serialization. Thanks to Robert + * Ramey for the hint. + * + * \note A few fixes introduced in version 3.1 let the archives pass all of the + * serialization tests. Thanks to Sergey Morozov for running the tests. + * Wouter Bijlsma pointed out where to find the fp_utilities and endian + * libraries headers inside the boost distribution. I would never have + * found them so thank him it works out of the box since boost 1.36. + * + * \note With Version 3.0 the archives have been made portable across different + * boost versions. For that purpose a header is added to the data that + * supplies the underlying serialization library version. Backwards + * compatibility is maintained by assuming library version boost 1.33 if + * the iarchive is created using the no_header flag. Whether a header is + * present or not can be guessed by peeking into the stream: the header's + * first byte is the magic number 127 coinciding with 'e'|'o'|'s' :-) + * + * \note Version 2.1 removes several compiler warnings and enhances floating + * point diagnostics to inform the user if some preconditions are violated + * on his platform. We do not strive for the universally portable solution + * in binary floating point serialization as desired by some boost users. + * Instead we support only the most widely used IEEE 754 format and try to + * detect when requirements are not met and hence our approach must fail. + * Contributions we made by Johan Rade and Ákos Maróy. + * + * \note Version 2.0 fixes a serious bug that effectively transformed most + * of negative integral values into positive values! For example the two + * numbers -12 and 234 were stored in the same 8-bit pattern and later + * always restored to 234. This was fixed in this version in a way that + * does not change the interpretation of existing archives that did work + * because there were no negative numbers. The other way round archives + * created by version 2.0 and containing negative numbers will raise an + * integer type size exception when reading it with version 1.0. Thanks + * to Markus Frohnmaier for testing the archives and finding the bug. + * + * \copyright The boost software license applies. + */ +/*****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#if _QX_SERIALIZE_PORTABLE_BINARY +#ifndef _QX_PORTABLE_BINARY_IARCHIVE_H_ +#define _QX_PORTABLE_BINARY_IARCHIVE_H_ + +#ifdef _MSC_VER +#pragma once +#endif // _MSC_VER + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4996) +#pragma warning(disable:4661) +#endif // _MSC_VER + +#include + +// basic headers +#include +#include +#include +#include + +#if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600 +#include +#endif + +// endian and fpclassify +#if BOOST_VERSION < 103600 +#include +#include +#elif BOOST_VERSION < 104800 +#include +#include +#else +#include +#include +#endif + +// namespace alias +#if BOOST_VERSION < 103800 +namespace fp = boost::math; +#else +namespace fp = boost::spirit::math; +#endif + +// namespace alias endian +#if BOOST_VERSION < 104800 +namespace endian = boost::detail; +#else +namespace endian = boost::spirit::detail; +#endif + +#if BOOST_VERSION >= 104500 && !defined BOOST_NO_STD_WSTRING +// used for wstring to utf8 conversion +#include +#include +#endif + +// generic type traits for numeric types +#include +#include +#include +#include + +#include "portable_archive_exception.hpp" + +// hint from Johan Rade: on VMS there is still support for +// the VAX floating point format and this macro detects it +#if defined(__vms) && defined(__DECCXX) && !__IEEE_FLOAT +#error "VAX floating point format is not supported!" +#endif + +namespace eos { + + // forward declaration + class portable_iarchive; + + typedef boost::archive::basic_binary_iprimitive< + portable_iarchive + #if BOOST_VERSION < 103400 + , std::istream + #else + , std::istream::char_type + , std::istream::traits_type + #endif + > portable_iprimitive; + + /** + * \brief Portable binary input archive using little endian format. + * + * This archive addresses integer size, endianness and floating point types so + * that data can be transferred across different systems. There may still be + * constraints as to what systems are compatible and the user will have to take + * care that e.g. a very large int being saved on a 64 bit machine will result + * in a portable_archive_exception if loaded into an int on a 32 bit system. + * A possible workaround to this would be to use fixed types like + * boost::uint64_t in your serialization structures. + * + * \note The class is based on the portable binary example by Robert Ramey and + * uses Beman Dawes endian library plus fp_utilities by Johan Rade. + */ + class portable_iarchive : public portable_iprimitive + + // the example derives from common_oarchive but that lacks the + // load_override functions so we chose to stay one level higher + , public boost::archive::basic_binary_iarchive + + #if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600 + // mix-in helper class for serializing shared_ptr + , public boost::archive::detail::shared_ptr_helper + #endif + { + // only needed for Robert's hack in basic_binary_iarchive::init + friend class boost::archive::basic_binary_iarchive; + + // workaround for gcc: use a dummy struct + // as additional argument type for overloading + template struct dummy { dummy(int) {}}; + + // loads directly from stream + inline signed char load_signed_char() + { + signed char c; + portable_iprimitive::load(c); + return c; + } + + // archive initialization + void init(unsigned flags) + { + using namespace boost::archive; + archive_version_type input_library_version(3); + + // it is vital to have version information! + // if we don't have any we assume boost 1.33 + if (flags & no_header) + set_library_version(input_library_version); + + // extract and check the magic eos byte + else if (load_signed_char() != magic_byte) + throw archive_exception(archive_exception::invalid_signature); + + else + { + // extract version information + operator>>(input_library_version); + + // throw if file version is newer than we are + if (input_library_version > archive_version) + throw archive_exception(archive_exception::unsupported_version); + + // else set the library version accordingly + else set_library_version(input_library_version); + } + } + + public: + /** + * \brief Constructor on a stream using ios::binary mode! + * + * We cannot call basic_binary_iprimitive::init which tries to detect + * if the binary archive stems from a different platform by examining + * type sizes. + * + * We could have called basic_binary_iarchive::init which would create + * the boost::serialization standard archive header containing also the + * library version. Due to efficiency we stick with our own. + */ + portable_iarchive(std::istream& is, unsigned flags = 0) + #if BOOST_VERSION < 103400 + : portable_iprimitive(is, flags & boost::archive::no_codecvt) + #else + : portable_iprimitive(*is.rdbuf(), flags & boost::archive::no_codecvt) + #endif + , boost::archive::basic_binary_iarchive(flags) + { + init(flags); + } + + #if BOOST_VERSION >= 103400 + portable_iarchive(std::streambuf& sb, unsigned flags = 0) + : portable_iprimitive(sb, flags & boost::archive::no_codecvt) + , boost::archive::basic_binary_iarchive(flags) + { + init(flags); + } + #endif + + //! Load narrow strings. + void load(std::string& s) + { + portable_iprimitive::load(s); + } + + #ifndef BOOST_NO_STD_WSTRING + /** + * \brief Load wide strings. + * + * This is rather tricky to get right for true portability as there + * are so many different character encodings around. However, wide + * strings that are encoded in one of the Unicode schemes only need + * to be _transcoded_ which is a lot easier actually. + * + * We generate the output string to be encoded in the system's native + * format, ie. UTF-16 on Windows and UTF-32 on Linux machines. Don't + * know about Mac here so I can't really say about that. + */ + void load(std::wstring& s) + { + std::string utf8; + load(utf8); + s = boost::from_utf8(utf8); + } + #endif + + /** + * \brief Loading bool type. + * + * Byte pattern is same as with integer types, so this function + * is somewhat redundant but treating bool as integer generates + * a lot of compiler warnings. + * + * \note If you cannot compile your application and it says something + * about load(bool) cannot convert your type A& into bool& then you + * should check your BOOST_CLASS_IMPLEMENTATION setting for A, as + * portable_archive is not able to handle custom primitive types in + * a general manner. + */ + void load(bool& b) + { + switch (signed char c = load_signed_char()) + { + case 0: b = false; break; + case 1: b = load_signed_char(); break; + default: throw portable_archive_exception(c); + } + } + + /** + * \brief Load integer types. + * + * First we load the size information ie. the number of bytes that + * hold the actual data. Then we retrieve the data and transform it + * to the original value by using load_little_endian. + */ + template + typename boost::enable_if >::type + load(T & t, dummy<2> = 0) + { + // get the number of bytes in the stream + if (signed char size = load_signed_char()) + { + // check for negative value in unsigned type + if (size < 0 && boost::is_unsigned::value) + throw portable_archive_exception(); + + // check that our type T is large enough + else if ((unsigned) abs(size) > sizeof(T)) + throw portable_archive_exception(size); + + // reconstruct the value + T temp = size < 0 ? -1 : 0; + load_binary(&temp, abs(size)); + + // load the value from little endian - it is then converted + // to the target type T and fits it because size <= sizeof(T) + t = endian::load_little_endian(&temp); + } + + else t = 0; // zero optimization + } + + /** + * \brief Load floating point types. + * + * We simply rely on fp_traits to set the bit pattern from the (unsigned) + * integral type that was stored in the stream. Francois Mauger provided + * standardized behaviour for special values like inf and NaN, that need to + * be serialized in his application. + * + * \note by Johan Rade (author of the floating point utilities library): + * Be warned that the math::detail::fp_traits::type::get_bits() function + * is *not* guaranteed to give you all bits of the floating point number. It + * will give you all bits if and only if there is an integer type that has + * the same size as the floating point you are copying from. It will not + * give you all bits for double if there is no uint64_t. It will not give + * you all bits for long double if sizeof(long double) > 8 or there is no + * uint64_t. + * + * The member fp_traits::type::coverage will tell you whether all bits + * are copied. This is a typedef for either math::detail::all_bits or + * math::detail::not_all_bits. + * + * If the function does not copy all bits, then it will copy the most + * significant bits. So if you serialize and deserialize the way you + * describe, and fp_traits::type::coverage is math::detail::not_all_bits, + * then your floating point numbers will be truncated. This will introduce + * small rounding off errors. + */ + template + typename boost::enable_if >::type + load(T & t, dummy<3> = 0) + { + typedef typename fp::detail::fp_traits::type traits; + + // if you end here there are three possibilities: + // 1. you're serializing a long double which is not portable + // 2. you're serializing a double but have no 64 bit integer + // 3. your machine is using an unknown floating point format + // after reading the note above you still might decide to + // deactivate this static assert and try if it works out. + typename traits::bits bits; + BOOST_STATIC_ASSERT(sizeof(bits) == sizeof(T)); + BOOST_STATIC_ASSERT(std::numeric_limits::is_iec559); + + load(bits); + traits::set_bits(t, bits); + + // if the no_infnan flag is set we must throw here + if (get_flags() & no_infnan && !fp::isfinite(t)) + throw portable_archive_exception(t); + + // if you end here your floating point type does not support + // denormalized numbers. this might be the case even though + // your type conforms to IEC 559 (and thus to IEEE 754) + if (std::numeric_limits::has_denorm == std::denorm_absent + && fp::fpclassify(t) == (int) FP_SUBNORMAL) // GCC4 + throw portable_archive_exception(t); + } + + // in boost 1.44 version_type was splitted into library_version_type and + // item_version_type, plus a whole bunch of additional strong typedefs. + template + typename boost::disable_if >::type + load(T& t, dummy<4> = 0) + { + // we provide a generic load routine for all types that feature + // conversion operators into an unsigned integer value like those + // created through BOOST_STRONG_TYPEDEF(X, some unsigned int) like + // library_version_type, collection_size_type, item_version_type, + // class_id_type, object_id_type, version_type and tracking_type + load((typename boost::uint_t::least&)(t)); + } + }; + +} // namespace eos + +// this is required by export which registers all of your +// classes with all the inbuilt archives plus our archive. +#if BOOST_VERSION < 103500 +#define BOOST_ARCHIVE_CUSTOM_IARCHIVE_TYPES eos::portable_iarchive +#else +BOOST_SERIALIZATION_REGISTER_ARCHIVE(eos::portable_iarchive) +#endif + +// if you include this header multiple times and your compiler is picky +// about multiple template instantiations (eg. gcc is) then you need to +// define NO_EXPLICIT_TEMPLATE_INSTANTIATION before every include but one +// or you move the instantiation section into an implementation file +#ifndef NO_EXPLICIT_TEMPLATE_INSTANTIATION + +#ifndef _QX_BOOST_ARCHIVE_SERIALIZER_IMPL_DEFINED_ +#define _QX_BOOST_ARCHIVE_SERIALIZER_IMPL_DEFINED_ + +#include +#include +#include +#include + +#if _QX_SERIALIZE_TEXT +#include +#include +#include +#include +#endif // _QX_SERIALIZE_TEXT + +#if (BOOST_VERSION < 104000) +#include +#include +#elif !defined BOOST_ARCHIVE_SERIALIZER_INCLUDED +#include +#define BOOST_ARCHIVE_SERIALIZER_INCLUDED +#endif // (BOOST_VERSION < 104000) + +#endif // _QX_BOOST_ARCHIVE_SERIALIZER_IMPL_DEFINED_ + +namespace boost { namespace archive { + + // explicitly instantiate for this type of binary stream + template class basic_binary_iarchive; + + template class basic_binary_iprimitive< + eos::portable_iarchive + #if BOOST_VERSION < 103400 + , std::istream + #else + , std::istream::char_type + , std::istream::traits_type + #endif + >; + +#if (! _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON) +#if (BOOST_VERSION < 104000) + template class detail::archive_pointer_iserializer; +#else // (BOOST_VERSION < 104000) + template class detail::archive_serializer_map; +#endif // (BOOST_VERSION < 104000) +#endif // (! _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON) + +} } // namespace boost::archive + +#endif // NO_EXPLICIT_TEMPLATE_INSTANTIATION + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +#endif // _QX_PORTABLE_BINARY_IARCHIVE_H_ +#endif // _QX_SERIALIZE_PORTABLE_BINARY +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/boost/portable_binary/portable_oarchive.hpp b/include/QxSerialize/boost/portable_binary/portable_oarchive.hpp new file mode 100644 index 0000000..2dad52a --- /dev/null +++ b/include/QxSerialize/boost/portable_binary/portable_oarchive.hpp @@ -0,0 +1,488 @@ +/*****************************************************************************/ +/** + * \file portable_oarchive.hpp + * \brief Provides an archive to create portable binary files. + * \author christian.pfligersdorffer@gmx.at + * \version 5.1 + * + * This pair of archives brings the advantages of binary streams to the cross + * platform boost::serialization user. While being almost as fast as the native + * binary archive it allows its files to be exchanged between cpu architectures + * using different byte order (endianness). Speaking of speed: in serializing + * numbers the (portable) binary approach is approximately ten times faster than + * the ascii implementation (that is inherently portable)! + * + * Based on the portable archive example by Robert Ramey this implementation + * uses Beman Dawes endian library and fp_utilities from Johan Rade, both being + * in boost since 1.36. Prior to that you need to add them both (header only) + * to your boost directory before you're able to use the archives provided. + * Our archives have been tested successfully for boost versions 1.33 to 1.49! + * + * \note Correct behaviour has so far been confirmed using PowerPC-32, x86-32 + * and x86-64 platforms featuring different byte order. So there is a good + * chance it will instantly work for your specific setup. If you encounter + * problems or have suggestions please contact the author. + * + * \note Version 5.1 is now compatible with boost up to version 1.59. Thanks to + * ecotax for pointing to the issue with shared_ptr_helper. + * + * \note Version 5.0 is now compatible with boost up to version 1.49 and enables + * serialization of std::wstring by converting it to/from utf8 (thanks to + * Arash Abghari for this suggestion). With that all unit tests from the + * serialization library pass again with the notable exception of user + * defined primitive types. Those are not supported and as a result any + * user defined type to be used with the portable archives are required + * to be at least object_serializable. + * + * \note Oliver Putz pointed out that -0.0 was not serialized correctly, so + * version 4.3 provides a fix for that. Thanks Ollie! + * + * \note Version 4.2 maintains compatibility with the latest boost 1.45 and adds + * serialization of special floating point values inf and NaN as proposed + * by Francois Mauger. + * + * \note Version 4.1 makes the archives work together with boost 1.40 and 1.41. + * Thanks to Francois Mauger for his suggestions. + * + * \note Version 4 removes one level of the inheritance hierarchy and directly + * builds upon binary primitive and basic binary archive, thereby fixing + * the last open issue regarding array serialization. Thanks to Robert + * Ramey for the hint. + * + * \note A few fixes introduced in version 3.1 let the archives pass all of the + * serialization tests. Thanks to Sergey Morozov for running the tests. + * Wouter Bijlsma pointed out where to find the fp_utilities and endian + * libraries headers inside the boost distribution. I would never have + * found them so thank him it works out of the box since boost 1.36. + * + * \note With Version 3.0 the archives have been made portable across different + * boost versions. For that purpose a header is added to the data that + * supplies the underlying serialization library version. Backwards + * compatibility is maintained by assuming library version boost 1.33 if + * the iarchive is created using the no_header flag. Whether a header is + * present or not can be guessed by peeking into the stream: the header's + * first byte is the magic number 127 coinciding with 'e'|'o'|'s' :-) + * + * \note Version 2.1 removes several compiler warnings and enhances floating + * point diagnostics to inform the user if some preconditions are violated + * on his platform. We do not strive for the universally portable solution + * in binary floating point serialization as desired by some boost users. + * Instead we support only the most widely used IEEE 754 format and try to + * detect when requirements are not met and hence our approach must fail. + * Contributions we made by Johan Rade and Ákos Maróy. + * + * \note Version 2.0 fixes a serious bug that effectively transformed most + * of negative integral values into positive values! For example the two + * numbers -12 and 234 were stored in the same 8-bit pattern and later + * always restored to 234. This was fixed in this version in a way that + * does not change the interpretation of existing archives that did work + * because there were no negative numbers. The other way round archives + * created by version 2.0 and containing negative numbers will raise an + * integer type size exception when reading it with version 1.0. Thanks + * to Markus Frohnmaier for testing the archives and finding the bug. + * + * \copyright The boost software license applies. + */ +/*****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#if _QX_SERIALIZE_PORTABLE_BINARY +#ifndef _QX_PORTABLE_BINARY_OARCHIVE_H_ +#define _QX_PORTABLE_BINARY_OARCHIVE_H_ + +#ifdef _MSC_VER +#pragma once +#endif // _MSC_VER + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4996) +#pragma warning(disable:4661) +#endif // _MSC_VER + +#include + +// basic headers +#include +#include +#include +#include + +#if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600 +#include +#endif + +// endian and fpclassify +#if BOOST_VERSION < 103600 +#include +#include +#elif BOOST_VERSION < 104800 +#include +#include +#else +#include +#include +#endif + +// namespace alias fp_classify +#if BOOST_VERSION < 103800 +namespace fp = boost::math; +#else +namespace fp = boost::spirit::math; +#endif + +// namespace alias endian +#if BOOST_VERSION < 104800 +namespace endian = boost::detail; +#else +namespace endian = boost::spirit::detail; +#endif + +#if BOOST_VERSION >= 104500 && !defined BOOST_NO_STD_WSTRING +// used for wstring to utf8 conversion +#include +#include +#endif + +// generic type traits for numeric types +#include +#include +#include +#include + +#include "portable_archive_exception.hpp" + +// hint from Johan Rade: on VMS there is still support for +// the VAX floating point format and this macro detects it +#if defined(__vms) && defined(__DECCXX) && !__IEEE_FLOAT +#error "VAX floating point format is not supported!" +#endif + +namespace eos { + + // forward declaration + class portable_oarchive; + + typedef boost::archive::basic_binary_oprimitive< + portable_oarchive + #if BOOST_VERSION < 103400 + , std::ostream + #else + , std::ostream::char_type + , std::ostream::traits_type + #endif + > portable_oprimitive; + + /** + * \brief Portable binary output archive using little endian format. + * + * This archive addresses integer size, endianness and floating point types so + * that data can be transferred across different systems. The archive consists + * primarily of three different save implementations for integral types, + * floating point types and string types. Those functions are templates and use + * enable_if to be correctly selected for overloading. + * + * \note The class is based on the portable binary example by Robert Ramey and + * uses Beman Dawes endian library plus fp_utilities by Johan Rade. + */ + class portable_oarchive : public portable_oprimitive + + // the example derives from common_oarchive but that lacks the + // save_override functions so we chose to stay one level higher + , public boost::archive::basic_binary_oarchive + + #if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600 + // mix-in helper class for serializing shared_ptr + , public boost::archive::detail::shared_ptr_helper + #endif + { + // workaround for gcc: use a dummy struct + // as additional argument type for overloading + template struct dummy { dummy(int) {}}; + + // stores a signed char directly to stream + inline void save_signed_char(const signed char& c) + { + portable_oprimitive::save(c); + } + + // archive initialization + void init(unsigned flags) + { + // it is vital to have version information if the archive is + // to be parsed with a newer version of boost::serialization + // therefor we create a header, no header means boost 1.33 + if (flags & boost::archive::no_header) + BOOST_ASSERT(archive_version == 3); + else + { + // write our minimalistic header (magic byte plus version) + // the boost archives write a string instead - by calling + // boost::archive::basic_binary_oarchive::init() + save_signed_char(magic_byte); + + // write current version +// save(archive_version); + operator<<(archive_version); + } + } + + public: + /** + * \brief Constructor on a stream using ios::binary mode! + * + * We cannot call basic_binary_oprimitive::init which stores type + * sizes to the archive in order to detect transfers to non-compatible + * platforms. + * + * We could have called basic_binary_oarchive::init which would create + * the boost::serialization standard archive header containing also the + * library version. Due to efficiency we stick with our own. + */ + portable_oarchive(std::ostream& os, unsigned flags = 0) + #if BOOST_VERSION < 103400 + : portable_oprimitive(os, flags & boost::archive::no_codecvt) + #else + : portable_oprimitive(*os.rdbuf(), flags & boost::archive::no_codecvt) + #endif + , boost::archive::basic_binary_oarchive(flags) + { + init(flags); + } + + #if BOOST_VERSION >= 103400 + portable_oarchive(std::streambuf& sb, unsigned flags = 0) + : portable_oprimitive(sb, flags & boost::archive::no_codecvt) + , boost::archive::basic_binary_oarchive(flags) + { + init(flags); + } + #endif + + //! Save narrow strings. + void save(const std::string& s) + { + portable_oprimitive::save(s); + } + + #ifndef BOOST_NO_STD_WSTRING + /** + * \brief Save wide strings. + * + * This is rather tricky to get right for true portability as there + * are so many different character encodings around. However, wide + * strings that are encoded in one of the Unicode schemes only need + * to be _transcoded_ which is a lot easier actually. + * + * We expect the input string to be encoded in the system's native + * format, ie. UTF-16 on Windows and UTF-32 on Linux machines. Don't + * know about Mac here so I can't really say about that. + */ + void save(const std::wstring& s) + { + save(boost::to_utf8(s)); + } + #endif + + /** + * \brief Saving bool type. + * + * Saving bool directly, not by const reference + * because of tracking_type's operator (bool). + * + * \note If you cannot compile your application and it says something + * about save(bool) cannot convert your type const A& into bool then + * you should check your BOOST_CLASS_IMPLEMENTATION setting for A, as + * portable_archive is not able to handle custom primitive types in + * a general manner. + */ + void save(const bool& b) + { + save_signed_char(b); + if (b) save_signed_char('T'); + } + + /** + * \brief Save integer types. + * + * First we save the size information ie. the number of bytes that hold the + * actual data. We subsequently transform the data using store_little_endian + * and store non-zero bytes to the stream. + */ + template + typename boost::enable_if >::type + save(const T & t, dummy<2> = 0) + { + if (T temp = t) + { + // examine the number of bytes + // needed to represent the number + signed char size = 0; + do { temp >>= CHAR_BIT; ++size; } + while (temp != 0 && temp != (T) -1); + + // encode the sign bit into the size + save_signed_char(t > 0 ? size : -size); + BOOST_ASSERT(t > 0 || boost::is_signed::value); + + // we choose to use little endian because this way we just + // save the first size bytes to the stream and skip the rest + endian::store_little_endian(&temp, t); + + save_binary(&temp, size); + } + // zero optimization + else save_signed_char(0); + } + + /** + * \brief Save floating point types. + * + * We simply rely on fp_traits to extract the bit pattern into an (unsigned) + * integral type and store that into the stream. Francois Mauger provided + * standardized behaviour for special values like inf and NaN, that need to + * be serialized in his application. + * + * \note by Johan Rade (author of the floating point utilities library): + * Be warned that the math::detail::fp_traits::type::get_bits() function + * is *not* guaranteed to give you all bits of the floating point number. It + * will give you all bits if and only if there is an integer type that has + * the same size as the floating point you are copying from. It will not + * give you all bits for double if there is no uint64_t. It will not give + * you all bits for long double if sizeof(long double) > 8 or there is no + * uint64_t. + * + * The member fp_traits::type::coverage will tell you whether all bits + * are copied. This is a typedef for either math::detail::all_bits or + * math::detail::not_all_bits. + * + * If the function does not copy all bits, then it will copy the most + * significant bits. So if you serialize and deserialize the way you + * describe, and fp_traits::type::coverage is math::detail::not_all_bits, + * then your floating point numbers will be truncated. This will introduce + * small rounding off errors. + */ + template + typename boost::enable_if >::type + save(const T & t, dummy<3> = 0) + { + typedef typename fp::detail::fp_traits::type traits; + + // if the no_infnan flag is set we must throw here + if (get_flags() & no_infnan && !fp::isfinite(t)) + throw portable_archive_exception(t); + + // if you end here there are three possibilities: + // 1. you're serializing a long double which is not portable + // 2. you're serializing a double but have no 64 bit integer + // 3. your machine is using an unknown floating point format + // after reading the note above you still might decide to + // deactivate this static assert and try if it works out. + typename traits::bits bits; + BOOST_STATIC_ASSERT(sizeof(bits) == sizeof(T)); + BOOST_STATIC_ASSERT(std::numeric_limits::is_iec559); + + // examine value closely + switch (fp::fpclassify(t)) + { + //case FP_ZERO: bits = 0; break; + case FP_NAN: bits = traits::exponent | traits::mantissa; break; + case FP_INFINITE: bits = traits::exponent | (t<0) * traits::sign; break; + case FP_SUBNORMAL: assert(std::numeric_limits::has_denorm); // pass + case FP_ZERO: // note that floats can be ±0.0 + case FP_NORMAL: traits::get_bits(t, bits); break; + default: throw portable_archive_exception(t); + } + + save(bits); + } + + // in boost 1.44 version_type was splitted into library_version_type and + // item_version_type, plus a whole bunch of additional strong typedefs. + template + typename boost::disable_if >::type + save(const T& t, dummy<4> = 0) + { + // we provide a generic save routine for all types that feature + // conversion operators into an unsigned integer value like those + // created through BOOST_STRONG_TYPEDEF(X, some unsigned int) like + // library_version_type, collection_size_type, item_version_type, + // class_id_type, object_id_type, version_type and tracking_type + save((typename boost::uint_t::least)(t)); + } + }; + +} // namespace eos + +// required by export +#if BOOST_VERSION < 103500 +#define BOOST_ARCHIVE_CUSTOM_OARCHIVE_TYPES eos::portable_oarchive +#else +BOOST_SERIALIZATION_REGISTER_ARCHIVE(eos::portable_oarchive) +#endif + +// if you include this header multiple times and your compiler is picky +// about multiple template instantiations (eg. gcc is) then you need to +// define NO_EXPLICIT_TEMPLATE_INSTANTIATION before every include but one +// or you move the instantiation section into an implementation file +#ifndef NO_EXPLICIT_TEMPLATE_INSTANTIATION + +#ifndef _QX_BOOST_ARCHIVE_SERIALIZER_IMPL_DEFINED_ +#define _QX_BOOST_ARCHIVE_SERIALIZER_IMPL_DEFINED_ + +#include +#include +#include +#include + +#if _QX_SERIALIZE_TEXT +#include +#include +#include +#include +#endif // _QX_SERIALIZE_TEXT + +#if (BOOST_VERSION < 104000) +#include +#include +#elif !defined BOOST_ARCHIVE_SERIALIZER_INCLUDED +#include +#define BOOST_ARCHIVE_SERIALIZER_INCLUDED +#endif // (BOOST_VERSION < 104000) + +#endif // _QX_BOOST_ARCHIVE_SERIALIZER_IMPL_DEFINED_ + +namespace boost { namespace archive { + + // explicitly instantiate for this type of binary stream + template class basic_binary_oarchive; + + template class basic_binary_oprimitive< + eos::portable_oarchive + #if BOOST_VERSION < 103400 + , std::ostream + #else + , std::ostream::char_type + , std::ostream::traits_type + #endif + >; + +#if (! _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON) +#if (BOOST_VERSION < 104000) + template class detail::archive_pointer_oserializer; +#else // (BOOST_VERSION < 104000) + template class detail::archive_serializer_map; +#endif // (BOOST_VERSION < 104000) +#endif // (! _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON) + +} } // namespace boost::archive + +#endif // NO_EXPLICIT_TEMPLATE_INSTANTIATION + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +#endif // _QX_PORTABLE_BINARY_OARCHIVE_H_ +#endif // _QX_SERIALIZE_PORTABLE_BINARY +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/std/QxSerialize_std_shared_ptr.h b/include/QxSerialize/std/QxSerialize_std_shared_ptr.h new file mode 100644 index 0000000..a94bb70 --- /dev/null +++ b/include/QxSerialize/std/QxSerialize_std_shared_ptr.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_STD_SHARED_PTR_H_ +#define _QX_SERIALIZE_STD_SHARED_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#if (BOOST_VERSION >= 105600) + +#include + +#else // (BOOST_VERSION >= 105600) + +#include +#include +#include + +namespace boost +{ + namespace serialization + { + + template + inline void save(Archive &ar, const std::shared_ptr &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + const T *ptr = t.get(); + ar << boost::serialization::make_nvp("std_shared_ptr", ptr); + } + + template + inline void load(Archive &ar, std::shared_ptr &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + T *ptr = NULL; + ar >> boost::serialization::make_nvp("std_shared_ptr", ptr); + t.reset(ptr); + } + + template + inline void serialize(Archive &ar, std::shared_ptr &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + } // namespace boost +} // namespace serialization + +#endif // (BOOST_VERSION >= 105600) +#endif // _QX_SERIALIZE_STD_SHARED_PTR_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/std/QxSerialize_std_tuple.h b/include/QxSerialize/std/QxSerialize_std_tuple.h new file mode 100644 index 0000000..c446abf --- /dev/null +++ b/include/QxSerialize/std/QxSerialize_std_tuple.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_STD_TUPLE_H_ +#define _QX_SERIALIZE_STD_TUPLE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +namespace boost +{ + namespace serialization + { + + template + inline void serialize(Archive &ar, std::tuple &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("tuple_1", std::get<0>(t)); + ar &boost::serialization::make_nvp("tuple_2", std::get<1>(t)); + } + + template + inline void serialize(Archive &ar, std::tuple &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("tuple_1", std::get<0>(t)); + ar &boost::serialization::make_nvp("tuple_2", std::get<1>(t)); + ar &boost::serialization::make_nvp("tuple_3", std::get<2>(t)); + } + + template + inline void serialize(Archive &ar, std::tuple &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("tuple_1", std::get<0>(t)); + ar &boost::serialization::make_nvp("tuple_2", std::get<1>(t)); + ar &boost::serialization::make_nvp("tuple_3", std::get<2>(t)); + ar &boost::serialization::make_nvp("tuple_4", std::get<3>(t)); + } + + template + inline void serialize(Archive &ar, std::tuple &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("tuple_1", std::get<0>(t)); + ar &boost::serialization::make_nvp("tuple_2", std::get<1>(t)); + ar &boost::serialization::make_nvp("tuple_3", std::get<2>(t)); + ar &boost::serialization::make_nvp("tuple_4", std::get<3>(t)); + ar &boost::serialization::make_nvp("tuple_5", std::get<4>(t)); + } + + template + inline void serialize(Archive &ar, std::tuple &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("tuple_1", std::get<0>(t)); + ar &boost::serialization::make_nvp("tuple_2", std::get<1>(t)); + ar &boost::serialization::make_nvp("tuple_3", std::get<2>(t)); + ar &boost::serialization::make_nvp("tuple_4", std::get<3>(t)); + ar &boost::serialization::make_nvp("tuple_5", std::get<4>(t)); + ar &boost::serialization::make_nvp("tuple_6", std::get<5>(t)); + } + + template + inline void serialize(Archive &ar, std::tuple &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("tuple_1", std::get<0>(t)); + ar &boost::serialization::make_nvp("tuple_2", std::get<1>(t)); + ar &boost::serialization::make_nvp("tuple_3", std::get<2>(t)); + ar &boost::serialization::make_nvp("tuple_4", std::get<3>(t)); + ar &boost::serialization::make_nvp("tuple_5", std::get<4>(t)); + ar &boost::serialization::make_nvp("tuple_6", std::get<5>(t)); + ar &boost::serialization::make_nvp("tuple_7", std::get<6>(t)); + } + + template + inline void serialize(Archive &ar, std::tuple &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("tuple_1", std::get<0>(t)); + ar &boost::serialization::make_nvp("tuple_2", std::get<1>(t)); + ar &boost::serialization::make_nvp("tuple_3", std::get<2>(t)); + ar &boost::serialization::make_nvp("tuple_4", std::get<3>(t)); + ar &boost::serialization::make_nvp("tuple_5", std::get<4>(t)); + ar &boost::serialization::make_nvp("tuple_6", std::get<5>(t)); + ar &boost::serialization::make_nvp("tuple_7", std::get<6>(t)); + ar &boost::serialization::make_nvp("tuple_8", std::get<7>(t)); + } + + template + inline void serialize(Archive &ar, std::tuple &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("tuple_1", std::get<0>(t)); + ar &boost::serialization::make_nvp("tuple_2", std::get<1>(t)); + ar &boost::serialization::make_nvp("tuple_3", std::get<2>(t)); + ar &boost::serialization::make_nvp("tuple_4", std::get<3>(t)); + ar &boost::serialization::make_nvp("tuple_5", std::get<4>(t)); + ar &boost::serialization::make_nvp("tuple_6", std::get<5>(t)); + ar &boost::serialization::make_nvp("tuple_7", std::get<6>(t)); + ar &boost::serialization::make_nvp("tuple_8", std::get<7>(t)); + ar &boost::serialization::make_nvp("tuple_9", std::get<8>(t)); + } + + template + inline void serialize(Archive &ar, std::tuple &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("tuple_1", std::get<0>(t)); + ar &boost::serialization::make_nvp("tuple_2", std::get<1>(t)); + ar &boost::serialization::make_nvp("tuple_3", std::get<2>(t)); + ar &boost::serialization::make_nvp("tuple_4", std::get<3>(t)); + ar &boost::serialization::make_nvp("tuple_5", std::get<4>(t)); + ar &boost::serialization::make_nvp("tuple_6", std::get<5>(t)); + ar &boost::serialization::make_nvp("tuple_7", std::get<6>(t)); + ar &boost::serialization::make_nvp("tuple_8", std::get<7>(t)); + ar &boost::serialization::make_nvp("tuple_9", std::get<8>(t)); + ar &boost::serialization::make_nvp("tuple_10", std::get<9>(t)); + } + + } // namespace boost +} // namespace serialization + +#endif // _QX_SERIALIZE_STD_TUPLE_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/std/QxSerialize_std_unique_ptr.h b/include/QxSerialize/std/QxSerialize_std_unique_ptr.h new file mode 100644 index 0000000..0310309 --- /dev/null +++ b/include/QxSerialize/std/QxSerialize_std_unique_ptr.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZE_STD_UNIQUE_PTR_H_ +#define _QX_SERIALIZE_STD_UNIQUE_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#if (BOOST_VERSION >= 105600) + +#include + +#else // (BOOST_VERSION >= 105600) + +#include +#include +#include + +namespace boost +{ + namespace serialization + { + + template + inline void save(Archive &ar, const std::unique_ptr &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + const T *ptr = t.get(); + ar << boost::serialization::make_nvp("std_unique_ptr", ptr); + } + + template + inline void load(Archive &ar, std::unique_ptr &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + T *ptr = NULL; + ar >> boost::serialization::make_nvp("std_unique_ptr", ptr); + t.reset(ptr); + } + + template + inline void serialize(Archive &ar, std::unique_ptr &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + } // namespace boost +} // namespace serialization + +#endif // (BOOST_VERSION >= 105600) +#endif // _QX_SERIALIZE_STD_UNIQUE_PTR_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/std/QxSerialize_std_unordered_map.h b/include/QxSerialize/std/QxSerialize_std_unordered_map.h new file mode 100644 index 0000000..0da8271 --- /dev/null +++ b/include/QxSerialize/std/QxSerialize_std_unordered_map.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZATION_STD_UNORDERED_MAP_H_ +#define _QX_SERIALIZATION_STD_UNORDERED_MAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#if (BOOST_VERSION >= 105600) + +#include + +#else // (BOOST_VERSION >= 105600) + +#include + +#include +#include +#include +#include +#include +#include + +namespace boost +{ + namespace serialization + { + + template + inline void save(Archive &ar, const std::unordered_map &t, const unsigned int /* file_version */) + { + boost::serialization::stl::save_collection>(ar, t); + } + + template + inline void load(Archive &ar, std::unordered_map &t, const unsigned int /* file_version */) + { + boost::serialization::stl::load_collection, + boost::serialization::stl::archive_input_map>, + boost::serialization::stl::no_reserve_imp>>(ar, t); + } + + template + inline void serialize(Archive &ar, std::unordered_map &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + template + inline void save(Archive &ar, const std::unordered_multimap &t, const unsigned int /* file_version */) + { + boost::serialization::stl::save_collection>(ar, t); + } + + template + inline void load(Archive &ar, std::unordered_multimap &t, const unsigned int /* file_version */) + { +#if (BOOST_VERSION >= 104200) + boost::serialization::stl::load_collection, + boost::serialization::stl::archive_input_map>, + boost::serialization::stl::no_reserve_imp>>(ar, t); +#else // (BOOST_VERSION >= 104200) + boost::serialization::stl::load_collection, + boost::serialization::stl::archive_input_multimap>, + boost::serialization::stl::no_reserve_imp>>(ar, t); +#endif // (BOOST_VERSION >= 104200) + } + + template + inline void serialize(Archive &ar, std::unordered_multimap &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + } // namespace serialization +} // namespace boost + +#endif // (BOOST_VERSION >= 105600) +#endif // _QX_SERIALIZATION_STD_UNORDERED_MAP_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxSerialize/std/QxSerialize_std_unordered_set.h b/include/QxSerialize/std/QxSerialize_std_unordered_set.h new file mode 100644 index 0000000..974924f --- /dev/null +++ b/include/QxSerialize/std/QxSerialize_std_unordered_set.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#ifndef _QX_SERIALIZATION_STD_UNORDERED_SET_H_ +#define _QX_SERIALIZATION_STD_UNORDERED_SET_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#if (BOOST_VERSION >= 105600) + +#include + +#else // (BOOST_VERSION >= 105600) + +#include + +#include +#include +#include +#include +#include +#include + +namespace boost +{ + namespace serialization + { + + template + inline void save(Archive &ar, const std::unordered_set &t, const unsigned int /* file_version */) + { + boost::serialization::stl::save_collection>(ar, t); + } + + template + inline void load(Archive &ar, std::unordered_set &t, const unsigned int /* file_version */) + { + boost::serialization::stl::load_collection, + boost::serialization::stl::archive_input_set>, + boost::serialization::stl::no_reserve_imp>>(ar, t); + } + + template + inline void serialize(Archive &ar, std::unordered_set &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + template + inline void save(Archive &ar, const std::unordered_multiset &t, const unsigned int /* file_version */) + { + boost::serialization::stl::save_collection>(ar, t); + } + + template + inline void load(Archive &ar, std::unordered_multiset &t, const unsigned int /* file_version */) + { +#if (BOOST_VERSION >= 104200) + boost::serialization::stl::load_collection, + boost::serialization::stl::archive_input_set>, + boost::serialization::stl::no_reserve_imp>>(ar, t); +#else // (BOOST_VERSION >= 104200) + boost::serialization::stl::load_collection, + boost::serialization::stl::archive_input_multiset>, + boost::serialization::stl::no_reserve_imp>>(ar, t); +#endif // (BOOST_VERSION >= 104200) + } + + template + inline void serialize(Archive &ar, std::unordered_multiset &t, const unsigned int file_version) + { + boost::serialization::split_free(ar, t, file_version); + } + + } // namespace serialization +} // namespace boost + +#endif // (BOOST_VERSION >= 105600) +#endif // _QX_SERIALIZATION_STD_UNORDERED_SET_H_ +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/include/QxService/IxParameter.h b/include/QxService/IxParameter.h new file mode 100644 index 0000000..42be59c --- /dev/null +++ b/include/QxService/IxParameter.h @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _IX_SERVICE_PARAMETER_H_ +#define _IX_SERVICE_PARAMETER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxParameter.h + * \author XDL Team + * \ingroup QxService + * \brief Common interface for all parameters transfered by QxService module of QxOrm library + */ + +#include + +#ifndef _QX_NO_JSON +#include +#endif // _QX_NO_JSON + +#include + +namespace qx +{ + namespace service + { + class IxParameter; + } // namespace service +} // namespace qx + +QX_DLL_EXPORT QDataStream &operator<<(QDataStream &stream, const qx::service::IxParameter &t) QX_USED; +QX_DLL_EXPORT QDataStream &operator>>(QDataStream &stream, qx::service::IxParameter &t) QX_USED; + +namespace qx +{ + namespace service + { + + /*! + * \ingroup QxService + * \brief qx::service::IxParameter : common interface for all parameters transfered by QxService module of QxOrm library + * + * Click here to access to a tutorial to explain how to work with QxService module. + */ + class QX_DLL_EXPORT IxParameter + { + + friend QX_DLL_EXPORT QDataStream & ::operator<<(QDataStream &stream, const qx::service::IxParameter &t); + friend QX_DLL_EXPORT QDataStream & ::operator>>(QDataStream &stream, qx::service::IxParameter &t); + + public: + IxParameter(); + virtual ~IxParameter(); + + // Need to override these methods only if you are using 'qx::service::QxConnect::serialization_qt' type (based on QDataStream) or 'qx::service::QxConnect::serialization_json' type (based on QJson engine) + // You can use QX_SERVICE_IX_PARAMETER_SERIALIZATION_HPP and QX_SERVICE_IX_PARAMETER_SERIALIZATION_CPP macro to override + virtual void registerClass() const; + virtual QString getClassName() const; + virtual void save(QDataStream &stream) const; + virtual void load(QDataStream &stream); + +#ifndef _QX_NO_JSON + virtual QJsonValue saveToJson() const; + virtual qx_bool loadFromJson(const QJsonValue &val); +#endif // _QX_NO_JSON + }; + + typedef std::shared_ptr IxParameter_ptr; + + } // namespace service +} // namespace qx + +QX_REGISTER_INTERNAL_HELPER_HPP(QX_DLL_EXPORT, qx::service::IxParameter, 0) + +#define QX_SERVICE_IX_PARAMETER_QDATASTREAM_HPP(className) \ +public: \ + virtual void save(QDataStream &stream) const; \ + virtual void load(QDataStream &stream); + +#define QX_SERVICE_IX_PARAMETER_QDATASTREAM_CPP(className) \ + void className::save(QDataStream &stream) const { qx::QxSerializeRegistered::save(stream, (*this)); } \ + void className::load(QDataStream &stream) { qx::QxSerializeRegistered::load(stream, (*this)); } + +#ifndef _QX_NO_JSON + +#define QX_SERVICE_IX_PARAMETER_QJSON_HPP(className) \ +public: \ + virtual QJsonValue saveToJson() const; \ + virtual qx_bool loadFromJson(const QJsonValue &val); + +#define QX_SERVICE_IX_PARAMETER_QJSON_CPP(className) \ + QJsonValue className::saveToJson() const { return qx::cvt::detail::QxSerializeJsonRegistered::save((*this), ""); } \ + qx_bool className::loadFromJson(const QJsonValue &val) { return qx::cvt::detail::QxSerializeJsonRegistered::load(val, (*this), ""); } + +#else // _QX_NO_JSON +#define QX_SERVICE_IX_PARAMETER_QJSON_HPP(className) /* Nothing */ +#define QX_SERVICE_IX_PARAMETER_QJSON_CPP(className) /* Nothing */ +#endif // _QX_NO_JSON + +#define QX_SERVICE_IX_PARAMETER_SERIALIZATION_HPP(className) \ + QX_SERVICE_IX_PARAMETER_QDATASTREAM_HPP(className) \ + QX_SERVICE_IX_PARAMETER_QJSON_HPP(className) \ +public: \ + virtual void registerClass() const; \ + virtual QString getClassName() const; + +#define QX_SERVICE_IX_PARAMETER_SERIALIZATION_CPP(className) \ + QX_SERVICE_IX_PARAMETER_QDATASTREAM_CPP(className) \ + QX_SERVICE_IX_PARAMETER_QJSON_CPP(className) \ + void className::registerClass() const { qx::QxClass::getSingleton(); } \ + QString className::getClassName() const { return #className; } + +#endif // _IX_SERVICE_PARAMETER_H_ +#endif // _QX_ENABLE_QT_NETWORK diff --git a/include/QxService/IxService.h b/include/QxService/IxService.h new file mode 100644 index 0000000..8c2a72b --- /dev/null +++ b/include/QxService/IxService.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _IX_SERVICE_H_ +#define _IX_SERVICE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxService.h + * \author XDL Team + * \ingroup QxService + * \brief Common interface for all services defined with QxService module of QxOrm library + */ + +#include + +#ifndef _QX_NO_JSON +#include +#endif // _QX_NO_JSON + +#include + +#include + +#include + +#include + +namespace qx +{ + namespace service + { + class QxTransaction; + class IxService; + } // namespace service +} // namespace qx + +QX_DLL_EXPORT QDataStream &operator<<(QDataStream &stream, const qx::service::IxService &t) QX_USED; +QX_DLL_EXPORT QDataStream &operator>>(QDataStream &stream, qx::service::IxService &t) QX_USED; + +#ifndef _QX_NO_JSON +namespace qx +{ + namespace cvt + { + namespace detail + { + template <> + struct QxConvert_ToJson; + template <> + struct QxConvert_FromJson; + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const qx::service::IxService &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, qx::service::IxService &t, const QString &format) QX_USED; + } // namespace detail + } // namespace cvt +} // namespace qx +#endif // _QX_NO_JSON + +namespace qx +{ + namespace service + { + + /*! + * \ingroup QxService + * \brief qx::service::IxService : common interface for all services defined with QxService module of QxOrm library + * + * Click here to access to a tutorial to explain how to work with QxService module. + */ + class QX_DLL_EXPORT IxService + { + + friend QX_DLL_EXPORT QDataStream & ::operator<<(QDataStream &stream, const qx::service::IxService &t); + friend QX_DLL_EXPORT QDataStream & ::operator>>(QDataStream &stream, qx::service::IxService &t); + +#ifndef _QX_NO_JSON + friend struct qx::cvt::detail::QxConvert_ToJson; + friend struct qx::cvt::detail::QxConvert_FromJson; + friend QX_DLL_EXPORT QJsonValue qx::cvt::detail::QxConvert_ToJson_Helper(const qx::service::IxService &t, const QString &format); + friend QX_DLL_EXPORT qx_bool qx::cvt::detail::QxConvert_FromJson_Helper(const QJsonValue &j, qx::service::IxService &t, const QString &format); +#endif // _QX_NO_JSON + + protected: + QString m_sServiceName; //!< Service name <=> class name + QString m_sServiceMethodName; //!< Service method name to call + IxParameter_ptr m_pInputParameter; //!< List of input parameters (request) + IxParameter_ptr m_pOutputParameter; //!< List of output parameters (reply) + qx_bool m_bMessageReturn; //!< Message return to indicate if an error occured + std::shared_ptr m_pTransaction; //!< Current transaction after executing service method + + public: + IxService(); + IxService(const QString &sServiceName); + virtual ~IxService(); + + QString getServiceName() const { return m_sServiceName; } + QString getServiceMethodName() const { return m_sServiceMethodName; } + IxParameter_ptr getInputParameter_BaseClass() const { return m_pInputParameter; } + IxParameter_ptr getOutputParameter_BaseClass() const { return m_pOutputParameter; } + qx_bool getMessageReturn() const { return m_bMessageReturn; } + std::shared_ptr getTransaction() const; + + void setServiceName(const QString &s) + { + qAssert(!s.isEmpty()); + m_sServiceName = s; + } + void setServiceMethodName(const QString &s) + { + qAssert(!s.isEmpty()); + m_sServiceMethodName = s; + } + void setInputParameter(IxParameter_ptr p) { m_pInputParameter = p; } + void setOutputParameter(IxParameter_ptr p) { m_pOutputParameter = p; } + void setMessageReturn(const qx_bool &b) { m_bMessageReturn = b; } + void setMessageReturn(long l, const QString &s) { m_bMessageReturn = qx_bool(l, s); } + void setTransaction(const std::shared_ptr &p); + + bool isValid() const { return m_bMessageReturn.getValue(); } + bool isValidWithOutput() const { return (isValid() && (m_pOutputParameter.get() != NULL)); } + + virtual void registerClass() const + { + qDebug("[QxOrm] qx::service::IxService : %s", "need to override 'registerClass()' method"); + qAssert(false); + } + virtual void onBeforeProcess() { ; } + virtual void onAfterProcess() { ; } + }; + + typedef std::shared_ptr IxService_ptr; + + } // namespace service +} // namespace qx + +QX_REGISTER_INTERNAL_HELPER_HPP(QX_DLL_EXPORT, qx::service::IxService, 0) + +#endif // _IX_SERVICE_H_ +#endif // _QX_ENABLE_QT_NETWORK diff --git a/include/QxService/QxClientAsync.h b/include/QxService/QxClientAsync.h new file mode 100644 index 0000000..435d5ef --- /dev/null +++ b/include/QxService/QxClientAsync.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _QX_SERVICE_CLIENT_ASYNC_H_ +#define _QX_SERVICE_CLIENT_ASYNC_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxClientAsync.h + * \author XDL Team + * \ingroup QxService + * \brief Provide a class helper to easily execute an asynchronous transaction using a multi-thread process + */ + +#include +#include + +namespace qx +{ + namespace service + { + + /*! + * \ingroup QxService + * \brief qx::service::QxClientAsync : class helper to easily execute an asynchronous transaction using a multi-thread process + * + * Click here to access to a tutorial to explain how to work with QxService module. + */ + class QxClientAsync : public QThread + { + + protected: + IxService_ptr m_pService; //!< Service to execute in thread + QString m_sServiceMethod; //!< Service method to call + + public: + QxClientAsync() : QThread() { ; } + virtual ~QxClientAsync() + { + if (isRunning()) + { + qDebug("[QxOrm] qx::service::QxClientAsync thread is running : %s", "wait thread finished"); + wait(); + } + } + + IxService_ptr getService() const { return m_pService; } + void setService(IxService_ptr service, const QString &method) + { + qAssert(!isRunning()); + m_pService = service; + m_sServiceMethod = method; + } + + protected: + virtual void run() { qx::service::execute_client(m_pService.get(), m_sServiceMethod); } + }; + + typedef std::shared_ptr QxClientAsync_ptr; + + } // namespace service +} // namespace qx + +#endif // _QX_SERVICE_CLIENT_ASYNC_H_ +#endif // _QX_ENABLE_QT_NETWORK diff --git a/include/QxService/QxConnect.h b/include/QxService/QxConnect.h new file mode 100644 index 0000000..9a54dc3 --- /dev/null +++ b/include/QxService/QxConnect.h @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _QX_SERVICE_CONNECT_H_ +#define _QX_SERVICE_CONNECT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxConnect.h + * \author XDL Team + * \ingroup QxService + * \brief Define connection parameters used by QxService module of QxOrm library + */ + +#ifndef QT_NO_SSL +#include +#include +#include +#include +#include +#endif // QT_NO_SSL + +#ifndef Q_MOC_RUN +#include +#endif // Q_MOC_RUN + +namespace qx +{ + namespace service + { + + /*! + * \ingroup QxService + * \brief qx::service::QxConnect : define connection parameters used by QxService module of QxOrm library (this class is a singleton) + * + * Click here to access to a tutorial to explain how to work with QxService module. + */ + class QX_DLL_EXPORT QxConnect : public qx::QxSingleton + { + + friend class qx::QxSingleton; + + public: + enum serialization_type + { + serialization_binary, + serialization_xml, + serialization_text, + serialization_portable_binary, + serialization_wide_binary, + serialization_wide_xml, + serialization_wide_text, + serialization_polymorphic_binary, + serialization_polymorphic_xml, + serialization_polymorphic_text, + serialization_qt, + serialization_json + }; + + private: + struct QxConnectImpl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + + QxConnect(); + virtual ~QxConnect(); + + public: + QString getIp(); + long getPort(); + serialization_type getSerializationType(); + long getThreadCount(); + int getMaxWait(); + bool getCompressData(); + bool getEncryptData(); + quint64 getEncryptKey(); + long getKeepAlive(); + bool getModeHTTP(); + qlonglong getSessionTimeOut(); + +#ifndef QT_NO_SSL + bool getSSLEnabled(); + QSslConfiguration getSSLConfiguration(); + QList getSSLCACertificates(); + QSslCertificate getSSLLocalCertificate(); + QSslKey getSSLPrivateKey(); + QList getSSLIgnoreErrors(); + QSsl::SslProtocol getSSLProtocol(); + QString getSSLPeerVerifyName(); + QSslSocket::PeerVerifyMode getSSLPeerVerifyMode(); + int getSSLPeerVerifyDepth(); +#endif // QT_NO_SSL + + void setIp(const QString &s); + void setPort(long l); + void setSerializationType(serialization_type e); + void setThreadCount(long l); + void setMaxWait(int i); + void setCompressData(bool b); + void setEncryptData(bool b, quint64 key = 0); + void setKeepAlive(long l); + void setModeHTTP(bool b); + void setSessionTimeOut(qlonglong l); + +#ifndef QT_NO_SSL + void setSSLEnabled(bool b); + void setSSLConfiguration(QSslConfiguration cfg); + void setSSLCACertificates(QList lst); + void setSSLLocalCertificate(QSslCertificate cert); + void setSSLPrivateKey(QSslKey key); + void setSSLIgnoreErrors(QList lst); + void setSSLProtocol(QSsl::SslProtocol e); + void setSSLPeerVerifyName(const QString &s); + void setSSLPeerVerifyMode(QSslSocket::PeerVerifyMode e); + void setSSLPeerVerifyDepth(int i); +#endif // QT_NO_SSL + }; + + } // namespace service +} // namespace qx + +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::service::QxConnect) + +#endif // _QX_SERVICE_CONNECT_H_ +#endif // _QX_ENABLE_QT_NETWORK diff --git a/include/QxService/QxServer.h b/include/QxService/QxServer.h new file mode 100644 index 0000000..5b9533b --- /dev/null +++ b/include/QxService/QxServer.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _QX_SERVICE_SERVER_H_ +#define _QX_SERVICE_SERVER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxServer.h + * \author XDL Team + * \ingroup QxService + * \brief Server side to manage a thread pool of incoming connections (new request from client) + */ + +#include + +namespace qx +{ + namespace service + { + + class QxThreadPool; + class QxThread; + + /*! + * \ingroup QxService + * \brief qx::service::QxServer : server side to manage a thread pool of incoming connections (new request from client) + * + * Click here to access to a tutorial to explain how to work with QxService module. + */ + class QX_DLL_EXPORT QxServer : public QTcpServer + { + + protected: + QxThreadPool *m_pThreadPool; //!< Parent thread pool to prevent from incoming connection + QMutex m_mutex; //!< Mutex => 'QxServer' is thread-safe + + public: + QxServer(QxThreadPool *pool) : QTcpServer(), m_pThreadPool(pool) { qAssert(m_pThreadPool); } + virtual ~QxServer() { ; } + + protected: +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + virtual void incomingConnection(qintptr socketDescriptor); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + virtual void incomingConnection(int socketDescriptor); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + + QxThread *getAvailable() const; + }; + + } // namespace service +} // namespace qx + +#endif // _QX_SERVICE_SERVER_H_ +#endif // _QX_ENABLE_QT_NETWORK diff --git a/include/QxService/QxService.h b/include/QxService/QxService.h new file mode 100644 index 0000000..bc452a1 --- /dev/null +++ b/include/QxService/QxService.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _QX_SERVICE_H_ +#define _QX_SERVICE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxService.h + * \author XDL Team + * \ingroup QxService + * \brief Concrete service class defined with QxService module of QxOrm library + */ + +#include + +#include + +namespace qx +{ + namespace service + { + + /*! + * \ingroup QxService + * \brief qx::service::QxService : concrete service class with INPUT parameters and OUTPUT parameters + * + * Click here to access to a tutorial to explain how to work with QxService module. + */ + template + class QxService : public IxService + { + + protected: + enum + { + is_input_parameter = std::is_base_of::value + }; + enum + { + is_output_parameter = std::is_base_of::value + }; + enum + { + is_input_registered = qx::trait::is_qx_registered::value + }; + enum + { + is_output_registered = qx::trait::is_qx_registered::value + }; + enum + { + is_valid_parameter = (is_input_parameter && is_output_parameter && is_input_registered && is_output_registered) + }; + + typedef std::shared_ptr INPUT_ptr; + typedef std::shared_ptr OUTPUT_ptr; + + public: + QxService(const QString &sServiceName) : IxService(sServiceName) { static_assert(is_valid_parameter, "is_valid_parameter"); } + virtual ~QxService() { ; } + + INPUT_ptr getInputParameter() const { return std::static_pointer_cast(m_pInputParameter); } + OUTPUT_ptr getOutputParameter() const { return std::static_pointer_cast(m_pOutputParameter); } + + virtual void registerClass() const + { + qx::QxClass::getSingleton(); + qx::QxClass::getSingleton(); + } + }; + + } // namespace service +} // namespace qx + +#endif // _QX_SERVICE_H_ +#endif // _QX_ENABLE_QT_NETWORK diff --git a/include/QxService/QxThread.h b/include/QxService/QxThread.h new file mode 100644 index 0000000..8345ff8 --- /dev/null +++ b/include/QxService/QxThread.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef Q_MOC_RUN +#include // Need to include this file for the 'moc' process +#endif // Q_MOC_RUN + +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _QX_SERVICE_THREAD_H_ +#define _QX_SERVICE_THREAD_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxThread.h + * \author XDL Team + * \ingroup QxService + * \brief Thread to execute a transaction of QxService module + */ + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +#ifdef QT_NO_OPENSSL +#ifndef QT_NO_SSL +#define QT_NO_SSL /* Nothing */ +#endif // QT_NO_SSL +#endif // QT_NO_OPENSSL + +#include + +#ifndef QT_NO_SSL +#include +#include +#include +#include +#include +#endif // QT_NO_SSL + +#ifndef Q_MOC_RUN +#include +#endif // Q_MOC_RUN + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#define QX_TYPE_SOCKET_DESC qintptr +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#define QX_TYPE_SOCKET_DESC int +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + +namespace qx +{ + namespace service + { + + class QxThreadPool; + + /*! + * \ingroup QxService + * \brief qx::service::QxThread : thread to execute a transaction of QxService module + * + * Click here to access to a tutorial to explain how to work with QxService module. + */ + class QX_DLL_EXPORT QxThread : public QObject + { + + Q_OBJECT + + protected: + QX_TYPE_SOCKET_DESC m_iSocketDescriptor; //!< Socket descriptor to retrieve 'QTcpSocket' + QxThreadPool *m_pThreadPool; //!< Parent thread pool to set available + QThread *m_pThread; //!< Thread where this worker is executed + QxTransaction_ptr m_pTransaction; //!< Current service transaction + bool m_bIsStopped; //!< Set this flag to 'true' to terminate thread + bool m_bIsDisconnected; //!< Socket has been disconnected + QMutex m_mutex; //!< Mutex => 'QxThread' is thread-safe + + public: + QxThread(QxThreadPool *pool, QThread *thread) : QObject(), m_iSocketDescriptor(0), m_pThreadPool(pool), m_pThread(thread), m_bIsStopped(false), m_bIsDisconnected(false) + { + qAssert(m_pThreadPool); + qAssert(m_pThread); + } + virtual ~QxThread() { clearData(); } + + void init(); + void stop(); + void wait(); + bool isAvailable(); + void execute(QX_TYPE_SOCKET_DESC socketDescriptor); + + protected: + void quit(); + void clearData(); + bool hasBeenStopped(); + void doProcess(QTcpSocket &socket); + bool checkKeepAlive(QTcpSocket &socket); + QX_TYPE_SOCKET_DESC getSocketDescriptor(); + +#ifndef QT_NO_SSL + bool checkSocketSSLEncrypted(QTcpSocket *socket); + QSslSocket *initSocketSSL(); +#endif // QT_NO_SSL + + Q_SIGNALS: + + void error(const QString &err, qx::service::QxTransaction_ptr transaction); + void transactionStarted(qx::service::QxTransaction_ptr transaction); + void transactionFinished(qx::service::QxTransaction_ptr transaction); + void customRequestHandler(qx::service::QxTransaction_ptr transaction); + void incomingConnection(); + void finished(); + + private Q_SLOTS: + + void onCustomRequestHandler(); + void onIncomingConnection(); + void onSocketDisconnected(); + void onSocketReadyRead(); + +#ifndef QT_NO_SSL +#ifndef QT_NO_OPENSSL + void onSocketSSLEncrypted(); + void onSocketSSLErrors(const QList &errors); + void onSocketSSLPeerVerifyError(const QSslError &error); +#endif // QT_NO_OPENSSL +#endif // QT_NO_SSL + }; + + } // namespace service +} // namespace qx + +#endif // _QX_SERVICE_THREAD_H_ +#endif // _QX_ENABLE_QT_NETWORK diff --git a/include/QxService/QxThreadPool.h b/include/QxService/QxThreadPool.h new file mode 100644 index 0000000..e433b8f --- /dev/null +++ b/include/QxService/QxThreadPool.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef Q_MOC_RUN +#include // Need to include this file for the 'moc' process +#endif // Q_MOC_RUN + +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _QX_SERVICE_THREAD_POOL_H_ +#define _QX_SERVICE_THREAD_POOL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxThreadPool.h + * \author XDL Team + * \ingroup QxService + * \brief Thread pool to manage list of threads for executing all transactions of QxService module + */ + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +#include + +#ifndef Q_MOC_RUN +#include +#include +#endif // Q_MOC_RUN + +namespace qx +{ + namespace service + { + + class QxThread; + + /*! + * \ingroup QxService + * \brief qx::service::QxThreadPool : thread-safe thread pool to manage list of threads for executing all transactions of QxService module + * + * Click here to access to a tutorial to explain how to work with QxService module. + */ + class QX_DLL_EXPORT QxThreadPool : public QThread + { + + Q_OBJECT + + protected: + QList m_lstAllServices; //!< List of all services created by 'QxThreadPool' + QQueue m_lstAvailable; //!< List of services available to execute process + bool m_bIsStopped; //!< Flag to indicate if thread has been stopped + QMutex m_mutex; //!< Mutex => 'QxThreadPool' is thread-safe + + public: + QxThreadPool() : QThread(), m_bIsStopped(false) { ; } + virtual ~QxThreadPool() + { + if (isRunning()) + { + qDebug("[QxOrm] qx::service::QxThreadPool thread is running : %s", "quit and wait"); + quit(); + wait(); + } + } + + bool isStopped() const; + QxThread *getAvailable(); + void setAvailable(QxThread *p); + void raiseError(const QString &err, QxTransaction_ptr transaction); + + static void sleepThread(unsigned long msecs) { QThread::msleep(msecs); } + + protected: + virtual void run(); + + void runServer(); + void initServices(); + void clearServices(); + + Q_SIGNALS: + + void error(const QString &err, qx::service::QxTransaction_ptr transaction); + void transactionStarted(qx::service::QxTransaction_ptr transaction); + void transactionFinished(qx::service::QxTransaction_ptr transaction); + void customRequestHandler(qx::service::QxTransaction_ptr transaction); + void serverIsRunning(bool bIsRunning, qx::service::QxServer *pServer); + }; + + typedef std::shared_ptr QxThreadPool_ptr; + + } // namespace service +} // namespace qx + +#endif // _QX_SERVICE_THREAD_POOL_H_ +#endif // _QX_ENABLE_QT_NETWORK diff --git a/include/QxService/QxTools.h b/include/QxService/QxTools.h new file mode 100644 index 0000000..2e91351 --- /dev/null +++ b/include/QxService/QxTools.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _QX_SERVICE_TOOLS_H_ +#define _QX_SERVICE_TOOLS_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxTools.h + * \author XDL Team + * \ingroup QxService + * \brief Provide some tools to read/write on socket all datas transfered by QxService module of QxOrm library + */ + +#include + +namespace qx +{ + namespace service + { + + /*! + * \ingroup QxService + * \brief qx::service::QxTools : provide some tools to read/write on socket all datas transfered by QxService module of QxOrm library + * + * Click here to access to a tutorial to explain how to work with QxService module. + */ + class QX_DLL_EXPORT QxTools + { + + public: + static qx_bool readSocket(QTcpSocket &socket, QxTransaction &transaction, quint32 &size); + static qx_bool writeSocket(QTcpSocket &socket, QxTransaction &transaction, quint32 &size); + }; + + } // namespace service +} // namespace qx + +#endif // _QX_SERVICE_TOOLS_H_ +#endif // _QX_ENABLE_QT_NETWORK diff --git a/include/QxService/QxTransaction.h b/include/QxService/QxTransaction.h new file mode 100644 index 0000000..76920c3 --- /dev/null +++ b/include/QxService/QxTransaction.h @@ -0,0 +1,242 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef Q_MOC_RUN +#include // Need to include this file for the 'moc' process +#endif // Q_MOC_RUN + +#ifdef _QX_ENABLE_QT_NETWORK +#ifndef _QX_SERVICE_TRANSACTION_H_ +#define _QX_SERVICE_TRANSACTION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxTransaction.h + * \author XDL Team + * \ingroup QxService + * \brief Transaction of QxService module (contains request from client and reply from server) + */ + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +#include + +#ifndef _QX_NO_JSON +#include +#endif // _QX_NO_JSON + +#include + +#ifndef QT_NO_SSL +#include +#include +#include +#include +#include +#endif // QT_NO_SSL + +#ifndef Q_MOC_RUN +#include +#include +#include +#include +#include +#endif // Q_MOC_RUN + +namespace qx +{ + namespace service + { + class QxTransaction; + } // namespace service +} // namespace qx + +QX_DLL_EXPORT QDataStream &operator<<(QDataStream &stream, const qx::service::QxTransaction &t) QX_USED; +QX_DLL_EXPORT QDataStream &operator>>(QDataStream &stream, qx::service::QxTransaction &t) QX_USED; + +#ifndef _QX_NO_JSON +namespace qx +{ + namespace cvt + { + namespace detail + { + template <> + struct QxConvert_ToJson; + template <> + struct QxConvert_FromJson; + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const qx::service::QxTransaction &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, qx::service::QxTransaction &t, const QString &format) QX_USED; + } // namespace detail + } // namespace cvt +} // namespace qx +#endif // _QX_NO_JSON + +namespace qx +{ + namespace service + { + + /*! + * \ingroup QxService + * \brief qx::service::QxTransaction : transaction of QxService module (contains request from client and reply from server) + * + * Click here to access to a tutorial to explain how to work with QxService module. + */ + class QX_DLL_EXPORT QxTransaction : public QObject + { + + Q_OBJECT + QX_REGISTER_FRIEND_CLASS(qx::service::QxTransaction) + + friend QX_DLL_EXPORT QDataStream & ::operator<<(QDataStream &stream, const qx::service::QxTransaction &t); + friend QX_DLL_EXPORT QDataStream & ::operator>>(QDataStream &stream, qx::service::QxTransaction &t); + +#ifndef _QX_NO_JSON + friend struct qx::cvt::detail::QxConvert_ToJson; + friend struct qx::cvt::detail::QxConvert_FromJson; + friend QX_DLL_EXPORT QJsonValue qx::cvt::detail::QxConvert_ToJson_Helper(const qx::service::QxTransaction &t, const QString &format); + friend QX_DLL_EXPORT qx_bool qx::cvt::detail::QxConvert_FromJson_Helper(const QJsonValue &j, qx::service::QxTransaction &t, const QString &format); +#endif // _QX_NO_JSON + + public: + enum connection_status + { + conn_none, + conn_keep_alive, + conn_close + }; + + protected: + QString m_sTransactionId; //!< Transaction id (GUID) + quint32 m_uiInputTransactionSize; //!< Input transaction size + quint32 m_uiOutputTransactionSize; //!< Output transaction size + QDateTime m_dtTransactionBegin; //!< Date-time transaction begin + QDateTime m_dtTransactionRequestSent; //!< Date-time transaction request sent + QDateTime m_dtTransactionRequestReceived; //!< Date-time transaction request received + QDateTime m_dtTransactionReplySent; //!< Date-time transaction reply sent + QDateTime m_dtTransactionReplyReceived; //!< Date-time transaction reply received + QDateTime m_dtTransactionEnd; //!< Date-time transaction end + QString m_sIpSource; //!< Ip address source (request) + QString m_sIpTarget; //!< Ip address target (reply) + long m_lPortSource; //!< Port number source (request) + long m_lPortTarget; //!< Port number target (reply) + QString m_sServiceName; //!< Service name to create using 'QxFactory' + QString m_sServiceMethod; //!< Service method to call to execute transaction + qx_bool m_bMessageReturn; //!< Message return to indicate if an error occured + IxParameter_ptr m_pInputParameter; //!< List of input parameters (request) + IxParameter_ptr m_pOutputParameter; //!< List of output parameters (reply) + IxService_ptr m_pServiceInstance; //!< Service instance created by 'm_sServiceName' property + connection_status m_eForceConnectionStatus; //!< Sometimes we have to force connection status + + public: + QxTransaction() : QObject(), m_uiInputTransactionSize(0), m_uiOutputTransactionSize(0), m_lPortSource(0), m_lPortTarget(0), m_eForceConnectionStatus(conn_none) { ; } + virtual ~QxTransaction() { ; } + virtual void clear(); + + QString getTransactionId() const { return m_sTransactionId; } + quint32 getInputTransactionSize() const { return m_uiInputTransactionSize; } + quint32 getOutputTransactionSize() const { return m_uiOutputTransactionSize; } + QDateTime getTransactionBegin() const { return m_dtTransactionBegin; } + QDateTime getTransactionRequestSent() const { return m_dtTransactionRequestSent; } + QDateTime getTransactionRequestReceived() const { return m_dtTransactionRequestReceived; } + QDateTime getTransactionReplySent() const { return m_dtTransactionReplySent; } + QDateTime getTransactionReplyReceived() const { return m_dtTransactionReplyReceived; } + QDateTime getTransactionEnd() const { return m_dtTransactionEnd; } + QString getIpSource() const { return m_sIpSource; } + QString getIpTarget() const { return m_sIpTarget; } + long getPortSource() const { return m_lPortSource; } + long getPortTarget() const { return m_lPortTarget; } + QString getServiceName() const { return m_sServiceName; } + QString getServiceMethod() const { return m_sServiceMethod; } + qx_bool getMessageReturn() const { return m_bMessageReturn; } + IxParameter_ptr getInputParameter() const { return m_pInputParameter; } + IxParameter_ptr getOutputParameter() const { return m_pOutputParameter; } + connection_status getForceConnectionStatus() const { return m_eForceConnectionStatus; } + + void setTransactionId(const QString &s) { m_sTransactionId = s; } + void setInputTransactionSize(quint32 ui) { m_uiInputTransactionSize = ui; } + void setOutputTransactionSize(quint32 ui) { m_uiOutputTransactionSize = ui; } + void setTransactionBegin(const QDateTime &dt) { m_dtTransactionBegin = dt; } + void setTransactionRequestSent(const QDateTime &dt) { m_dtTransactionRequestSent = dt; } + void setTransactionRequestReceived(const QDateTime &dt) { m_dtTransactionRequestReceived = dt; } + void setTransactionReplySent(const QDateTime &dt) { m_dtTransactionReplySent = dt; } + void setTransactionReplyReceived(const QDateTime &dt) { m_dtTransactionReplyReceived = dt; } + void setTransactionEnd(const QDateTime &dt) { m_dtTransactionEnd = dt; } + void setIpSource(const QString &s) { m_sIpSource = s; } + void setIpTarget(const QString &s) { m_sIpTarget = s; } + void setPortSource(long l) { m_lPortSource = l; } + void setPortTarget(long l) { m_lPortTarget = l; } + void setServiceName(const QString &s) { m_sServiceName = s; } + void setServiceMethod(const QString &s) { m_sServiceMethod = s; } + void setMessageReturn(const qx_bool &b) { m_bMessageReturn = b; } + void setInputParameter(IxParameter_ptr p) { m_pInputParameter = p; } + void setOutputParameter(IxParameter_ptr p) { m_pOutputParameter = p; } + void setForceConnectionStatus(connection_status e) { m_eForceConnectionStatus = e; } + + virtual void executeServer(); + virtual qx_bool writeSocketServer(QTcpSocket &socket); + virtual qx_bool readSocketServer(QTcpSocket &socket); + + virtual void executeClient(IxService *pService, const QString &sMethod); + virtual qx_bool writeSocketClient(QTcpSocket &socket); + virtual qx_bool readSocketClient(QTcpSocket &socket); + + QString getInfos() const; + + protected: +#ifndef QT_NO_SSL + QSslSocket *initSocketSSL(); + bool checkSocketSSLEncrypted(QTcpSocket *socket); +#endif // QT_NO_SSL + + Q_SIGNALS: + + void onCustomRequestHandler(); + }; + + typedef std::shared_ptr QxTransaction_ptr; + QX_DLL_EXPORT void execute_client(IxService *pService, const QString &sMethod); + + } // namespace service +} // namespace qx + +QX_REGISTER_INTERNAL_HELPER_HPP(QX_DLL_EXPORT, qx::service::QxTransaction, 0) + +#endif // _QX_SERVICE_TRANSACTION_H_ +#endif // _QX_ENABLE_QT_NETWORK diff --git a/include/QxServices.h b/include/QxServices.h new file mode 100644 index 0000000..e7b6512 --- /dev/null +++ b/include/QxServices.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SERVICES_H_ +#define _QX_SERVICES_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxServices.h + * \author XDL Team + * \ingroup QxService + * \brief Include all headers required to use QxService module : https://www.qxorm.com/qxorm_en/manual.html#manual_80 + */ + +#include + +#ifdef _QX_ENABLE_QT_NETWORK + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // _QX_ENABLE_QT_NETWORK + +#endif // _QX_SERVICES_H_ diff --git a/include/QxSingleton/IxSingleton.h b/include/QxSingleton/IxSingleton.h new file mode 100644 index 0000000..7a36a52 --- /dev/null +++ b/include/QxSingleton/IxSingleton.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_SINGLETON_H_ +#define _IX_SINGLETON_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxSingleton.h + * \author XDL Team + * \ingroup QxSingleton + * \brief Common interface for all singleton of QxOrm library + */ + +namespace qx +{ + + class QxSingletonX; + + /*! + * \ingroup QxSingleton + * \brief qx::IxSingleton : common interface for all singleton of QxOrm library + */ + class QX_DLL_EXPORT IxSingleton + { + + friend class QxSingletonX; + + protected: + QString m_sKeySingleton; //!< Singleton key used by 'QxSingletonX' container + + protected: + IxSingleton(const QString &sKey); + virtual ~IxSingleton(); + + virtual void deleteInstance() = 0; + + static void initQxSingletonX(); + + private: + IxSingleton(const IxSingleton &other) { Q_UNUSED(other); } + IxSingleton &operator=(const IxSingleton &other) + { + Q_UNUSED(other); + return (*this); + } + }; + +} // namespace qx + +#endif // _IX_SINGLETON_H_ diff --git a/include/QxSingleton/QxSingleton.h b/include/QxSingleton/QxSingleton.h new file mode 100644 index 0000000..009856b --- /dev/null +++ b/include/QxSingleton/QxSingleton.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SINGLETON_H_ +#define _QX_SINGLETON_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSingleton.h + * \author XDL Team + * \ingroup QxSingleton + * \brief Concrete class to define a thread-safe singleton of QxOrm library + */ + +#include +#include + +#include + +namespace qx +{ + + /*! + * \ingroup QxSingleton + * \brief qx::QxSingleton : concrete class to define a thread-safe singleton of type T + */ + template + class QxSingleton : public IxSingleton + { + + private: + static T *m_pSingleton; //!< Singleton -> only 1 instance allowed + static QMutex m_oMutexSingleton; //!< Mutex -> 'QxSingleton' is thread-safe + + protected: + QxSingleton(const QString &sKey) : IxSingleton(sKey) { ; } + virtual ~QxSingleton() { ; } + + public: + static T *getSingleton(); + static void deleteSingleton(); + + protected: + static bool isSingletonNull() { return (m_pSingleton == NULL); } + + virtual void deleteInstance() { QxSingleton::deleteSingleton(); } + }; + +} // namespace qx + +#include "../../inl/QxSingleton/QxSingleton.inl" + +#endif // _QX_SINGLETON_H_ diff --git a/include/QxSingleton/QxSingletonInit.h b/include/QxSingleton/QxSingletonInit.h new file mode 100644 index 0000000..a4dc3f5 --- /dev/null +++ b/include/QxSingleton/QxSingletonInit.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SINGLETON_INIT_H_ +#define _QX_SINGLETON_INIT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSingletonInit.h + * \author XDL Team + * \ingroup QxSingleton + * \brief Initialize some singletons hosted by QxOrm shared library + */ + +#include + +#include + +#include + +#ifdef _QX_ENABLE_QT_NETWORK +#include +#include +#include +#endif // _QX_ENABLE_QT_NETWORK + +#include + +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::QxClass) +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::QxDataMemberX) + +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::QxClass) +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::QxDataMemberX) + +#ifdef _QX_ENABLE_QT_NETWORK + +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::QxClass) +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::QxDataMemberX) + +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::QxClass) +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::QxDataMemberX) + +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::QxClass) +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::QxDataMemberX) + +#endif // _QX_ENABLE_QT_NETWORK + +#endif // _QX_SINGLETON_INIT_H_ diff --git a/include/QxSingleton/QxSingletonX.h b/include/QxSingleton/QxSingletonX.h new file mode 100644 index 0000000..b44b1f5 --- /dev/null +++ b/include/QxSingleton/QxSingletonX.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_SINGLETON_X_H_ +#define _QX_SINGLETON_X_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxSingletonX.h + * \author XDL Team + * \ingroup QxSingleton + * \brief List of all singleton defined by QxOrm library + */ + +#include + +#include + +#include + +namespace qx +{ + + /*! + * \ingroup QxSingleton + * \brief qx::QxSingletonX : list of all singleton defined by QxOrm library (this container is also a singleton) + */ + class QX_DLL_EXPORT QxSingletonX : public QxSingleton + { + + friend class QxSingleton; + friend class IxSingleton; + + private: + static QHash &getMapSingletonX(); //!< Collection of all 'IxSingleton' pointer + static QMutex *getMutexSingletonX(); //!< Mutex -> 'QxSingletonX' is thread-safe + static bool &getOnClearSingletonX(); //!< Clear collection of 'IxSingleton' in event + + private: + QxSingletonX(); + virtual ~QxSingletonX() { ; } + + static bool addSingleton(const QString &sKey, IxSingleton *pSingleton); + static bool removeSingleton(const QString &sKey); + static void deleteAllSingleton(); + }; + +} // namespace qx + +QX_DLL_EXPORT_QX_SINGLETON_HPP(qx::QxSingletonX) + +#endif // _QX_SINGLETON_X_H_ diff --git a/include/QxTraits/archive_printable.h b/include/QxTraits/archive_printable.h new file mode 100644 index 0000000..3fa6fc7 --- /dev/null +++ b/include/QxTraits/archive_printable.h @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_ARCHIVE_PRINTABLE_H_ +#define _QX_ARCHIVE_PRINTABLE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file archive_printable.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_archive_printable::value : define if a boost::archive type is readable by a human (for example XML archive) or not (for example binary archive) + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_archive_printable::value : define if a boost::archive type is readable by a human (for example XML archive) or not (for example binary archive) + */ + template + struct is_archive_printable + { + enum + { + value = false + }; + }; + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#if _QX_SERIALIZE_POLYMORPHIC + template <> + struct is_archive_printable + { + enum + { + value = true + }; + }; + template <> + struct is_archive_printable + { + enum + { + value = true + }; + }; +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_TEXT + template <> + struct is_archive_printable + { + enum + { + value = true + }; + }; + template <> + struct is_archive_printable + { + enum + { + value = true + }; + }; +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML + template <> + struct is_archive_printable + { + enum + { + value = true + }; + }; + template <> + struct is_archive_printable + { + enum + { + value = true + }; + }; +#endif // _QX_SERIALIZE_XML + +#if _QX_SERIALIZE_WIDE_TEXT + template <> + struct is_archive_printable + { + enum + { + value = true + }; + }; + template <> + struct is_archive_printable + { + enum + { + value = true + }; + }; +#endif // _QX_SERIALIZE_WIDE_TEXT + +#if _QX_SERIALIZE_WIDE_XML + template <> + struct is_archive_printable + { + enum + { + value = true + }; + }; + template <> + struct is_archive_printable + { + enum + { + value = true + }; + }; +#endif // _QX_SERIALIZE_WIDE_XML + +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + } // namespace trait +} // namespace qx + +#endif // _QX_ARCHIVE_PRINTABLE_H_ diff --git a/include/QxTraits/archive_wide_traits.h b/include/QxTraits/archive_wide_traits.h new file mode 100644 index 0000000..b9c525a --- /dev/null +++ b/include/QxTraits/archive_wide_traits.h @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_ARCHIVE_WIDE_TRAITS_H_ +#define _QX_ARCHIVE_WIDE_TRAITS_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file archive_wide_traits.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_archive_wide::value : define if a boost::archive type uses wide string character and stream (for example std::wstring) or not (for example std::string) + */ + +#include +#include +#include +#include + +#include + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_archive_wide::value : define if a boost::archive type uses wide string character and stream (for example std::wstring) or not (for example std::string) + */ + template + struct is_archive_wide + { + enum + { + value = false + }; + }; + +#if _QX_SERIALIZE_WIDE_BINARY + template <> + struct is_archive_wide + { + enum + { + value = true + }; + }; + template <> + struct is_archive_wide + { + enum + { + value = true + }; + }; +#endif // _QX_SERIALIZE_WIDE_BINARY + +#if _QX_SERIALIZE_WIDE_TEXT + template <> + struct is_archive_wide + { + enum + { + value = true + }; + }; + template <> + struct is_archive_wide + { + enum + { + value = true + }; + }; +#endif // _QX_SERIALIZE_WIDE_TEXT + +#if _QX_SERIALIZE_WIDE_XML + template <> + struct is_archive_wide + { + enum + { + value = true + }; + }; + template <> + struct is_archive_wide + { + enum + { + value = true + }; + }; +#endif // _QX_SERIALIZE_WIDE_XML + + template + class archive_wide_traits + { + + public: + enum + { + is_wide = qx::trait::is_archive_wide::value + }; + + typedef typename std::conditional::type type_char; + typedef typename std::conditional::type type_string; + + typedef typename std::conditional::type type_istream; + typedef typename std::conditional::type type_ostream; + + typedef typename std::conditional::type type_stringstream; + typedef typename std::conditional::type type_istringstream; + typedef typename std::conditional::type type_ostringstream; + + typedef typename std::conditional::type type_fstream; + typedef typename std::conditional::type type_ifstream; + typedef typename std::conditional::type type_ofstream; + + static inline QString toQString(const type_string &str) { return cvtQString::toQString(str); } + static inline void fromQString(const QString &str, type_string &result) { cvtQString::fromQString(str, result); } + + static inline QByteArray toQByteArray(const type_string &str, type_string *owner) { return cvtQByteArray::toQByteArray(str, owner); } + static inline void fromQByteArray(const QByteArray &data, type_string &result) { cvtQByteArray::fromQByteArray(data, result); } + + private: + template + struct cvtQString + { +#ifndef QT_NO_STL + static inline QString toQString(const std::string &str) { return QString::fromStdString(str); } + static inline void fromQString(const QString &str, std::string &result) { result = str.toStdString(); } +#else // QT_NO_STL + static inline QString toQString(const std::string &str) { return QString::fromLatin1(str.data(), int(str.size())); } + static inline void fromQString(const QString &str, std::string &result) { result = str.toLatin1().constData(); } +#endif // QT_NO_STL + }; + + template + struct cvtQString + { +#if ((!defined(QT_NO_STL)) && (!defined(QT_NO_STL_WCHAR))) + static inline QString toQString(const std::wstring &str) { return QString::fromStdWString(str); } + static inline void fromQString(const QString &str, std::wstring &result) { result = str.toStdWString(); } +#else // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + static inline QString toQString(const std::wstring &str) + { + Q_UNUSED(str); + qAssert(false); /* Need STL compatibility ! */ + return QString(); + } + static inline void fromQString(const QString &str, std::wstring &result) + { + Q_UNUSED(str); + Q_UNUSED(result); + qAssert(false); /* Need STL compatibility ! */ + } +#endif // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + }; + + template + struct cvtQByteArray + { + static inline QByteArray toQByteArray(const std::string &str, std::string *owner) + { + if (owner) + { + (*owner) = str; + }; + return (owner ? QByteArray::fromRawData(owner->data(), owner->size()) : QByteArray(str.data(), str.size())); + } + static inline void fromQByteArray(const QByteArray &data, std::string &result) + { + result.clear(); + result.append(data.constData(), data.size()); + } + }; + + template + struct cvtQByteArray + { + static inline QByteArray toQByteArray(const std::wstring &str, std::wstring *owner) +#if ((!defined(QT_NO_STL)) && (!defined(QT_NO_STL_WCHAR))) + { + Q_UNUSED(owner); + return QString::fromStdWString(str).toUtf8(); + } +#else // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + { + Q_UNUSED(owner); + Q_UNUSED(str); + qAssert(false); /* Need STL compatibility ! */ + return QByteArray(); + } +#endif // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + + static inline void fromQByteArray(const QByteArray &data, std::wstring &result) +#if ((!defined(QT_NO_STL)) && (!defined(QT_NO_STL_WCHAR))) + { + result = QString::fromUtf8(data.constData(), data.size()).toStdWString(); + } +#else // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + { + Q_UNUSED(data); + Q_UNUSED(result); + qAssert(false); /* Need STL compatibility ! */ + } +#endif // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + }; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_ARCHIVE_WIDE_TRAITS_H_ diff --git a/include/QxTraits/construct_null_qvariant.h b/include/QxTraits/construct_null_qvariant.h new file mode 100644 index 0000000..8bcce28 --- /dev/null +++ b/include/QxTraits/construct_null_qvariant.h @@ -0,0 +1,727 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_CONSTRUCT_NULL_QVARIANT_H_ +#define _QX_CONSTRUCT_NULL_QVARIANT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file construct_null_qvariant.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::construct_null_qvariant::get() : create a NULL QVariant which matches QVariant::Type with type T + */ + +#include + +#include +#include +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::construct_null_qvariant::get() : create a NULL QVariant which matches QVariant::Type with type T + */ + template + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::BitArray); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::BitArray); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::BitArray); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::BitArray); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::ByteArray); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::ByteArray); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::ByteArray); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::ByteArray); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Bool); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Bool); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Bool); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Bool); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Int); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Int); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Int); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Int); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Int); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Int); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Int); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Int); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::LongLong); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::LongLong); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::LongLong); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::LongLong); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::LongLong); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::LongLong); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::LongLong); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::LongLong); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Double); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Double); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Double); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Double); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Double); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Double); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Double); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Double); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::UInt); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::UInt); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::UInt); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::UInt); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::UInt); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::UInt); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::UInt); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::UInt); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::ULongLong); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::ULongLong); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::ULongLong); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::ULongLong); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::ULongLong); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::ULongLong); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::ULongLong); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::ULongLong); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::StringList); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::StringList); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::StringList); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::StringList); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Date); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Date); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Date); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Date); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::DateTime); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::DateTime); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::DateTime); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::DateTime); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Time); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Time); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Time); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Time); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::String); } + }; + +#ifdef _QX_ENABLE_QT_GUI + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Brush); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Brush); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Brush); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Brush); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Color); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Color); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Color); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Color); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Font); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Font); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Font); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Font); } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Matrix); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Matrix); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Matrix); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Matrix); } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Region); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Region); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Region); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Region); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Image); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Image); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Image); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Image); } + }; + + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Pixmap); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Pixmap); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Pixmap); } + }; + template <> + struct construct_null_qvariant + { + static inline QVariant get() { return QVariant(QVariant::Pixmap); } + }; + +#endif // _QX_ENABLE_QT_GUI + + } // namespace trait +} // namespace qx + +#endif // _QX_CONSTRUCT_NULL_QVARIANT_H_ diff --git a/include/QxTraits/construct_ptr.h b/include/QxTraits/construct_ptr.h new file mode 100644 index 0000000..560f16d --- /dev/null +++ b/include/QxTraits/construct_ptr.h @@ -0,0 +1,242 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_CONSTRUCT_PTR_H_ +#define _QX_CONSTRUCT_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file construct_ptr.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::construct_ptr::get(T & t, bool bReset = false) : instantiate (or reset) a new pointer, support both nude-pointer and smart-pointer of boost, Qt and QxOrm libraries + */ + +#include + +#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) +#include +#endif // (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::construct_ptr::get(T & t, bool bReset = false) : instantiate (or reset) a new pointer, support both nude-pointer and smart-pointer of boost, Qt and QxOrm libraries + */ + template + struct construct_ptr + { + + private: + typedef typename std::remove_pointer::type type_ptr; + + public: + static inline void get(T &t, bool bReset = false) + { + new_ptr::value, 0>::get(t, bReset); + } + + private: + template + struct new_ptr + { + static inline void get(T &t, bool bReset) + { + if (bReset) + { + t = NULL; + } + else + { + t = new type_ptr(); + } + } + }; + + template + struct new_ptr + { + static inline void get(T &t, bool bReset) + { + Q_UNUSED(t); + Q_UNUSED(bReset); + qDebug("[QxOrm] qx::trait::construct_ptr : %s", "cannot instantiate abstract class"); + } + }; + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct construct_ptr> + { + static inline void get(boost::scoped_ptr &t, bool bReset = false) + { + if (bReset) + { + t.reset(); + } + else + { + t.reset(new T()); + } + } + }; + + template + struct construct_ptr> + { + static inline void get(boost::shared_ptr &t, bool bReset = false) + { + if (bReset) + { + t.reset(); + } + else + { + t.reset(new T()); + } + } + }; + + template + struct construct_ptr> + { + static inline void get(boost::intrusive_ptr &t, bool bReset = false) + { + if (bReset) + { + t.reset(); + } + else + { + t.reset(new T()); + } + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct construct_ptr> + { + static inline void get(QSharedPointer &t, bool bReset = false) + { + if (bReset) + { + t = QSharedPointer(); + } + else + { + t = QSharedPointer(new T()); + } + } + }; + +#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) + template + struct construct_ptr> + { + static inline void get(QScopedPointer &t, bool bReset = false) + { + if (bReset) + { + t = QScopedPointer(); + } + else + { + t = QScopedPointer(new T()); + } + } + }; +#endif // (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) + + template + struct construct_ptr> + { + static inline void get(qx::dao::ptr &t, bool bReset = false) + { + if (bReset) + { + t = qx::dao::ptr(); + } + else + { + t = qx::dao::ptr(new T()); + } + } + }; + + template + struct construct_ptr> + { + static inline void get(std::unique_ptr &t, bool bReset = false) + { + if (bReset) + { + t.reset(); + } + else + { + t.reset(new T()); + } + } + }; + + template + struct construct_ptr> + { + static inline void get(std::shared_ptr &t, bool bReset = false) + { + if (bReset) + { + t.reset(); + } + else + { + t = std::make_shared(); + } + } + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_CONSTRUCT_PTR_H_ diff --git a/include/QxTraits/generic_container.h b/include/QxTraits/generic_container.h new file mode 100644 index 0000000..4b11eb7 --- /dev/null +++ b/include/QxTraits/generic_container.h @@ -0,0 +1,662 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_GENERIC_CONTAINER_H_ +#define _QX_GENERIC_CONTAINER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file generic_container.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::generic_container : provide some tools to manage all containers without knowing its type + */ + +#include +#include +#include +#include + +namespace qx +{ + namespace trait + { + + class no_type + { + private: + void dummy() const { ; } + }; + + /*! + * \ingroup QxTraits + * \brief qx::trait::generic_container : provide some tools to manage all containers without knowing its type + */ + template + struct generic_container + { + typedef no_type type_item; + typedef no_type type_key; + typedef no_type type_value; + typedef no_type type_value_qx; + typedef no_type type_iterator; + }; + + template + struct generic_container_item + { + + typedef Key type_key; + typedef Value type_value; + typedef typename qx::trait::remove_attr::type type_value_qx_tmp; + typedef typename qx::trait::remove_smart_ptr::type type_value_qx; + + enum + { + is_key_pointer = (std::is_pointer::value || qx::trait::is_smart_ptr::value) + }; + enum + { + is_value_pointer = (std::is_pointer::value || qx::trait::is_smart_ptr::value) + }; + + private: + std::pair m_pair; + + public: + generic_container_item() { ; } + generic_container_item(const Key &key, const Value &value) { m_pair = std::make_pair(key, value); } + ~generic_container_item() { ; } + + inline type_key &key() { return m_pair.first; } + inline type_value &value() { return m_pair.second; } + inline const type_key &key() const { return m_pair.first; } + inline const type_value &value() const { return m_pair.second; } + inline type_value_qx &value_qx() { return value_qx_Helper::get(m_pair.second); } + inline const type_value_qx &value_qx() const { return value_qx_Helper::get(m_pair.second); } + + inline void key(const Key &key) { m_pair.first = key; } + inline void value(const Value &value) { m_pair.second = value; } + + static inline type_key newKey() { return new_Helper::get(); } + static inline type_value newValue() { return new_Helper::get(); } + + private: + template + struct new_Helper + { + static inline T get() + { + T t; + qx::trait::construct_ptr::get(t); + return t; + } + }; + + template + struct new_Helper + { + static inline T get() { return T(); } + }; + + template + struct value_qx_Helper + { + static inline U &get(T &t) { return (*t); } + }; + + template + struct value_qx_Helper + { + static inline U &get(T &t) { return t; } + }; + }; + + } // namespace trait +} // namespace qx + +#define QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(TypeContainer, TypeKey, TypeValue) \ + typedef qx::trait::generic_container_item type_item; \ + typedef typename type_item::type_key type_key; \ + typedef typename type_item::type_value type_value; \ + typedef typename type_item::type_value_qx type_value_qx; \ + typedef typename TypeContainer::iterator type_iterator; + +namespace qx +{ + namespace trait + { + namespace detail + { + + template + struct generic_container_base + { + + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(Container, qx::trait::no_type, Item) + + static inline long size(const Container &t) { return static_cast(t.size()); } + static inline void clear(Container &t) { t.clear(); } + static inline void reserve(Container &t, long l) { t.reserve(l); } + static inline type_item createItem() { return type_item(type_item::newKey(), type_item::newValue()); } + static inline Item *insertItem(Container &t, type_item &item) + { + t.push_back(item.value()); + return (&t.back()); + } + static inline type_iterator end(Container &t) { return t.end(); } + + static inline type_iterator begin(Container &t, type_item &item) + { + if (t.size() <= 0) + { + return t.end(); + }; + item.value(*t.begin()); + return t.begin(); + } + + static inline type_iterator next(Container &t, type_iterator itr, type_item &item) + { + itr++; + if (itr == t.end()) + { + return t.end(); + }; + item.value(*itr); + return itr; + } + }; + + template + struct generic_container_base_without_reserve + { + + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(Container, qx::trait::no_type, Item) + + static inline long size(const Container &t) { return static_cast(t.size()); } + static inline void clear(Container &t) { t.clear(); } + static inline void reserve(Container &t, long l) + { + Q_UNUSED(t); + Q_UNUSED(l); + } + static inline type_item createItem() { return type_item(type_item::newKey(), type_item::newValue()); } + static inline Item *insertItem(Container &t, type_item &item) + { + t.push_back(item.value()); + return (&t.back()); + } + static inline type_iterator end(Container &t) { return t.end(); } + + static inline type_iterator begin(Container &t, type_item &item) + { + if (t.size() <= 0) + { + return t.end(); + }; + item.value(*t.begin()); + return t.begin(); + } + + static inline type_iterator next(Container &t, type_iterator itr, type_item &item) + { + itr++; + if (itr == t.end()) + { + return t.end(); + }; + item.value(*itr); + return itr; + } + }; + + template + struct generic_container_base_set + { + + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(Container, qx::trait::no_type, Item) + + static inline long size(const Container &t) { return static_cast(t.size()); } + static inline void clear(Container &t) { t.clear(); } + static inline void reserve(Container &t, long l) + { + Q_UNUSED(t); + Q_UNUSED(l); + } + static inline type_item createItem() { return type_item(type_item::newKey(), type_item::newValue()); } + static inline Item *insertItem(Container &t, type_item &item) { return const_cast(&(*(t.insert(item.value()).first))); } + static inline type_iterator end(Container &t) { return t.end(); } + + static inline type_iterator begin(Container &t, type_item &item) + { + if (t.size() <= 0) + { + return t.end(); + }; + item.value(*t.begin()); + return t.begin(); + } + + static inline type_iterator next(Container &t, type_iterator itr, type_item &item) + { + itr++; + if (itr == t.end()) + { + return t.end(); + }; + item.value(*itr); + return itr; + } + }; + + template + struct generic_container_base_multi_set + { + + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(Container, qx::trait::no_type, Item) + + static inline long size(const Container &t) { return static_cast(t.size()); } + static inline void clear(Container &t) { t.clear(); } + static inline void reserve(Container &t, long l) + { + Q_UNUSED(t); + Q_UNUSED(l); + } + static inline type_item createItem() { return type_item(type_item::newKey(), type_item::newValue()); } + static inline Item *insertItem(Container &t, type_item &item) { return const_cast(&(*(t.insert(item.value())))); } + static inline type_iterator end(Container &t) { return t.end(); } + + static inline type_iterator begin(Container &t, type_item &item) + { + if (t.size() <= 0) + { + return t.end(); + }; + item.value(*t.begin()); + return t.begin(); + } + + static inline type_iterator next(Container &t, type_iterator itr, type_item &item) + { + itr++; + if (itr == t.end()) + { + return t.end(); + }; + item.value(*itr); + return itr; + } + }; + + template + struct generic_container_base_key_value_std_style + { + + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(Container, Key, Value) + + static inline long size(const Container &t) { return static_cast(t.size()); } + static inline void clear(Container &t) { t.clear(); } + static inline void reserve(Container &t, long l) { t.reserve(l); } + static inline type_item createItem() { return type_item(type_item::newKey(), type_item::newValue()); } + static inline Value *insertItem(Container &t, type_item &item) { return (&(t.insert(std::make_pair(item.key(), item.value())).first->second)); } + static inline type_iterator end(Container &t) { return t.end(); } + + static inline type_iterator begin(Container &t, type_item &item) + { + if (t.size() <= 0) + { + return t.end(); + }; + item.value(*t.begin().second); + item.key(*t.begin().first); + return t.begin(); + } + + static inline type_iterator next(Container &t, type_iterator itr, type_item &item) + { + itr++; + if (itr == t.end()) + { + return t.end(); + }; + item.value(*itr.second); + item.key(*itr.first); + return itr; + } + }; + + template + struct generic_container_base_key_value_without_reserve + { + + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(Container, Key, Value) + + static inline long size(const Container &t) { return static_cast(t.size()); } + static inline void clear(Container &t) { t.clear(); } + static inline void reserve(Container &t, long l) + { + Q_UNUSED(t); + Q_UNUSED(l); + } + static inline type_item createItem() { return type_item(type_item::newKey(), type_item::newValue()); } + static inline Value *insertItem(Container &t, type_item &item) { return (&(t.insert(std::make_pair(item.key(), item.value())).first->second)); } + static inline type_iterator end(Container &t) { return t.end(); } + + static inline type_iterator begin(Container &t, type_item &item) + { + if (t.size() <= 0) + { + return t.end(); + }; + item.value(*t.begin().second); + item.key(*t.begin().first); + return t.begin(); + } + + static inline type_iterator next(Container &t, type_iterator itr, type_item &item) + { + itr++; + if (itr == t.end()) + { + return t.end(); + }; + item.value(*itr.second); + item.key(*itr.first); + return itr; + } + }; + + template + struct generic_container_base_key_value_multi_std_style + { + + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(Container, Key, Value) + + static inline long size(const Container &t) { return static_cast(t.size()); } + static inline void clear(Container &t) { t.clear(); } + static inline void reserve(Container &t, long l) { t.reserve(l); } + static inline type_item createItem() { return type_item(type_item::newKey(), type_item::newValue()); } + static inline Value *insertItem(Container &t, type_item &item) { return (&(t.insert(std::make_pair(item.key(), item.value()))->second)); } + static inline type_iterator end(Container &t) { return t.end(); } + + static inline type_iterator begin(Container &t, type_item &item) + { + if (t.size() <= 0) + { + return t.end(); + }; + item.value(*t.begin().second); + item.key(*t.begin().first); + return t.begin(); + } + + static inline type_iterator next(Container &t, type_iterator itr, type_item &item) + { + itr++; + if (itr == t.end()) + { + return t.end(); + }; + item.value(*itr.second); + item.key(*itr.first); + return itr; + } + }; + + template + struct generic_container_base_key_value_qt_style + { + + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(Container, Key, Value) + + static inline long size(const Container &t) { return static_cast(t.size()); } + static inline void clear(Container &t) { t.clear(); } + static inline void reserve(Container &t, long l) { t.reserve(l); } + static inline type_item createItem() { return type_item(type_item::newKey(), type_item::newValue()); } + static inline Value *insertItem(Container &t, type_item &item) { return (&(t.insert(item.key(), item.value()).value())); } + static inline type_iterator end(Container &t) { return t.end(); } + + static inline type_iterator begin(Container &t, type_item &item) + { + if (t.size() <= 0) + { + return t.end(); + }; + item.value(*t.begin().value()); + item.key(*t.begin().key()); + return t.begin(); + } + + static inline type_iterator next(Container &t, type_iterator itr, type_item &item) + { + itr++; + if (itr == t.end()) + { + return t.end(); + }; + item.value(*itr.value()); + item.key(*itr.key()); + return itr; + } + }; + + } // namespace detail + + template + struct generic_container> : public qx::trait::detail::generic_container_base, T> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1(std::vector, T), qx::trait::no_type, T) + }; + + template + struct generic_container> : public qx::trait::detail::generic_container_base_without_reserve, T> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1(std::list, T), qx::trait::no_type, T) + }; + + template + struct generic_container> : public qx::trait::detail::generic_container_base_set, T> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1(std::set, T), qx::trait::no_type, T) + }; + + template + struct generic_container> : public qx::trait::detail::generic_container_base_key_value_without_reserve, Key, Value> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1_P2(std::map, Key, Value), Key, Value) + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct generic_container> : public qx::trait::detail::generic_container_base_set, T> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1(boost::unordered_set, T), qx::trait::no_type, T) + }; + + template + struct generic_container> : public qx::trait::detail::generic_container_base_multi_set, T> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1(boost::unordered_multiset, T), qx::trait::no_type, T) + }; + + template + struct generic_container> : public qx::trait::detail::generic_container_base_key_value_std_style, Key, Value> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1_P2(boost::unordered_map, Key, Value), Key, Value) + }; + + template + struct generic_container> : public qx::trait::detail::generic_container_base_key_value_multi_std_style, Key, Value> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1_P2(boost::unordered_multimap, Key, Value), Key, Value) + }; + +#endif // _QX_ENABLE_BOOST + + template + struct generic_container> : public qx::trait::detail::generic_container_base_set, T> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1(std::unordered_set, T), qx::trait::no_type, T) + }; + + template + struct generic_container> : public qx::trait::detail::generic_container_base_multi_set, T> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1(std::unordered_multiset, T), qx::trait::no_type, T) + }; + + template + struct generic_container> : public qx::trait::detail::generic_container_base_key_value_std_style, Key, Value> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1_P2(std::unordered_map, Key, Value), Key, Value) + }; + + template + struct generic_container> : public qx::trait::detail::generic_container_base_key_value_multi_std_style, Key, Value> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1_P2(std::unordered_multimap, Key, Value), Key, Value) + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct generic_container> : public qx::trait::detail::generic_container_base, T> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1(QVector, T), qx::trait::no_type, T) + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +#if (QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) + + template + struct generic_container> : public qx::trait::detail::generic_container_base, T> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1(QList, T), qx::trait::no_type, T) + }; + +#else // (QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) + + template + struct generic_container> : public qx::trait::detail::generic_container_base_without_reserve, T> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1(QList, T), qx::trait::no_type, T) + }; + +#endif // (QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) + +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + template + struct generic_container> : public qx::trait::detail::generic_container_base_without_reserve, T> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1(QLinkedList, T), qx::trait::no_type, T) + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + + template + struct generic_container> : public qx::trait::detail::generic_container_base_multi_set, T> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1(QSet, T), qx::trait::no_type, T) + }; + + template + struct generic_container> : public qx::trait::detail::generic_container_base_key_value_qt_style, Key, Value> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1_P2(QMap, Key, Value), Key, Value) + }; + + template + struct generic_container> : public qx::trait::detail::generic_container_base_key_value_qt_style, Key, Value> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1_P2(QMultiMap, Key, Value), Key, Value) + }; + + template + struct generic_container> : public qx::trait::detail::generic_container_base_key_value_qt_style, Key, Value> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1_P2(QHash, Key, Value), Key, Value) + }; + + template + struct generic_container> : public qx::trait::detail::generic_container_base_key_value_qt_style, Key, Value> + { + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1_P2(QMultiHash, Key, Value), Key, Value) + }; + + template + struct generic_container> + { + + QX_TRAIT_GENERIC_CONTAINER_TYPEDEF(QX_TEMPLATE_T_P1_P2(qx::QxCollection, Key, Value), Key, Value) + + static inline long size(const qx::QxCollection &t) { return static_cast(t.size()); } + static inline void clear(qx::QxCollection &t) { t.clear(); } + static inline void reserve(qx::QxCollection &t, long l) { t.reserve(l); } + static inline type_item createItem() { return type_item(type_item::newKey(), type_item::newValue()); } + static inline Value *insertItem(qx::QxCollection &t, type_item &item) + { + t.insert(item.key(), item.value()); + return const_cast(&t.getByKey(item.key())); + } + static inline type_iterator end(qx::QxCollection &t) { return t.end(); } + + static inline type_iterator begin(qx::QxCollection &t, type_item &item) + { + if (t.size() <= 0) + { + return t.end(); + }; + item.value(t.getByIndex(0)); + item.key(t.getKeyByIndex(0)); + return t.begin(); + } + + static inline type_iterator next(qx::QxCollection &t, type_iterator itr, type_item &item) + { + itr++; + if (itr == t.end()) + { + return t.end(); + }; + long l = (itr - t.begin()); + item.value(t.getByIndex(l)); + item.key(t.getKeyByIndex(l)); + return itr; + } + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_GENERIC_CONTAINER_H_ diff --git a/include/QxTraits/get_base_class.h b/include/QxTraits/get_base_class.h new file mode 100644 index 0000000..7756783 --- /dev/null +++ b/include/QxTraits/get_base_class.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_GET_BASE_CLASS_H_ +#define _QX_GET_BASE_CLASS_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file get_base_class.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::get_base_class::type : retrieve base class of type T registered into QxOrm context and return qx::trait::no_base_class_defined if no base class defined + */ + +#include + +namespace qx +{ + namespace trait + { + + class no_base_class_defined + { + public: + no_base_class_defined() { ; }; + virtual ~no_base_class_defined() { ; }; + virtual void dummy() = 0; + }; + + /*! + * \ingroup QxTraits + * \brief qx::trait::get_base_class::type : retrieve base class of type T registered into QxOrm context and return qx::trait::no_base_class_defined if no base class defined + */ + template + class get_base_class + { + public: + typedef qx::trait::no_base_class_defined type; + }; + + template + class is_base_class_defined + { + public: + enum + { + value = (std::is_same::type, qx::trait::no_base_class_defined>::value ? 0 : 1) + }; + }; + + template + class get_base_class_2 + { + private: + typedef typename qx::trait::get_base_class::type type_base; + + private: + enum + { + is_base_ok = (std::is_same::value ? 0 : 1) + }; + + public: + typedef typename std::conditional::type type; + }; + + } // namespace trait +} // namespace qx + +QX_REGISTER_CLASS_NAME(qx::trait::no_base_class_defined) + +#define QX_REGISTER_BASE_CLASS(derivedClass, baseClass) \ + namespace qx \ + { \ + namespace trait \ + { \ + template <> \ + class get_base_class \ + { \ + public: \ + typedef baseClass type; \ + }; \ + } \ + } // namespace qx::trait + +#define QX_GET_BASE_CLASS(T) qx::trait::get_base_class::type +#define QX_GET_BASE_CLASS_WITH_TYPENAME(T) qx::trait::get_base_class::type +#define QX_IS_BASE_CLASS_DEFINED(T) qx::trait::is_base_class_defined::value +#define QX_IS_BASE_CLASS_DEFINED_WITH_TYPENAME(T) qx::trait::is_base_class_defined::value +#define QX_GET_BASE_CLASS_2(T) qx::trait::get_base_class_2::type +#define QX_GET_BASE_CLASS_2_WITH_TYPENAME(T) qx::trait::get_base_class_2::type + +#endif // _QX_GET_BASE_CLASS_H_ diff --git a/include/QxTraits/get_class_name.h b/include/QxTraits/get_class_name.h new file mode 100644 index 0000000..dff540b --- /dev/null +++ b/include/QxTraits/get_class_name.h @@ -0,0 +1,378 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_GET_CLASS_NAME_H_ +#define _QX_GET_CLASS_NAME_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file get_class_name.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::get_class_name::get() : return class name of type T under const char * format, T must be registered with QX_REGISTER_CLASS_NAME(T) macro + */ + +#ifndef _QX_NO_RTTI +#include +#endif // _QX_NO_RTTI + +#include + +#define QX_REGISTER_CLASS_NAME_SEP_INF "<" +#define QX_REGISTER_CLASS_NAME_SEP_SUP ">" +#define QX_REGISTER_CLASS_NAME_SEP_NXT ", " + +#define QX_REGISTER_CLASS_NAME_SEP_INF_XML_TAG "-" +#define QX_REGISTER_CLASS_NAME_SEP_SUP_XML_TAG "-" +#define QX_REGISTER_CLASS_NAME_SEP_NXT_XML_TAG "_" + +#define QX_GET_CLASS_NAME(TYPE) \ + qx::trait::get_class_name::type>::get() + +#define QX_GET_CLASS_NAME_STD_STR(TYPE) \ + std::string(qx::trait::get_class_name::type>::get()) + +#define QX_GET_CLASS_NAME_XML_TAG(TYPE) \ + qx::trait::get_class_name::type>::get_xml_tag() + +#define QX_GET_CLASS_NAME_XML_TAG_STD_STR(TYPE) \ + std::string(qx::trait::get_class_name::type>::get_xml_tag()) + +#define QX_GET_CLASS_NAME_WITH_TYPENAME(TYPE) \ + qx::trait::get_class_name::type>::get() + +#define QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(TYPE) \ + std::string(qx::trait::get_class_name::type>::get()) + +#define QX_GET_CLASS_NAME_XML_TAG_WITH_TYPENAME(TYPE) \ + qx::trait::get_class_name::type>::get_xml_tag() + +#define QX_GET_CLASS_NAME_XML_TAG_STD_STR_WITH_TYPENAME(TYPE) \ + std::string(qx::trait::get_class_name::type>::get_xml_tag()) + +#define QX_GET_CLASS_NAME_IMPLEMENT_FCT_GET_XML_TAG() \ + static inline const char *get_xml_tag() \ + { \ + static std::string result_xml; \ + if (!result_xml.empty()) \ + { \ + return result_xml.c_str(); \ + } \ + QString tmp = get_class_name::get(); \ + tmp.replace(QX_REGISTER_CLASS_NAME_SEP_INF, QX_REGISTER_CLASS_NAME_SEP_INF_XML_TAG); \ + tmp.replace(QX_REGISTER_CLASS_NAME_SEP_SUP, QX_REGISTER_CLASS_NAME_SEP_SUP_XML_TAG); \ + tmp.replace(QX_REGISTER_CLASS_NAME_SEP_NXT, QX_REGISTER_CLASS_NAME_SEP_NXT_XML_TAG); \ + tmp.replace("::", "."); \ + tmp.replace(" ", ""); \ + result_xml = tmp.toLatin1().constData(); \ + return result_xml.c_str(); \ + } + +#ifdef _QX_NO_RTTI +#define QX_REGISTER_CLASS_NAME_NO_RTTI(className) std::string(#className) +#endif // _QX_NO_RTTI + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::get_class_name::get() : return class name of type T under const char * format, T must be registered with QX_REGISTER_CLASS_NAME(T) macro + */ + template + struct get_class_name + { + + static inline const char *get() + { + static std::string result; + if (!result.empty()) + { + return result.c_str(); + } + +#ifndef _QX_NO_RTTI + T *dummy = NULL; + Q_UNUSED(dummy); + result = std::string(typeid(dummy).name()); +#else // _QX_NO_RTTI + result = QX_REGISTER_CLASS_NAME_NO_RTTI(T); +#endif // _QX_NO_RTTI + + qDebug("[QxOrm] Unable to define correct class name : '%s' => use macro 'QX_REGISTER_CLASS_NAME()' or 'QX_REGISTER_CLASS_NAME_TEMPLATE_X()'", result.c_str()); + return result.c_str(); + } + + QX_GET_CLASS_NAME_IMPLEMENT_FCT_GET_XML_TAG(); + }; + + } // namespace trait +} // namespace qx + +#define QX_REGISTER_CLASS_NAME_TEMPLATE_INIT(className) \ + static std::string result; \ + if (!result.empty()) \ + { \ + return result.c_str(); \ + } \ + result = std::string(#className); + +#define QX_REGISTER_CLASS_NAME(className) \ + namespace qx \ + { \ + namespace trait \ + { \ + template <> \ + struct get_class_name \ + { \ + static inline const char *get() \ + { \ + QX_REGISTER_CLASS_NAME_TEMPLATE_INIT(className); \ + return result.c_str(); \ + } \ + QX_GET_CLASS_NAME_IMPLEMENT_FCT_GET_XML_TAG(); \ + }; \ + } \ + } // namespace qx::trait + +#define QX_REGISTER_CLASS_NAME_TEMPLATE_1(className) \ + namespace qx \ + { \ + namespace trait \ + { \ + template \ + struct get_class_name> \ + { \ + static inline const char *get() \ + { \ + QX_REGISTER_CLASS_NAME_TEMPLATE_INIT(className); \ + result += QX_REGISTER_CLASS_NAME_SEP_INF + QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T) + QX_REGISTER_CLASS_NAME_SEP_SUP; \ + return result.c_str(); \ + } \ + QX_GET_CLASS_NAME_IMPLEMENT_FCT_GET_XML_TAG(); \ + }; \ + } \ + } // namespace qx::trait + +#define QX_REGISTER_CLASS_NAME_TEMPLATE_2(className) \ + namespace qx \ + { \ + namespace trait \ + { \ + template \ + struct get_class_name> \ + { \ + static inline const char *get() \ + { \ + QX_REGISTER_CLASS_NAME_TEMPLATE_INIT(className); \ + result += QX_REGISTER_CLASS_NAME_SEP_INF + QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T1) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T2) + QX_REGISTER_CLASS_NAME_SEP_SUP; \ + return result.c_str(); \ + } \ + QX_GET_CLASS_NAME_IMPLEMENT_FCT_GET_XML_TAG(); \ + }; \ + } \ + } // namespace qx::trait + +#define QX_REGISTER_CLASS_NAME_TEMPLATE_3(className) \ + namespace qx \ + { \ + namespace trait \ + { \ + template \ + struct get_class_name> \ + { \ + static inline const char *get() \ + { \ + QX_REGISTER_CLASS_NAME_TEMPLATE_INIT(className); \ + result += QX_REGISTER_CLASS_NAME_SEP_INF + QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T1) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T2) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T3) + QX_REGISTER_CLASS_NAME_SEP_SUP; \ + return result.c_str(); \ + } \ + QX_GET_CLASS_NAME_IMPLEMENT_FCT_GET_XML_TAG(); \ + }; \ + } \ + } // namespace qx::trait + +#define QX_REGISTER_CLASS_NAME_TEMPLATE_4(className) \ + namespace qx \ + { \ + namespace trait \ + { \ + template \ + struct get_class_name> \ + { \ + static inline const char *get() \ + { \ + QX_REGISTER_CLASS_NAME_TEMPLATE_INIT(className); \ + result += QX_REGISTER_CLASS_NAME_SEP_INF + QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T1) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T2) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T3) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T4) + QX_REGISTER_CLASS_NAME_SEP_SUP; \ + return result.c_str(); \ + } \ + QX_GET_CLASS_NAME_IMPLEMENT_FCT_GET_XML_TAG(); \ + }; \ + } \ + } // namespace qx::trait + +#define QX_REGISTER_CLASS_NAME_TEMPLATE_5(className) \ + namespace qx \ + { \ + namespace trait \ + { \ + template \ + struct get_class_name> \ + { \ + static inline const char *get() \ + { \ + QX_REGISTER_CLASS_NAME_TEMPLATE_INIT(className); \ + result += QX_REGISTER_CLASS_NAME_SEP_INF + QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T1) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T2) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T3) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T4) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T5) + QX_REGISTER_CLASS_NAME_SEP_SUP; \ + return result.c_str(); \ + } \ + QX_GET_CLASS_NAME_IMPLEMENT_FCT_GET_XML_TAG(); \ + }; \ + } \ + } // namespace qx::trait + +#define QX_REGISTER_CLASS_NAME_TEMPLATE_6(className) \ + namespace qx \ + { \ + namespace trait \ + { \ + template \ + struct get_class_name> \ + { \ + static inline const char *get() \ + { \ + QX_REGISTER_CLASS_NAME_TEMPLATE_INIT(className); \ + result += QX_REGISTER_CLASS_NAME_SEP_INF + QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T1) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T2) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T3) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T4) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T5) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T6) + QX_REGISTER_CLASS_NAME_SEP_SUP; \ + return result.c_str(); \ + } \ + QX_GET_CLASS_NAME_IMPLEMENT_FCT_GET_XML_TAG(); \ + }; \ + } \ + } // namespace qx::trait + +#define QX_REGISTER_CLASS_NAME_TEMPLATE_7(className) \ + namespace qx \ + { \ + namespace trait \ + { \ + template \ + struct get_class_name> \ + { \ + static inline const char *get() \ + { \ + QX_REGISTER_CLASS_NAME_TEMPLATE_INIT(className); \ + result += QX_REGISTER_CLASS_NAME_SEP_INF + QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T1) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T2) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T3) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T4) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T5) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T6) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T7) + QX_REGISTER_CLASS_NAME_SEP_SUP; \ + return result.c_str(); \ + } \ + QX_GET_CLASS_NAME_IMPLEMENT_FCT_GET_XML_TAG(); \ + }; \ + } \ + } // namespace qx::trait + +#define QX_REGISTER_CLASS_NAME_TEMPLATE_8(className) \ + namespace qx \ + { \ + namespace trait \ + { \ + template \ + struct get_class_name> \ + { \ + static inline const char *get() \ + { \ + QX_REGISTER_CLASS_NAME_TEMPLATE_INIT(className); \ + result += QX_REGISTER_CLASS_NAME_SEP_INF + QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T1) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T2) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T3) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T4) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T5) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T6) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T7) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T8) + QX_REGISTER_CLASS_NAME_SEP_SUP; \ + return result.c_str(); \ + } \ + QX_GET_CLASS_NAME_IMPLEMENT_FCT_GET_XML_TAG(); \ + }; \ + } \ + } // namespace qx::trait + +#define QX_REGISTER_CLASS_NAME_TEMPLATE_9(className) \ + namespace qx \ + { \ + namespace trait \ + { \ + template \ + struct get_class_name> \ + { \ + static inline const char *get() \ + { \ + QX_REGISTER_CLASS_NAME_TEMPLATE_INIT(className); \ + result += QX_REGISTER_CLASS_NAME_SEP_INF + QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T1) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T2) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T3) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T4) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T5) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T6) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T7) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T8) + QX_REGISTER_CLASS_NAME_SEP_NXT; \ + result += QX_GET_CLASS_NAME_STD_STR_WITH_TYPENAME(T9) + QX_REGISTER_CLASS_NAME_SEP_SUP; \ + return result.c_str(); \ + } \ + QX_GET_CLASS_NAME_IMPLEMENT_FCT_GET_XML_TAG(); \ + }; \ + } \ + } // namespace qx::trait + +#endif // _QX_GET_CLASS_NAME_H_ diff --git a/include/QxTraits/get_class_name_primitive.h b/include/QxTraits/get_class_name_primitive.h new file mode 100644 index 0000000..1832736 --- /dev/null +++ b/include/QxTraits/get_class_name_primitive.h @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_GET_CLASS_NAME_PRIMITIVE_H_ +#define _QX_GET_CLASS_NAME_PRIMITIVE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file get_class_name_primitive.h + * \author XDL Team + * \ingroup QxTraits + * \brief Register all primitive and useful types of stl, boost and Qt libraries using QX_REGISTER_CLASS_NAME(T) macro + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) +#include +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#include +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +#include + +#ifdef _QX_ENABLE_QT_GUI +#include +#include +#include +#include +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#include +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#include +#include +#include +#endif // _QX_ENABLE_QT_GUI + +#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) +#include +#endif // (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) + +#include + +#include + +QX_REGISTER_CLASS_NAME(void) +QX_REGISTER_CLASS_NAME(bool) +QX_REGISTER_CLASS_NAME(int) +QX_REGISTER_CLASS_NAME(short) +QX_REGISTER_CLASS_NAME(long) +QX_REGISTER_CLASS_NAME(float) +QX_REGISTER_CLASS_NAME(double) +QX_REGISTER_CLASS_NAME(long double) +QX_REGISTER_CLASS_NAME(char) +QX_REGISTER_CLASS_NAME(unsigned int) +QX_REGISTER_CLASS_NAME(unsigned short) +QX_REGISTER_CLASS_NAME(unsigned long) +QX_REGISTER_CLASS_NAME(unsigned char) + +QX_REGISTER_CLASS_NAME(std::string) +QX_REGISTER_CLASS_NAME(std::wstring) + +QX_REGISTER_CLASS_NAME(QObject) +QX_REGISTER_CLASS_NAME(QString) +QX_REGISTER_CLASS_NAME(QStringList) +QX_REGISTER_CLASS_NAME(QByteArray) +QX_REGISTER_CLASS_NAME(QDate) +QX_REGISTER_CLASS_NAME(QDateTime) +QX_REGISTER_CLASS_NAME(QPoint) +QX_REGISTER_CLASS_NAME(QRect) +QX_REGISTER_CLASS_NAME(QSize) +QX_REGISTER_CLASS_NAME(QTime) +QX_REGISTER_CLASS_NAME(QUrl) +QX_REGISTER_CLASS_NAME(QVariant) +QX_REGISTER_CLASS_NAME(QUuid) +QX_REGISTER_CLASS_NAME(QSqlError) + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_REGISTER_CLASS_NAME(QRegExp) +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +#ifdef _QX_ENABLE_QT_GUI +QX_REGISTER_CLASS_NAME(QColor) +QX_REGISTER_CLASS_NAME(QFont) +QX_REGISTER_CLASS_NAME(QImage) +QX_REGISTER_CLASS_NAME(QBrush) +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_REGISTER_CLASS_NAME(QMatrix) +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_REGISTER_CLASS_NAME(QPicture) +QX_REGISTER_CLASS_NAME(QPixmap) +QX_REGISTER_CLASS_NAME(QRegion) +#endif // _QX_ENABLE_QT_GUI + +QX_REGISTER_CLASS_NAME_TEMPLATE_1(std::allocator) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(std::vector) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(std::list) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(std::set) + +#ifdef _QX_ENABLE_BOOST + +QX_REGISTER_CLASS_NAME_TEMPLATE_1(boost::shared_ptr) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(boost::scoped_ptr) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(boost::weak_ptr) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(boost::optional) + +#endif // _QX_ENABLE_BOOST + +QX_REGISTER_CLASS_NAME_TEMPLATE_1(QList) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(QSharedPointer) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(QWeakPointer) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(QFlags) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(QSet) + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(QVector) +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(QLinkedList) +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + +QX_REGISTER_CLASS_NAME_TEMPLATE_1(qx::dao::ptr) + +QX_REGISTER_CLASS_NAME_TEMPLATE_1(std::unique_ptr) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(std::shared_ptr) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(std::weak_ptr) + +QX_REGISTER_CLASS_NAME_TEMPLATE_2(std::pair) +QX_REGISTER_CLASS_NAME_TEMPLATE_2(std::map) + +#ifdef _QX_ENABLE_BOOST +QX_REGISTER_CLASS_NAME_TEMPLATE_2(boost::unordered_map) +QX_REGISTER_CLASS_NAME_TEMPLATE_2(boost::unordered_multimap) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(boost::unordered_set) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(boost::unordered_multiset) +#endif // _QX_ENABLE_BOOST + +QX_REGISTER_CLASS_NAME_TEMPLATE_2(std::unordered_map) +QX_REGISTER_CLASS_NAME_TEMPLATE_2(std::unordered_multimap) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(std::unordered_set) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(std::unordered_multiset) + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_REGISTER_CLASS_NAME_TEMPLATE_2(QPair) +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +QX_REGISTER_CLASS_NAME_TEMPLATE_2(QHash) +QX_REGISTER_CLASS_NAME_TEMPLATE_2(QMultiHash) +QX_REGISTER_CLASS_NAME_TEMPLATE_2(QMap) +QX_REGISTER_CLASS_NAME_TEMPLATE_2(QMultiMap) + +#ifdef _QX_ENABLE_BOOST +QX_REGISTER_CLASS_NAME_TEMPLATE_1(boost::tuple) +QX_REGISTER_CLASS_NAME_TEMPLATE_2(boost::tuple) +QX_REGISTER_CLASS_NAME_TEMPLATE_3(boost::tuple) +QX_REGISTER_CLASS_NAME_TEMPLATE_4(boost::tuple) +QX_REGISTER_CLASS_NAME_TEMPLATE_5(boost::tuple) +QX_REGISTER_CLASS_NAME_TEMPLATE_6(boost::tuple) +QX_REGISTER_CLASS_NAME_TEMPLATE_7(boost::tuple) +QX_REGISTER_CLASS_NAME_TEMPLATE_8(boost::tuple) +QX_REGISTER_CLASS_NAME_TEMPLATE_9(boost::tuple) +#endif // _QX_ENABLE_BOOST + +QX_REGISTER_CLASS_NAME_TEMPLATE_1(std::tuple) +QX_REGISTER_CLASS_NAME_TEMPLATE_2(std::tuple) +QX_REGISTER_CLASS_NAME_TEMPLATE_3(std::tuple) +QX_REGISTER_CLASS_NAME_TEMPLATE_4(std::tuple) +QX_REGISTER_CLASS_NAME_TEMPLATE_5(std::tuple) +QX_REGISTER_CLASS_NAME_TEMPLATE_6(std::tuple) +QX_REGISTER_CLASS_NAME_TEMPLATE_7(std::tuple) +QX_REGISTER_CLASS_NAME_TEMPLATE_8(std::tuple) +QX_REGISTER_CLASS_NAME_TEMPLATE_9(std::tuple) + +#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) +QX_REGISTER_CLASS_NAME_TEMPLATE_1(QScopedPointer) +#endif // (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) + +#endif // _QX_GET_CLASS_NAME_PRIMITIVE_H_ diff --git a/include/QxTraits/get_primary_key.h b/include/QxTraits/get_primary_key.h new file mode 100644 index 0000000..1bbf13b --- /dev/null +++ b/include/QxTraits/get_primary_key.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_GET_PRIMARY_KEY_H_ +#define _QX_GET_PRIMARY_KEY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file get_primary_key.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::get_primary_key::type : return primary key type of T, by default primary key is long type, use QX_REGISTER_PRIMARY_KEY() macro to register another type (for example QX_REGISTER_PRIMARY_KEY(T, QString)) + */ + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::get_primary_key::type : return primary key type of T, by default primary key is long type, use QX_REGISTER_PRIMARY_KEY() macro to register another type (for example QX_REGISTER_PRIMARY_KEY(T, QString)) + */ + template + class get_primary_key + { + public: + typedef long type; + }; + + } // namespace trait +} // namespace qx + +#define QX_REGISTER_PRIMARY_KEY(daoClass, primaryKey) \ + namespace qx \ + { \ + namespace trait \ + { \ + template <> \ + class get_primary_key \ + { \ + public: \ + typedef primaryKey type; \ + }; \ + } \ + } // namespace qx::trait + +#endif // _QX_GET_PRIMARY_KEY_H_ diff --git a/include/QxTraits/get_sql_type.h b/include/QxTraits/get_sql_type.h new file mode 100644 index 0000000..8412e71 --- /dev/null +++ b/include/QxTraits/get_sql_type.h @@ -0,0 +1,416 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_GET_SQL_TYPE_H_ +#define _QX_GET_SQL_TYPE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file get_sql_type.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::get_sql_type::get() : return type name under const char * format used by database engine to map a C++ type T + */ + +#include + +#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) +#include +#endif // (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace qx +{ + namespace trait + { + namespace detail + { + + template + struct get_sql_type_helper + { + + private: + typedef typename qx::trait::remove_attr::type type_1; + typedef typename std::conditional::value, typename qx::trait::get_primary_key::type, type_1>::type type_2; + typedef typename std::conditional::value, long, type_2>::type type_3; + + public: + typedef typename qx::trait::detail::get_sql_type_helper::type_3 type; + }; + + template + struct get_sql_type + { + static inline const char *get() { return ""; } + }; + + /* Implemented into './src/QxRegister/QxClassX.cpp' file */ + QX_DLL_EXPORT const char *get_sql_type_by_class_name(const char *sClassName, const char *sDefaultValue); + + } // namespace detail + + /*! + * \ingroup QxTraits + * \brief qx::trait::get_sql_type::get() : return type name under const char * format used by database engine to map a C++ type T + */ + template + struct get_sql_type + { + typedef typename qx::trait::detail::get_sql_type_helper::type type_sql; + static inline const char *get() { return (std::is_same::value ? qx::trait::detail::get_sql_type::get() : qx::trait::get_sql_type::get()); } + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct get_sql_type> + { + static inline const char *get() { return qx::trait::get_sql_type::get(); } + }; + + template + struct get_sql_type> + { + static inline const char *get() { return qx::trait::get_sql_type::get(); } + }; + + template + struct get_sql_type> + { + static inline const char *get() { return qx::trait::get_sql_type::get(); } + }; + + template + struct get_sql_type> + { + static inline const char *get() { return qx::trait::get_sql_type::get(); } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct get_sql_type> + { + static inline const char *get() { return qx::trait::get_sql_type::get(); } + }; + +#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) + template + struct get_sql_type> + { + static inline const char *get() { return qx::trait::get_sql_type::get(); } + }; +#endif // (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) + + template + struct get_sql_type> + { + static inline const char *get() { return qx::trait::get_sql_type::get(); } + }; + + template + struct get_sql_type> + { + static inline const char *get() { return qx::trait::get_sql_type::get(); } + }; + + template + struct get_sql_type> + { + static inline const char *get() { return qx::trait::get_sql_type::get(); } + }; + + template + struct get_sql_type> + { + static inline const char *get() + { + static std::string s; + s = (std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get())); + return s.c_str(); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct get_sql_type> + { + static inline const char *get() + { + static std::string s; + s = (std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get())); + return s.c_str(); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +#ifdef _QX_ENABLE_BOOST + + template + struct get_sql_type> + { + static inline const char *get() + { + static std::string s; + s = (std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get())); + return s.c_str(); + } + }; + + template + struct get_sql_type> + { + static inline const char *get() + { + static std::string s; + s = (std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get())); + return s.c_str(); + } + }; + + template + struct get_sql_type> + { + static inline const char *get() + { + static std::string s; + s = (std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get())); + return s.c_str(); + } + }; + + template + struct get_sql_type> + { + static inline const char *get() + { + static std::string s; + s = (std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get())); + return s.c_str(); + } + }; + + template + struct get_sql_type> + { + static inline const char *get() + { + static std::string s; + s = (std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get())); + return s.c_str(); + } + }; + + template + struct get_sql_type> + { + static inline const char *get() + { + static std::string s; + s = (std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get())); + return s.c_str(); + } + }; + + template + struct get_sql_type> + { + static inline const char *get() + { + static std::string s; + s = (std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get())); + return s.c_str(); + } + }; + + template + struct get_sql_type> + { + static inline const char *get() + { + static std::string s; + s = (std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get())); + return s.c_str(); + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct get_sql_type> + { + static inline const char *get() + { + static std::string s; + s = (std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get())); + return s.c_str(); + } + }; + + template + struct get_sql_type> + { + static inline const char *get() + { + static std::string s; + s = (std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get())); + return s.c_str(); + } + }; + + template + struct get_sql_type> + { + static inline const char *get() + { + static std::string s; + s = (std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get())); + return s.c_str(); + } + }; + + template + struct get_sql_type> + { + static inline const char *get() + { + static std::string s; + s = (std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get())); + return s.c_str(); + } + }; + + template + struct get_sql_type> + { + static inline const char *get() + { + static std::string s; + s = (std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get())); + return s.c_str(); + } + }; + + template + struct get_sql_type> + { + static inline const char *get() + { + static std::string s; + s = (std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get())); + return s.c_str(); + } + }; + + template + struct get_sql_type> + { + static inline const char *get() + { + static std::string s; + s = (std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get())); + return s.c_str(); + } + }; + + template + struct get_sql_type> + { + static inline const char *get() + { + static std::string s; + s = (std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get()) + "|" + std::string(qx::trait::get_sql_type::get())); + return s.c_str(); + } + }; + + } // namespace trait +} // namespace qx + +#define QX_REGISTER_TRAIT_GET_SQL_TYPE(className, sqlType) \ + namespace qx \ + { \ + namespace trait \ + { \ + namespace detail \ + { \ + template <> \ + struct get_sql_type \ + { \ + static inline const char *get() { return get_sql_type_by_class_name(#className, sqlType); } \ + }; \ + } \ + } \ + } + +QX_REGISTER_TRAIT_GET_SQL_TYPE(bool, "SMALLINT") +QX_REGISTER_TRAIT_GET_SQL_TYPE(qx_bool, "TEXT") +QX_REGISTER_TRAIT_GET_SQL_TYPE(char, "SMALLINT") +QX_REGISTER_TRAIT_GET_SQL_TYPE(short, "SMALLINT") +QX_REGISTER_TRAIT_GET_SQL_TYPE(int, "INTEGER") +QX_REGISTER_TRAIT_GET_SQL_TYPE(long, "INTEGER") +QX_REGISTER_TRAIT_GET_SQL_TYPE(long long, "INTEGER") +QX_REGISTER_TRAIT_GET_SQL_TYPE(float, "FLOAT") +QX_REGISTER_TRAIT_GET_SQL_TYPE(double, "FLOAT") +QX_REGISTER_TRAIT_GET_SQL_TYPE(long double, "FLOAT") +QX_REGISTER_TRAIT_GET_SQL_TYPE(unsigned short, "SMALLINT") +QX_REGISTER_TRAIT_GET_SQL_TYPE(unsigned int, "INTEGER") +QX_REGISTER_TRAIT_GET_SQL_TYPE(unsigned long, "INTEGER") +QX_REGISTER_TRAIT_GET_SQL_TYPE(unsigned long long, "INTEGER") +QX_REGISTER_TRAIT_GET_SQL_TYPE(std::string, "TEXT") +QX_REGISTER_TRAIT_GET_SQL_TYPE(std::wstring, "TEXT") +QX_REGISTER_TRAIT_GET_SQL_TYPE(QString, "TEXT") +QX_REGISTER_TRAIT_GET_SQL_TYPE(QVariant, "TEXT") +QX_REGISTER_TRAIT_GET_SQL_TYPE(QUuid, "TEXT") +QX_REGISTER_TRAIT_GET_SQL_TYPE(QDate, "DATE") +QX_REGISTER_TRAIT_GET_SQL_TYPE(QTime, "TIME") +QX_REGISTER_TRAIT_GET_SQL_TYPE(QDateTime, "TIMESTAMP") +QX_REGISTER_TRAIT_GET_SQL_TYPE(QByteArray, "BLOB") +QX_REGISTER_TRAIT_GET_SQL_TYPE(qx::QxDateNeutral, "TEXT") +QX_REGISTER_TRAIT_GET_SQL_TYPE(qx::QxTimeNeutral, "TEXT") +QX_REGISTER_TRAIT_GET_SQL_TYPE(qx::QxDateTimeNeutral, "TEXT") + +#endif // _QX_GET_SQL_TYPE_H_ diff --git a/include/QxTraits/is_boost_intrusive_ptr.h b/include/QxTraits/is_boost_intrusive_ptr.h new file mode 100644 index 0000000..28eb3b3 --- /dev/null +++ b/include/QxTraits/is_boost_intrusive_ptr.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST +#ifndef _QX_IS_BOOST_INTRUSIVE_PTR_H_ +#define _QX_IS_BOOST_INTRUSIVE_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_boost_intrusive_ptr.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_boost_intrusive_ptr::value : return true if T is a boost::intrusive_ptr<> smart-pointer, otherwise return false + */ + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_boost_intrusive_ptr::value : return true if T is a boost::intrusive_ptr<> smart-pointer, otherwise return false + */ + template + struct is_boost_intrusive_ptr : public std::false_type + { + ; + }; + + template + struct is_boost_intrusive_ptr> : public std::true_type + { + ; + }; + + template + struct is_boost_intrusive_ptr &> : public std::true_type + { + ; + }; + + template + struct is_boost_intrusive_ptr> : public std::true_type + { + ; + }; + + template + struct is_boost_intrusive_ptr &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_BOOST_INTRUSIVE_PTR_H_ +#endif // _QX_ENABLE_BOOST diff --git a/include/QxTraits/is_boost_scoped_ptr.h b/include/QxTraits/is_boost_scoped_ptr.h new file mode 100644 index 0000000..871dd9f --- /dev/null +++ b/include/QxTraits/is_boost_scoped_ptr.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST +#ifndef _QX_IS_BOOST_SCOPED_PTR_H_ +#define _QX_IS_BOOST_SCOPED_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_boost_scoped_ptr.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_boost_scoped_ptr::value : return true if T is a boost::scoped_ptr<> smart-pointer, otherwise return false + */ + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_boost_scoped_ptr::value : return true if T is a boost::scoped_ptr<> smart-pointer, otherwise return false + */ + template + struct is_boost_scoped_ptr : public std::false_type + { + ; + }; + + template + struct is_boost_scoped_ptr> : public std::true_type + { + ; + }; + + template + struct is_boost_scoped_ptr &> : public std::true_type + { + ; + }; + + template + struct is_boost_scoped_ptr> : public std::true_type + { + ; + }; + + template + struct is_boost_scoped_ptr &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_BOOST_SCOPED_PTR_H_ +#endif // _QX_ENABLE_BOOST diff --git a/include/QxTraits/is_boost_shared_ptr.h b/include/QxTraits/is_boost_shared_ptr.h new file mode 100644 index 0000000..eb2c095 --- /dev/null +++ b/include/QxTraits/is_boost_shared_ptr.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST +#ifndef _QX_IS_BOOST_SHARED_PTR_H_ +#define _QX_IS_BOOST_SHARED_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_boost_shared_ptr.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_boost_shared_ptr::value : return true if T is a boost::shared_ptr<> smart-pointer, otherwise return false + */ + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_boost_shared_ptr::value : return true if T is a boost::shared_ptr<> smart-pointer, otherwise return false + */ + template + struct is_boost_shared_ptr : public std::false_type + { + ; + }; + + template + struct is_boost_shared_ptr> : public std::true_type + { + ; + }; + + template + struct is_boost_shared_ptr &> : public std::true_type + { + ; + }; + + template + struct is_boost_shared_ptr> : public std::true_type + { + ; + }; + + template + struct is_boost_shared_ptr &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_BOOST_SHARED_PTR_H_ +#endif // _QX_ENABLE_BOOST diff --git a/include/QxTraits/is_boost_unordered_map.h b/include/QxTraits/is_boost_unordered_map.h new file mode 100644 index 0000000..d9f76d1 --- /dev/null +++ b/include/QxTraits/is_boost_unordered_map.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST +#ifndef _QX_IS_BOOST_UNORDERED_MAP_H_ +#define _QX_IS_BOOST_UNORDERED_MAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_boost_unordered_map.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_boost_unordered_map::value : return true if T is a boost::unordered_map<> or boost::unordered_multimap<> container, otherwise return false + */ + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_boost_unordered_map::value : return true if T is a boost::unordered_map<> or boost::unordered_multimap<> container, otherwise return false + */ + template + struct is_boost_unordered_map : public std::false_type + { + ; + }; + + template + struct is_boost_unordered_map> : public std::true_type + { + ; + }; + + template + struct is_boost_unordered_map &> : public std::true_type + { + ; + }; + + template + struct is_boost_unordered_map> : public std::true_type + { + ; + }; + + template + struct is_boost_unordered_map &> : public std::true_type + { + ; + }; + + template + struct is_boost_unordered_map> : public std::true_type + { + ; + }; + + template + struct is_boost_unordered_map &> : public std::true_type + { + ; + }; + + template + struct is_boost_unordered_map> : public std::true_type + { + ; + }; + + template + struct is_boost_unordered_map &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_BOOST_UNORDERED_MAP_H_ +#endif // _QX_ENABLE_BOOST diff --git a/include/QxTraits/is_boost_unordered_set.h b/include/QxTraits/is_boost_unordered_set.h new file mode 100644 index 0000000..96c3f7c --- /dev/null +++ b/include/QxTraits/is_boost_unordered_set.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST +#ifndef _QX_IS_BOOST_UNORDERED_SET_H_ +#define _QX_IS_BOOST_UNORDERED_SET_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_boost_unordered_set.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_boost_unordered_set::value : return true if T is a boost::unordered_set<> or boost::unordered_multiset<> container, otherwise return false + */ + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_boost_unordered_set::value : return true if T is a boost::unordered_set<> or boost::unordered_multiset<> container, otherwise return false + */ + template + struct is_boost_unordered_set : public std::false_type + { + ; + }; + + template + struct is_boost_unordered_set> : public std::true_type + { + ; + }; + + template + struct is_boost_unordered_set &> : public std::true_type + { + ; + }; + + template + struct is_boost_unordered_set> : public std::true_type + { + ; + }; + + template + struct is_boost_unordered_set &> : public std::true_type + { + ; + }; + + template + struct is_boost_unordered_set> : public std::true_type + { + ; + }; + + template + struct is_boost_unordered_set &> : public std::true_type + { + ; + }; + + template + struct is_boost_unordered_set> : public std::true_type + { + ; + }; + + template + struct is_boost_unordered_set &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_BOOST_UNORDERED_SET_H_ +#endif // _QX_ENABLE_BOOST diff --git a/include/QxTraits/is_boost_weak_ptr.h b/include/QxTraits/is_boost_weak_ptr.h new file mode 100644 index 0000000..732a668 --- /dev/null +++ b/include/QxTraits/is_boost_weak_ptr.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST +#ifndef _QX_IS_BOOST_WEAK_PTR_H_ +#define _QX_IS_BOOST_WEAK_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_boost_weak_ptr.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_boost_weak_ptr::value : return true if T is a boost::weak_ptr<> smart-pointer, otherwise return false + */ + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_boost_weak_ptr::value : return true if T is a boost::weak_ptr<> smart-pointer, otherwise return false + */ + template + struct is_boost_weak_ptr : public std::false_type + { + ; + }; + + template + struct is_boost_weak_ptr> : public std::true_type + { + ; + }; + + template + struct is_boost_weak_ptr &> : public std::true_type + { + ; + }; + + template + struct is_boost_weak_ptr> : public std::true_type + { + ; + }; + + template + struct is_boost_weak_ptr &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_BOOST_WEAK_PTR_H_ +#endif // _QX_ENABLE_BOOST diff --git a/include/QxTraits/is_container.h b/include/QxTraits/is_container.h new file mode 100644 index 0000000..f3a488a --- /dev/null +++ b/include/QxTraits/is_container.h @@ -0,0 +1,596 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_CONTAINER_H_ +#define _QX_IS_CONTAINER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_container.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_container::value : return true if T is a container from stl, boost, Qt or QxOrm library, otherwise return false + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_container::value : return true if T is a container from stl, boost, Qt or QxOrm library, otherwise return false + */ + template + struct is_container : public std::false_type + { + ; + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + +#endif // _QX_ENABLE_BOOST + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + template + struct is_container> : public std::true_type + { + ; + }; + + template + struct is_container &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_CONTAINER_H_ diff --git a/include/QxTraits/is_container_base_of.h b/include/QxTraits/is_container_base_of.h new file mode 100644 index 0000000..39762f6 --- /dev/null +++ b/include/QxTraits/is_container_base_of.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_CONTAINER_BASE_OF_H_ +#define _QX_IS_CONTAINER_BASE_OF_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#define qx_container_base_of_test_0() (sizeof(qx::trait::is_container_base_of::removeContainer(b, d)) == sizeof(char)) +#define qx_container_base_of_test_1() (sizeof(qx::trait::is_container_base_of::removeContainer((*b_std_vector), d)) == sizeof(char)) +#define qx_container_base_of_test_2() (sizeof(qx::trait::is_container_base_of::removeContainer((*b_std_list), d)) == sizeof(char)) +#define qx_container_base_of_test_3() (sizeof(qx::trait::is_container_base_of::removeContainer((*b_std_set), d)) == sizeof(char)) +#define qx_container_base_of_test_4() (sizeof(qx::trait::is_container_base_of::removeContainer((*b_qt_vector), d)) == sizeof(char)) +#define qx_container_base_of_test_5() (sizeof(qx::trait::is_container_base_of::removeContainer((*b_qt_list), d)) == sizeof(char)) +#define qx_container_base_of_test_6() (sizeof(qx::trait::is_container_base_of::removeContainer((*b_qt_set), d)) == sizeof(char)) +#define qx_container_base_of_test_7() (sizeof(qx::trait::is_container_base_of::removeContainer((*b_qt_linked_list), d)) == sizeof(char)) +#define qx_container_base_of_test_8() (sizeof(qx::trait::is_container_base_of::removeContainer((*b_boost_unordered_set), d)) == sizeof(char)) +#define qx_container_base_of_test_9() (sizeof(qx::trait::is_container_base_of::removeContainer((*b_boost_unordered_multi_set), d)) == sizeof(char)) + +#define qx_container_base_of_test_10() (sizeof(qx::trait::is_container_base_of::removeContainer((*b_std_unordered_set), d)) == sizeof(char)) +#define qx_container_base_of_test_11() (sizeof(qx::trait::is_container_base_of::removeContainer((*b_std_unordered_multi_set), d)) == sizeof(char)) + +#define qx_container_base_of_all_test() \ + qx_container_base_of_test_1() || qx_container_base_of_test_2() || qx_container_base_of_test_3() || \ + qx_container_base_of_test_4() || qx_container_base_of_test_5() || qx_container_base_of_test_6() || \ + qx_container_base_of_test_7() || qx_container_base_of_test_8() || qx_container_base_of_test_9() || \ + qx_container_base_of_test_10() || qx_container_base_of_test_11() + +namespace qx +{ + namespace trait + { + + template + class is_container_base_of + { + + private: + template + static typename std::conditional::value, char, int>::type removeContainer(const std::vector &, const std::vector &); + + template + static typename std::conditional::value, char, int>::type removeContainer(const std::list &, const std::list &); + + template + static typename std::conditional::value, char, int>::type removeContainer(const std::set &, const std::set &); + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + static typename std::conditional::value, char, int>::type removeContainer(const QVector &, const QVector &); +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + static typename std::conditional::value, char, int>::type removeContainer(const QList &, const QList &); + + template + static typename std::conditional::value, char, int>::type removeContainer(const QSet &, const QSet &); + +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + template + static typename std::conditional::value, char, int>::type removeContainer(const QLinkedList &, const QLinkedList &); +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + +#ifdef _QX_ENABLE_BOOST + + template + static typename std::conditional::value, char, int>::type removeContainer(const boost::unordered_set &, const boost::unordered_set &); + + template + static typename std::conditional::value, char, int>::type removeContainer(const boost::unordered_multiset &, const boost::unordered_multiset &); + +#endif // _QX_ENABLE_BOOST + + template + static typename std::conditional::value, char, int>::type removeContainer(const std::unordered_set &, const std::unordered_set &); + + template + static typename std::conditional::value, char, int>::type removeContainer(const std::unordered_multiset &, const std::unordered_multiset &); + + static int removeContainer(...); + static B b; + static D d; + + static std::vector *b_std_vector; + static std::list *b_std_list; + static std::set *b_std_set; + static QVector *b_qt_vector; + static QList *b_qt_list; + static QSet *b_qt_set; + +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + static QLinkedList *b_qt_linked_list; +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + +#ifdef _QX_ENABLE_BOOST + + static boost::unordered_set *b_boost_unordered_set; + static boost::unordered_multiset *b_boost_unordered_multi_set; + +#endif // _QX_ENABLE_BOOST + + static std::unordered_set *b_std_unordered_set; + static std::unordered_multiset *b_std_unordered_multi_set; + + enum + { + value_0 = (qx::trait::is_container::value) + }; + enum + { + value_1 = (qx::trait::is_container::value) + }; + enum + { + value_2 = ((value_0 && value_1) ? qx_container_base_of_test_0() : 0) + }; + enum + { + value_3 = ((value_0 && !value_1) ? qx_container_base_of_all_test() : 0) + }; + + public: + enum + { + value = (qx::trait::is_container_base_of::value_2 || qx::trait::is_container_base_of::value_3) + }; + + typedef typename std::conditional::value, std::true_type, std::false_type>::type type; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_CONTAINER_BASE_OF_H_ diff --git a/include/QxTraits/is_container_key_value.h b/include/QxTraits/is_container_key_value.h new file mode 100644 index 0000000..ddab4f8 --- /dev/null +++ b/include/QxTraits/is_container_key_value.h @@ -0,0 +1,310 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_CONTAINER_KEY_VALUE_H_ +#define _QX_IS_CONTAINER_KEY_VALUE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_container_key_value.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_container_key_value::value : return true if T is a map or hash-map (with template format) container from stl, boost, Qt or QxOrm library, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_container_key_value::value : return true if T is a map or hash-map (with template format) container from stl, boost, Qt or QxOrm library, otherwise return false + */ + template + struct is_container_key_value : public std::false_type + { + ; + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + +#endif // _QX_ENABLE_BOOST + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + + template + struct is_container_key_value> : public std::true_type + { + ; + }; + + template + struct is_container_key_value &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_CONTAINER_KEY_VALUE_H_ diff --git a/include/QxTraits/is_container_to_pod.h b/include/QxTraits/is_container_to_pod.h new file mode 100644 index 0000000..9469f32 --- /dev/null +++ b/include/QxTraits/is_container_to_pod.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_CONTAINER_TO_POD_H_ +#define _QX_IS_CONTAINER_TO_POD_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include + +namespace qx +{ + namespace trait + { + + template + class is_container_to_pod + { + + private: + template + static typename std::conditional::value, char, int>::type removeContainer(const std::vector &); + + template + static typename std::conditional::value, char, int>::type removeContainer(const std::list &); + + template + static typename std::conditional::value, char, int>::type removeContainer(const std::set &); + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + static typename std::conditional::value, char, int>::type removeContainer(const QVector &); +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + static typename std::conditional::value, char, int>::type removeContainer(const QList &); + + template + static typename std::conditional::value, char, int>::type removeContainer(const QSet &); + +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + template + static typename std::conditional::value, char, int>::type removeContainer(const QLinkedList &); +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + +#ifdef _QX_ENABLE_BOOST + + template + static typename std::conditional::value, char, int>::type removeContainer(const boost::unordered_set &); + + template + static typename std::conditional::value, char, int>::type removeContainer(const boost::unordered_multiset &); + +#endif // _QX_ENABLE_BOOST + + template + static typename std::conditional::value, char, int>::type removeContainer(const std::unordered_set &); + + template + static typename std::conditional::value, char, int>::type removeContainer(const std::unordered_multiset &); + + static int removeContainer(...); + static T t; + + public: + enum + { + value = (qx::trait::is_container::value && (sizeof(qx::trait::is_container_to_pod::removeContainer(t)) == sizeof(char))) + }; + + typedef typename std::conditional::value, std::true_type, std::false_type>::type type; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_CONTAINER_TO_POD_H_ diff --git a/include/QxTraits/is_equal.h b/include/QxTraits/is_equal.h new file mode 100644 index 0000000..bad32e7 --- /dev/null +++ b/include/QxTraits/is_equal.h @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_TRAIT_IS_EQUAL_H_ +#define _QX_TRAIT_IS_EQUAL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_equal.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::has_operator_equal_equal::value : return true if T provides operator==() function, T must be registered with QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(T) macro + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#include +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +#ifdef _QX_ENABLE_QT_GUI +#include +#include +#include +#include +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#include +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#include +#endif // _QX_ENABLE_QT_GUI + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::has_operator_equal_equal::value : return true if T provides operator==() function, T must be registered with QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(T) macro + */ + template + struct has_operator_equal_equal + { + enum + { + value = std::is_pointer::value + }; + }; + + } // namespace trait +} // namespace qx + +#define QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(className) \ + namespace qx \ + { \ + namespace trait \ + { \ + template <> \ + struct has_operator_equal_equal \ + { \ + enum \ + { \ + value = true \ + }; \ + }; \ + } \ + } // namespace qx::trait + +#define QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL_TEMPLATE_1(className) \ + namespace qx \ + { \ + namespace trait \ + { \ + template \ + struct has_operator_equal_equal> \ + { \ + enum \ + { \ + value = true \ + }; \ + }; \ + } \ + } // namespace qx::trait + +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(bool) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(int) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(short) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(long) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(float) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(double) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(long double) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(char) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(unsigned int) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(unsigned short) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(unsigned long) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(unsigned char) + +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(std::string) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(std::wstring) + +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(QString) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(QByteArray) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(QDate) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(QDateTime) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(QPoint) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(QRect) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(QSize) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(QTime) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(QUrl) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(QVariant) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(QUuid) + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(QRegExp) +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +#ifdef _QX_ENABLE_QT_GUI +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(QColor) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(QFont) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(QImage) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(QBrush) +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(QMatrix) +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL(QRegion) +#endif // _QX_ENABLE_QT_GUI + +#ifdef _QX_ENABLE_BOOST +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL_TEMPLATE_1(boost::shared_ptr) +#endif // _QX_ENABLE_BOOST + +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL_TEMPLATE_1(QSharedPointer) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL_TEMPLATE_1(QWeakPointer) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL_TEMPLATE_1(qx::dao::ptr) + +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL_TEMPLATE_1(std::shared_ptr) +QX_TYPE_HAS_OPERATOR_EQUAL_EQUAL_TEMPLATE_1(std::weak_ptr) + +#endif // _QX_TRAIT_IS_EQUAL_H_ diff --git a/include/QxTraits/is_ptr_base_of.h b/include/QxTraits/is_ptr_base_of.h new file mode 100644 index 0000000..a45189c --- /dev/null +++ b/include/QxTraits/is_ptr_base_of.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_PTR_BASE_OF_H_ +#define _QX_IS_PTR_BASE_OF_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_ptr_base_of.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_ptr_base_of::value : return true if B and D are pointer type and (*B) is a base class of (*D) or if B and D are same type, otherwise return false + */ + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_ptr_base_of::value : return true if B and D are pointer type and (*B) is a base class of (*D) or if B and D are same type, otherwise return false + */ + template + class is_ptr_base_of + { + + private: + template + static typename std::conditional::value, char, int>::type removePtr(const volatile V *const volatile, const volatile W *const volatile); + + static int removePtr(...); + static B b; + static D d; + + public: + enum + { + value = (std::is_pointer::value && std::is_pointer::value && (sizeof(qx::trait::is_ptr_base_of::removePtr(b, d)) == sizeof(char))) + }; + + typedef typename std::conditional::value, std::true_type, std::false_type>::type type; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_PTR_BASE_OF_H_ diff --git a/include/QxTraits/is_ptr_to_pod.h b/include/QxTraits/is_ptr_to_pod.h new file mode 100644 index 0000000..d5a3192 --- /dev/null +++ b/include/QxTraits/is_ptr_to_pod.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_PTR_TO_POD_H_ +#define _QX_IS_PTR_TO_POD_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_ptr_to_pod.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_ptr_to_pod::value : return true if T is a pointer to a POD type (char, int, long, etc.), otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_ptr_to_pod::value : return true if T is a pointer to a POD type (char, int, long, etc.), otherwise return false + */ + template + class is_ptr_to_pod + { + + private: + template + static typename std::conditional::value, char, int>::type removePtr(const volatile U *const volatile); + + static int removePtr(...); + static T t; + + public: + enum + { + value = (std::is_pointer::value && (sizeof(qx::trait::is_ptr_to_pod::removePtr(t)) == sizeof(char))) + }; + + typedef typename std::conditional::value, std::true_type, std::false_type>::type type; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_PTR_TO_POD_H_ diff --git a/include/QxTraits/is_qt_hash.h b/include/QxTraits/is_qt_hash.h new file mode 100644 index 0000000..0e927bb --- /dev/null +++ b/include/QxTraits/is_qt_hash.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_QT_HASH_H_ +#define _QX_IS_QT_HASH_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_qt_hash.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_qt_hash::value : return true if T is a QHash<> container of Qt library, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_qt_hash::value : return true if T is a QHash<> container of Qt library, otherwise return false + */ + template + struct is_qt_hash : public std::false_type + { + ; + }; + + template + struct is_qt_hash> : public std::true_type + { + ; + }; + + template + struct is_qt_hash &> : public std::true_type + { + ; + }; + + template + struct is_qt_hash> : public std::true_type + { + ; + }; + + template + struct is_qt_hash &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_QT_HASH_H_ diff --git a/include/QxTraits/is_qt_linked_list.h b/include/QxTraits/is_qt_linked_list.h new file mode 100644 index 0000000..dad7541 --- /dev/null +++ b/include/QxTraits/is_qt_linked_list.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) +#ifndef _QX_IS_QT_LINKED_LIST_H_ +#define _QX_IS_QT_LINKED_LIST_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_qt_linked_list.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_qt_linked_list::value : return true if T is a QLinkedList<> container of Qt library, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_qt_linked_list::value : return true if T is a QLinkedList<> container of Qt library, otherwise return false + */ + template + struct is_qt_linked_list : public std::false_type + { + ; + }; + + template + struct is_qt_linked_list> : public std::true_type + { + ; + }; + + template + struct is_qt_linked_list &> : public std::true_type + { + ; + }; + + template + struct is_qt_linked_list> : public std::true_type + { + ; + }; + + template + struct is_qt_linked_list &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_QT_LINKED_LIST_H_ +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) diff --git a/include/QxTraits/is_qt_list.h b/include/QxTraits/is_qt_list.h new file mode 100644 index 0000000..d297f7a --- /dev/null +++ b/include/QxTraits/is_qt_list.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_QT_LIST_H_ +#define _QX_IS_QT_LIST_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_qt_list.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_qt_list::value : return true if T is a QList<> container of Qt library, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_qt_list::value : return true if T is a QList<> container of Qt library, otherwise return false + */ + template + struct is_qt_list : public std::false_type + { + ; + }; + + template + struct is_qt_list> : public std::true_type + { + ; + }; + + template + struct is_qt_list &> : public std::true_type + { + ; + }; + + template + struct is_qt_list> : public std::true_type + { + ; + }; + + template + struct is_qt_list &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_QT_LIST_H_ diff --git a/include/QxTraits/is_qt_map.h b/include/QxTraits/is_qt_map.h new file mode 100644 index 0000000..58e815c --- /dev/null +++ b/include/QxTraits/is_qt_map.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_QT_MAP_H_ +#define _QX_IS_QT_MAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_qt_map.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_qt_map::value : return true if T is a QMap<> container of Qt library, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_qt_map::value : return true if T is a QMap<> container of Qt library, otherwise return false + */ + template + struct is_qt_map : public std::false_type + { + ; + }; + + template + struct is_qt_map> : public std::true_type + { + ; + }; + + template + struct is_qt_map &> : public std::true_type + { + ; + }; + + template + struct is_qt_map> : public std::true_type + { + ; + }; + + template + struct is_qt_map &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_QT_MAP_H_ diff --git a/include/QxTraits/is_qt_multi_hash.h b/include/QxTraits/is_qt_multi_hash.h new file mode 100644 index 0000000..defdaa6 --- /dev/null +++ b/include/QxTraits/is_qt_multi_hash.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_QT_MULTI_HASH_H_ +#define _QX_IS_QT_MULTI_HASH_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_qt_multi_hash.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_qt_multi_hash::value : return true if T is a QMultiHash<> container of Qt library, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_qt_multi_hash::value : return true if T is a QMultiHash<> container of Qt library, otherwise return false + */ + template + struct is_qt_multi_hash : public std::false_type + { + ; + }; + + template + struct is_qt_multi_hash> : public std::true_type + { + ; + }; + + template + struct is_qt_multi_hash &> : public std::true_type + { + ; + }; + + template + struct is_qt_multi_hash> : public std::true_type + { + ; + }; + + template + struct is_qt_multi_hash &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_QT_MULTI_HASH_H_ diff --git a/include/QxTraits/is_qt_multi_map.h b/include/QxTraits/is_qt_multi_map.h new file mode 100644 index 0000000..35d9478 --- /dev/null +++ b/include/QxTraits/is_qt_multi_map.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_QT_MULTI_MAP_H_ +#define _QX_IS_QT_MULTI_MAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_qt_multi_map.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_qt_multi_map::value : return true if T is a QMultiMap<> container of Qt library, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_qt_multi_map::value : return true if T is a QMultiMap<> container of Qt library, otherwise return false + */ + template + struct is_qt_multi_map : public std::false_type + { + ; + }; + + template + struct is_qt_multi_map> : public std::true_type + { + ; + }; + + template + struct is_qt_multi_map &> : public std::true_type + { + ; + }; + + template + struct is_qt_multi_map> : public std::true_type + { + ; + }; + + template + struct is_qt_multi_map &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_QT_MULTI_MAP_H_ diff --git a/include/QxTraits/is_qt_scoped_ptr.h b/include/QxTraits/is_qt_scoped_ptr.h new file mode 100644 index 0000000..c9974b3 --- /dev/null +++ b/include/QxTraits/is_qt_scoped_ptr.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) +#ifndef _QX_IS_QT_SCOPED_PTR_H_ +#define _QX_IS_QT_SCOPED_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_qt_scoped_ptr.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_qt_scoped_ptr::value : return true if T is a QScopedPointer<> smart-pointer of Qt library, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_qt_scoped_ptr::value : return true if T is a QScopedPointer<> smart-pointer of Qt library, otherwise return false + */ + template + struct is_qt_scoped_ptr : public std::false_type + { + ; + }; + + template + struct is_qt_scoped_ptr> : public std::true_type + { + ; + }; + + template + struct is_qt_scoped_ptr &> : public std::true_type + { + ; + }; + + template + struct is_qt_scoped_ptr> : public std::true_type + { + ; + }; + + template + struct is_qt_scoped_ptr &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_QT_SCOPED_PTR_H_ +#endif // (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) + +#if (QT_VERSION < QT_VERSION_CHECK(4, 6, 0)) +#ifndef _QX_IS_QT_SCOPED_PTR_H_ +#define _QX_IS_QT_SCOPED_PTR_H_ +namespace qx +{ + namespace trait + { + template + class is_qt_scoped_ptr + { + public: + enum + { + value = false + }; + typedef std::false_type type; + }; + } +} // namespace qx::trait +#endif // _QX_IS_QT_SCOPED_PTR_H_ +#endif // (QT_VERSION < QT_VERSION_CHECK(4, 6, 0)) diff --git a/include/QxTraits/is_qt_set.h b/include/QxTraits/is_qt_set.h new file mode 100644 index 0000000..03936fe --- /dev/null +++ b/include/QxTraits/is_qt_set.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_QT_SET_H_ +#define _QX_IS_QT_SET_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_qt_set.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_qt_set::value : return true if T is a QSet<> container of Qt library, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_qt_set::value : return true if T is a QSet<> container of Qt library, otherwise return false + */ + template + struct is_qt_set : public std::false_type + { + ; + }; + + template + struct is_qt_set> : public std::true_type + { + ; + }; + + template + struct is_qt_set &> : public std::true_type + { + ; + }; + + template + struct is_qt_set> : public std::true_type + { + ; + }; + + template + struct is_qt_set &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_QT_SET_H_ diff --git a/include/QxTraits/is_qt_shared_data_ptr.h b/include/QxTraits/is_qt_shared_data_ptr.h new file mode 100644 index 0000000..9b5c27c --- /dev/null +++ b/include/QxTraits/is_qt_shared_data_ptr.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_QT_SHARED_DATA_PTR_H_ +#define _QX_IS_QT_SHARED_DATA_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_qt_shared_data_ptr.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_qt_shared_data_ptr::value : return true if T is a QSharedDataPointer<> smart-pointer of Qt library, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_qt_shared_data_ptr::value : return true if T is a QSharedDataPointer<> smart-pointer of Qt library, otherwise return false + */ + template + struct is_qt_shared_data_ptr : public std::false_type + { + ; + }; + + template + struct is_qt_shared_data_ptr> : public std::true_type + { + ; + }; + + template + struct is_qt_shared_data_ptr &> : public std::true_type + { + ; + }; + + template + struct is_qt_shared_data_ptr> : public std::true_type + { + ; + }; + + template + struct is_qt_shared_data_ptr &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_QT_SHARED_DATA_PTR_H_ diff --git a/include/QxTraits/is_qt_shared_ptr.h b/include/QxTraits/is_qt_shared_ptr.h new file mode 100644 index 0000000..9ecb7a8 --- /dev/null +++ b/include/QxTraits/is_qt_shared_ptr.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_QT_SHARED_PTR_H_ +#define _QX_IS_QT_SHARED_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_qt_shared_ptr.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_qt_shared_ptr::value : return true if T is a QSharedPointer<> smart-pointer of Qt library, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_qt_shared_ptr::value : return true if T is a QSharedPointer<> smart-pointer of Qt library, otherwise return false + */ + template + struct is_qt_shared_ptr : public std::false_type + { + ; + }; + + template + struct is_qt_shared_ptr> : public std::true_type + { + ; + }; + + template + struct is_qt_shared_ptr &> : public std::true_type + { + ; + }; + + template + struct is_qt_shared_ptr> : public std::true_type + { + ; + }; + + template + struct is_qt_shared_ptr &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_QT_SHARED_PTR_H_ diff --git a/include/QxTraits/is_qt_variant_compatible.h b/include/QxTraits/is_qt_variant_compatible.h new file mode 100644 index 0000000..70b0f47 --- /dev/null +++ b/include/QxTraits/is_qt_variant_compatible.h @@ -0,0 +1,415 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_QT_VARIANT_COMPATIBLE_H_ +#define _QX_IS_QT_VARIANT_COMPATIBLE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_qt_variant_compatible.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_qt_variant_compatible::value : return true if T can be host into a QVariant object of Qt library, otherwise return false + */ + +#include + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_qt_variant_compatible::value : return true if T can be host into a QVariant object of Qt library, otherwise return false + */ + template + struct is_qt_variant_compatible + { + enum + { + value = (std::is_enum::value || std::is_integral::value) + }; + }; + + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +#ifdef _QX_ENABLE_QT_GUI + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible + { + enum + { + value = true + }; + }; +#endif // _QX_ENABLE_QT_GUI + + template <> + struct is_qt_variant_compatible> + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible> + { + enum + { + value = true + }; + }; + template <> + struct is_qt_variant_compatible> + { + enum + { + value = true + }; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_QT_VARIANT_COMPATIBLE_H_ diff --git a/include/QxTraits/is_qt_vector.h b/include/QxTraits/is_qt_vector.h new file mode 100644 index 0000000..bcd6dc4 --- /dev/null +++ b/include/QxTraits/is_qt_vector.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_QT_VECTOR_H_ +#define _QX_IS_QT_VECTOR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_qt_vector.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_qt_vector::value : return true if T is a QVector<> container of Qt library, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_qt_vector::value : return true if T is a QVector<> container of Qt library, otherwise return false + */ + template + struct is_qt_vector : public std::false_type + { + ; + }; + + template + struct is_qt_vector> : public std::true_type + { + ; + }; + + template + struct is_qt_vector &> : public std::true_type + { + ; + }; + + template + struct is_qt_vector> : public std::true_type + { + ; + }; + + template + struct is_qt_vector &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_QT_VECTOR_H_ diff --git a/include/QxTraits/is_qt_weak_ptr.h b/include/QxTraits/is_qt_weak_ptr.h new file mode 100644 index 0000000..a7a11b9 --- /dev/null +++ b/include/QxTraits/is_qt_weak_ptr.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_QT_WEAK_PTR_H_ +#define _QX_IS_QT_WEAK_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_qt_weak_ptr.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_qt_weak_ptr::value : return true if T is a QWeakPointer<> smart-pointer of Qt library, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_qt_weak_ptr::value : return true if T is a QWeakPointer<> smart-pointer of Qt library, otherwise return false + */ + template + struct is_qt_weak_ptr : public std::false_type + { + ; + }; + + template + struct is_qt_weak_ptr> : public std::true_type + { + ; + }; + + template + struct is_qt_weak_ptr &> : public std::true_type + { + ; + }; + + template + struct is_qt_weak_ptr> : public std::true_type + { + ; + }; + + template + struct is_qt_weak_ptr &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_QT_WEAK_PTR_H_ diff --git a/include/QxTraits/is_qx_collection.h b/include/QxTraits/is_qx_collection.h new file mode 100644 index 0000000..2fe2f44 --- /dev/null +++ b/include/QxTraits/is_qx_collection.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_QX_COLLECTION_H_ +#define _QX_IS_QX_COLLECTION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_qx_collection.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_qx_collection::value : return true if T is a qx::QxCollection<> container of QxOrm library, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_qx_collection::value : return true if T is a qx::QxCollection<> container of QxOrm library, otherwise return false + */ + template + class is_qx_collection + { + + public: + enum + { + value = std::is_base_of::value + }; + + typedef typename std::conditional::value, + std::true_type, + std::false_type>::type type; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_QX_COLLECTION_H_ diff --git a/include/QxTraits/is_qx_dao_ptr.h b/include/QxTraits/is_qx_dao_ptr.h new file mode 100644 index 0000000..c6f24b3 --- /dev/null +++ b/include/QxTraits/is_qx_dao_ptr.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_QX_DAO_PTR_H_ +#define _QX_IS_QX_DAO_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_qx_dao_ptr.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_qx_dao_ptr::value : return true if T is a qx::dao::ptr<> smart-pointer of QxOrm library, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_qx_dao_ptr::value : return true if T is a qx::dao::ptr<> smart-pointer of QxOrm library, otherwise return false + */ + template + struct is_qx_dao_ptr : public std::false_type + { + ; + }; + + template + struct is_qx_dao_ptr> : public std::true_type + { + ; + }; + + template + struct is_qx_dao_ptr &> : public std::true_type + { + ; + }; + + template + struct is_qx_dao_ptr> : public std::true_type + { + ; + }; + + template + struct is_qx_dao_ptr &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_QX_DAO_PTR_H_ diff --git a/include/QxTraits/is_qx_pod.h b/include/QxTraits/is_qx_pod.h new file mode 100644 index 0000000..f08935c --- /dev/null +++ b/include/QxTraits/is_qx_pod.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_QX_POD_H_ +#define _QX_IS_QX_POD_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_qx_pod.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_qx_pod::value : return true if T is a POD type and not a pointer + */ + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_qx_pod::value : return true if T is a POD type and not a pointer + */ + template + struct is_qx_pod + { + + enum + { + value = (std::is_pod::value && !std::is_pointer::value && !std::is_member_pointer::value) + }; + + typedef typename std::conditional::value, std::true_type, std::false_type>::type type; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_QX_POD_H_ diff --git a/include/QxTraits/is_qx_registered.h b/include/QxTraits/is_qx_registered.h new file mode 100644 index 0000000..99b2229 --- /dev/null +++ b/include/QxTraits/is_qx_registered.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_QX_REGISTERED_H_ +#define _QX_IS_QX_REGISTERED_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_qx_registered.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_qx_registered::value : return true if T is registered into QxOrm context to provide persitence (ORM), serialization and introspection features + */ + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_qx_registered::value : return true if T is registered into QxOrm context to provide persitence (ORM), serialization and introspection features + */ + template + struct is_qx_registered + { + enum + { + value = false + }; + }; + + } // namespace trait +} // namespace qx + +#define QX_SET_REGISTERED(className) \ + namespace qx \ + { \ + namespace trait \ + { \ + template <> \ + struct is_qx_registered \ + { \ + enum \ + { \ + value = true \ + }; \ + }; \ + } \ + } // namespace qx::trait + +#endif // _QX_IS_QX_REGISTERED_H_ diff --git a/include/QxTraits/is_smart_ptr.h b/include/QxTraits/is_smart_ptr.h new file mode 100644 index 0000000..e327485 --- /dev/null +++ b/include/QxTraits/is_smart_ptr.h @@ -0,0 +1,373 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_SMART_PTR_H_ +#define _QX_IS_SMART_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_smart_ptr.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_smart_ptr::value : return true if T is a smart-pointer of boost, Qt or QxOrm libraries, otherwise return false + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_smart_ptr::value : return true if T is a smart-pointer of boost, Qt or QxOrm libraries, otherwise return false + */ + template + struct is_smart_ptr : public std::false_type + { + ; + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + +#endif // _QX_ENABLE_BOOST + +#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + +#endif // (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr> : public std::true_type + { + ; + }; + + template + struct is_smart_ptr &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_SMART_PTR_H_ diff --git a/include/QxTraits/is_smart_ptr_base_of.h b/include/QxTraits/is_smart_ptr_base_of.h new file mode 100644 index 0000000..c4bc51d --- /dev/null +++ b/include/QxTraits/is_smart_ptr_base_of.h @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_SMART_PTR_BASE_OF_H_ +#define _QX_IS_SMART_PTR_BASE_OF_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_smart_ptr_base_of.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_smart_ptr_base_of::value : return true if B and D are smart-pointers of boost, Qt or QxOrm libraries and if (*B) is a base class of (*D), otherwise return false + */ + +#include + +#define qx_smart_ptr_base_of_test_0() (sizeof(qx::trait::is_smart_ptr_base_of::removeSmartPtr(b, d)) == sizeof(char)) +#define qx_smart_ptr_base_of_test_1() (sizeof(qx::trait::is_smart_ptr_base_of::removeSmartPtr((*b_boost_scoped_ptr), d)) == sizeof(char)) +#define qx_smart_ptr_base_of_test_2() (sizeof(qx::trait::is_smart_ptr_base_of::removeSmartPtr((*b_boost_shared_ptr), d)) == sizeof(char)) +#define qx_smart_ptr_base_of_test_3() (sizeof(qx::trait::is_smart_ptr_base_of::removeSmartPtr((*b_boost_weak_ptr), d)) == sizeof(char)) +#define qx_smart_ptr_base_of_test_4() (sizeof(qx::trait::is_smart_ptr_base_of::removeSmartPtr((*b_boost_intrusive_ptr), d)) == sizeof(char)) +#define qx_smart_ptr_base_of_test_5() (sizeof(qx::trait::is_smart_ptr_base_of::removeSmartPtr((*b_qt_shared_data_ptr), d)) == sizeof(char)) +#define qx_smart_ptr_base_of_test_6() (sizeof(qx::trait::is_smart_ptr_base_of::removeSmartPtr((*b_qt_shared_ptr), d)) == sizeof(char)) +#define qx_smart_ptr_base_of_test_7() (sizeof(qx::trait::is_smart_ptr_base_of::removeSmartPtr((*b_qt_weak_ptr), d)) == sizeof(char)) +#define qx_smart_ptr_base_of_test_8() (sizeof(qx::trait::is_smart_ptr_base_of::removeSmartPtr((*b_qx_dao_ptr), d)) == sizeof(char)) + +#define qx_smart_ptr_base_of_test_9() (sizeof(qx::trait::is_smart_ptr_base_of::removeSmartPtr((*b_std_unique_ptr), d)) == sizeof(char)) +#define qx_smart_ptr_base_of_test_10() (sizeof(qx::trait::is_smart_ptr_base_of::removeSmartPtr((*b_std_shared_ptr), d)) == sizeof(char)) +#define qx_smart_ptr_base_of_test_11() (sizeof(qx::trait::is_smart_ptr_base_of::removeSmartPtr((*b_std_weak_ptr), d)) == sizeof(char)) + +#define qx_smart_ptr_base_of_all_test() \ + qx_smart_ptr_base_of_test_1() || qx_smart_ptr_base_of_test_2() || qx_smart_ptr_base_of_test_3() || \ + qx_smart_ptr_base_of_test_4() || qx_smart_ptr_base_of_test_5() || qx_smart_ptr_base_of_test_6() || \ + qx_smart_ptr_base_of_test_7() || qx_smart_ptr_base_of_test_8() || qx_smart_ptr_base_of_test_9() || \ + qx_smart_ptr_base_of_test_10() || qx_smart_ptr_base_of_test_11() + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_smart_ptr_base_of::value : return true if B and D are smart-pointers of boost, Qt or QxOrm libraries and if (*B) is a base class of (*D), otherwise return false + */ + template + class is_smart_ptr_base_of + { + + private: +#ifdef _QX_ENABLE_BOOST + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const boost::scoped_ptr &, const boost::scoped_ptr &); + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const boost::shared_ptr &, const boost::shared_ptr &); + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const boost::weak_ptr &, const boost::weak_ptr &); + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const boost::intrusive_ptr &, const boost::intrusive_ptr &); + +#endif // _QX_ENABLE_BOOST + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const QSharedDataPointer &, const QSharedDataPointer &); + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const QSharedPointer &, const QSharedPointer &); + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const QWeakPointer &, const QWeakPointer &); + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const qx::dao::ptr &, const qx::dao::ptr &); + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const std::unique_ptr &, const std::unique_ptr &); + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const std::shared_ptr &, const std::shared_ptr &); + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const std::weak_ptr &, const std::weak_ptr &); + + static int removeSmartPtr(...); + static B b; + static D d; + +#ifdef _QX_ENABLE_BOOST + + static boost::scoped_ptr *b_boost_scoped_ptr; + static boost::shared_ptr *b_boost_shared_ptr; + static boost::weak_ptr *b_boost_weak_ptr; + static boost::intrusive_ptr *b_boost_intrusive_ptr; + +#endif // _QX_ENABLE_BOOST + + static QSharedDataPointer *b_qt_shared_data_ptr; + static QSharedPointer *b_qt_shared_ptr; + static QWeakPointer *b_qt_weak_ptr; + static qx::dao::ptr *b_qx_dao_ptr; + + static std::unique_ptr *b_std_unique_ptr; + static std::shared_ptr *b_std_shared_ptr; + static std::weak_ptr *b_std_weak_ptr; + + enum + { + value_0 = (qx::trait::is_smart_ptr::value) + }; + enum + { + value_1 = (qx::trait::is_smart_ptr::value) + }; + enum + { + value_2 = ((value_0 && value_1) ? qx_smart_ptr_base_of_test_0() : 0) + }; + enum + { + value_3 = ((value_0 && !value_1) ? qx_smart_ptr_base_of_all_test() : 0) + }; + + public: + enum + { + value = (qx::trait::is_smart_ptr_base_of::value_2 || qx::trait::is_smart_ptr_base_of::value_3) + }; + + typedef typename std::conditional::value, std::true_type, std::false_type>::type type; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_SMART_PTR_BASE_OF_H_ diff --git a/include/QxTraits/is_smart_ptr_to_pod.h b/include/QxTraits/is_smart_ptr_to_pod.h new file mode 100644 index 0000000..9cda1bd --- /dev/null +++ b/include/QxTraits/is_smart_ptr_to_pod.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_SMART_PTR_TO_POD_H_ +#define _QX_IS_SMART_PTR_TO_POD_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_smart_ptr_to_pod.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_smart_ptr_to_pod::value : return true if T is a smart-pointer of boost, Qt or QxOrm libraries and (*T) is a POD type (char, int, long, etc.), otherwise return false + */ + +#include +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_smart_ptr_to_pod::value : return true if T is a smart-pointer of boost, Qt or QxOrm libraries and (*T) is a POD type (char, int, long, etc.), otherwise return false + */ + template + class is_smart_ptr_to_pod + { + + private: +#ifdef _QX_ENABLE_BOOST + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const boost::scoped_ptr &); + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const boost::shared_ptr &); + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const boost::weak_ptr &); + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const boost::intrusive_ptr &); + +#endif // _QX_ENABLE_BOOST + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const QSharedDataPointer &); + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const QSharedPointer &); + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const QWeakPointer &); + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const qx::dao::ptr &); + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const std::unique_ptr &); + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const std::shared_ptr &); + + template + static typename std::conditional::value, char, int>::type removeSmartPtr(const std::weak_ptr &); + + static int removeSmartPtr(...); + static T t; + + public: + enum + { + value = (qx::trait::is_smart_ptr::value && (sizeof(qx::trait::is_smart_ptr_to_pod::removeSmartPtr(t)) == sizeof(char))) + }; + + typedef typename std::conditional::value, std::true_type, std::false_type>::type type; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_SMART_PTR_TO_POD_H_ diff --git a/include/QxTraits/is_std_list.h b/include/QxTraits/is_std_list.h new file mode 100644 index 0000000..c12b670 --- /dev/null +++ b/include/QxTraits/is_std_list.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_STD_LIST_H_ +#define _QX_IS_STD_LIST_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_std_list.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_std_list::value : return true if T is a std::list<> container of stl library, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_std_list::value : return true if T is a std::list<> container of stl library, otherwise return false + */ + template + struct is_std_list : public std::false_type + { + ; + }; + + template + struct is_std_list> : public std::true_type + { + ; + }; + + template + struct is_std_list &> : public std::true_type + { + ; + }; + + template + struct is_std_list> : public std::true_type + { + ; + }; + + template + struct is_std_list &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_STD_LIST_H_ diff --git a/include/QxTraits/is_std_map.h b/include/QxTraits/is_std_map.h new file mode 100644 index 0000000..ce06c04 --- /dev/null +++ b/include/QxTraits/is_std_map.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_STD_MAP_H_ +#define _QX_IS_STD_MAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_std_map.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_std_map::value : return true if T is a std::map<> container of stl library, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_std_map::value : return true if T is a std::map<> container of stl library, otherwise return false + */ + template + struct is_std_map : public std::false_type + { + ; + }; + + template + struct is_std_map> : public std::true_type + { + ; + }; + + template + struct is_std_map &> : public std::true_type + { + ; + }; + + template + struct is_std_map> : public std::true_type + { + ; + }; + + template + struct is_std_map &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_STD_MAP_H_ diff --git a/include/QxTraits/is_std_set.h b/include/QxTraits/is_std_set.h new file mode 100644 index 0000000..97118bf --- /dev/null +++ b/include/QxTraits/is_std_set.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_STD_SET_H_ +#define _QX_IS_STD_SET_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_std_set.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_std_set::value : return true if T is a std::set<> container of stl library, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_std_set::value : return true if T is a std::set<> container of stl library, otherwise return false + */ + template + struct is_std_set : public std::false_type + { + ; + }; + + template + struct is_std_set> : public std::true_type + { + ; + }; + + template + struct is_std_set &> : public std::true_type + { + ; + }; + + template + struct is_std_set> : public std::true_type + { + ; + }; + + template + struct is_std_set &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_STD_SET_H_ diff --git a/include/QxTraits/is_std_shared_ptr.h b/include/QxTraits/is_std_shared_ptr.h new file mode 100644 index 0000000..f5813ae --- /dev/null +++ b/include/QxTraits/is_std_shared_ptr.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_STD_SHARED_PTR_H_ +#define _QX_IS_STD_SHARED_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_std_shared_ptr.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_std_shared_ptr::value : return true if T is a std::shared_ptr<> smart-pointer, otherwise return false + */ + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_std_shared_ptr::value : return true if T is a std::shared_ptr<> smart-pointer, otherwise return false + */ + template + struct is_std_shared_ptr : public std::false_type + { + ; + }; + + template + struct is_std_shared_ptr> : public std::true_type + { + ; + }; + + template + struct is_std_shared_ptr &> : public std::true_type + { + ; + }; + + template + struct is_std_shared_ptr> : public std::true_type + { + ; + }; + + template + struct is_std_shared_ptr &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_STD_SHARED_PTR_H_ diff --git a/include/QxTraits/is_std_unique_ptr.h b/include/QxTraits/is_std_unique_ptr.h new file mode 100644 index 0000000..30d426f --- /dev/null +++ b/include/QxTraits/is_std_unique_ptr.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_STD_UNIQUE_PTR_H_ +#define _QX_IS_STD_UNIQUE_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_std_unique_ptr.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_std_unique_ptr::value : return true if T is a std::unique_ptr<> smart-pointer, otherwise return false + */ + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_std_unique_ptr::value : return true if T is a std::unique_ptr<> smart-pointer, otherwise return false + */ + template + struct is_std_unique_ptr : public std::false_type + { + ; + }; + + template + struct is_std_unique_ptr> : public std::true_type + { + ; + }; + + template + struct is_std_unique_ptr &> : public std::true_type + { + ; + }; + + template + struct is_std_unique_ptr> : public std::true_type + { + ; + }; + + template + struct is_std_unique_ptr &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_STD_UNIQUE_PTR_H_ diff --git a/include/QxTraits/is_std_unordered_map.h b/include/QxTraits/is_std_unordered_map.h new file mode 100644 index 0000000..b55347b --- /dev/null +++ b/include/QxTraits/is_std_unordered_map.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_STD_UNORDERED_MAP_H_ +#define _QX_IS_STD_UNORDERED_MAP_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_std_unordered_map.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_std_unordered_map::value : return true if T is a std::unordered_map<> or std::unordered_multimap<> container, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_std_unordered_map::value : return true if T is a std::unordered_map<> or std::unordered_multimap<> container, otherwise return false + */ + template + struct is_std_unordered_map : public std::false_type + { + ; + }; + + template + struct is_std_unordered_map> : public std::true_type + { + ; + }; + + template + struct is_std_unordered_map &> : public std::true_type + { + ; + }; + + template + struct is_std_unordered_map> : public std::true_type + { + ; + }; + + template + struct is_std_unordered_map &> : public std::true_type + { + ; + }; + + template + struct is_std_unordered_map> : public std::true_type + { + ; + }; + + template + struct is_std_unordered_map &> : public std::true_type + { + ; + }; + + template + struct is_std_unordered_map> : public std::true_type + { + ; + }; + + template + struct is_std_unordered_map &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_STD_UNORDERED_MAP_H_ diff --git a/include/QxTraits/is_std_unordered_set.h b/include/QxTraits/is_std_unordered_set.h new file mode 100644 index 0000000..cce4cba --- /dev/null +++ b/include/QxTraits/is_std_unordered_set.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_STD_UNORDERED_SET_H_ +#define _QX_IS_STD_UNORDERED_SET_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_std_unordered_set.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_std_unordered_set::value : return true if T is a std::unordered_set<> or std::unordered_multiset<> container, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_std_unordered_set::value : return true if T is a std::unordered_set<> or std::unordered_multiset<> container, otherwise return false + */ + template + struct is_std_unordered_set : public std::false_type + { + ; + }; + + template + struct is_std_unordered_set> : public std::true_type + { + ; + }; + + template + struct is_std_unordered_set &> : public std::true_type + { + ; + }; + + template + struct is_std_unordered_set> : public std::true_type + { + ; + }; + + template + struct is_std_unordered_set &> : public std::true_type + { + ; + }; + + template + struct is_std_unordered_set> : public std::true_type + { + ; + }; + + template + struct is_std_unordered_set &> : public std::true_type + { + ; + }; + + template + struct is_std_unordered_set> : public std::true_type + { + ; + }; + + template + struct is_std_unordered_set &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_STD_UNORDERED_SET_H_ diff --git a/include/QxTraits/is_std_vector.h b/include/QxTraits/is_std_vector.h new file mode 100644 index 0000000..4c697e0 --- /dev/null +++ b/include/QxTraits/is_std_vector.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_STD_VECTOR_H_ +#define _QX_IS_STD_VECTOR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_std_vector.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_std_vector::value : return true if T is a std::vector<> container of stl library, otherwise return false + */ + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_std_vector::value : return true if T is a std::vector<> container of stl library, otherwise return false + */ + template + struct is_std_vector : public std::false_type + { + ; + }; + + template + struct is_std_vector> : public std::true_type + { + ; + }; + + template + struct is_std_vector &> : public std::true_type + { + ; + }; + + template + struct is_std_vector> : public std::true_type + { + ; + }; + + template + struct is_std_vector &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_STD_VECTOR_H_ diff --git a/include/QxTraits/is_std_weak_ptr.h b/include/QxTraits/is_std_weak_ptr.h new file mode 100644 index 0000000..811d1fe --- /dev/null +++ b/include/QxTraits/is_std_weak_ptr.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_STD_WEAK_PTR_H_ +#define _QX_IS_STD_WEAK_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_std_weak_ptr.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_std_weak_ptr::value : return true if T is a std::weak_ptr<> smart-pointer, otherwise return false + */ + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_std_weak_ptr::value : return true if T is a std::weak_ptr<> smart-pointer, otherwise return false + */ + template + struct is_std_weak_ptr : public std::false_type + { + ; + }; + + template + struct is_std_weak_ptr> : public std::true_type + { + ; + }; + + template + struct is_std_weak_ptr &> : public std::true_type + { + ; + }; + + template + struct is_std_weak_ptr> : public std::true_type + { + ; + }; + + template + struct is_std_weak_ptr &> : public std::true_type + { + ; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_STD_WEAK_PTR_H_ diff --git a/include/QxTraits/is_valid_primary_key.h b/include/QxTraits/is_valid_primary_key.h new file mode 100644 index 0000000..acde9fe --- /dev/null +++ b/include/QxTraits/is_valid_primary_key.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_IS_VALID_PRIMARY_KEY_H_ +#define _QX_IS_VALID_PRIMARY_KEY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file is_valid_primary_key.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::is_valid_primary_key(const T & t) : return true if t can be a valid primary key to be inserted into a database, otherwise return false + */ + +namespace qx +{ + namespace trait + { + namespace detail + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::is_valid_primary_key(const T & t) : return true if t can be a valid primary key to be inserted into a database, otherwise return false + */ + template + struct is_valid_primary_key + { + static inline bool get(const T &t) + { + Q_UNUSED(t); + qAssert(false); + return false; + } + }; + + template <> + struct is_valid_primary_key + { + static inline bool get(const short &t) { return (t != 0); } + }; + + template <> + struct is_valid_primary_key + { + static inline bool get(const int &t) { return (t != 0); } + }; + + template <> + struct is_valid_primary_key + { + static inline bool get(const long &t) { return (t != 0); } + }; + + template <> + struct is_valid_primary_key + { + static inline bool get(const long long &t) { return (t != 0); } + }; + + template <> + struct is_valid_primary_key + { + static inline bool get(const QString &t) { return (!t.isEmpty()); } + }; + + template <> + struct is_valid_primary_key + { + static inline bool get(const QByteArray &t) { return (!t.isEmpty()); } + }; + + template <> + struct is_valid_primary_key + { + static inline bool get(const std::string &t) { return (!t.empty()); } + }; + + template <> + struct is_valid_primary_key + { + static inline bool get(const std::wstring &t) { return (!t.empty()); } + }; + + template <> + struct is_valid_primary_key + { + static inline bool get(const QVariant &t) + { + if (t.type() == QVariant::ByteArray) + { + return qx::trait::detail::is_valid_primary_key::get(t.toByteArray()); + } + if (t.type() == QVariant::String) + { + return qx::trait::detail::is_valid_primary_key::get(t.toString()); + } + return (!t.isNull() && (t.toLongLong() != 0)); + } + }; + + } // namespace detail + + template + inline bool is_valid_primary_key(const T &t) + { + return qx::trait::detail::is_valid_primary_key::get(t); + } + + } // namespace trait +} // namespace qx + +#endif // _QX_IS_VALID_PRIMARY_KEY_H_ diff --git a/include/QxTraits/qt_meta_object.h b/include/QxTraits/qt_meta_object.h new file mode 100644 index 0000000..3a035b1 --- /dev/null +++ b/include/QxTraits/qt_meta_object.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_QT_META_OBJECT_H_ +#define _QX_QT_META_OBJECT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file qt_meta_object.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::qt_meta_object::get() : if T is based on QObject class, then return QMetaObject instance of Qt introspection engine, else return NULL + */ + +#include +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::qt_meta_object::get() : if T is based on QObject class, then return QMetaObject instance of Qt introspection engine, else return NULL + */ + template + class qt_meta_object + { + + public: + enum + { + is_valid = (std::is_base_of::value) + }; + + static const QMetaObject *get() + { + return qtMetaObject::is_valid, 0>::get(); + } + + private: + template + struct qtMetaObject + { + static inline const QMetaObject *get() { return NULL; } + }; + + template + struct qtMetaObject + { + static inline const QMetaObject *get() { return (&T::staticMetaObject); } + }; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_QT_META_OBJECT_H_ diff --git a/include/QxTraits/qx_traits.h b/include/QxTraits/qx_traits.h new file mode 100644 index 0000000..7978374 --- /dev/null +++ b/include/QxTraits/qx_traits.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_TRAITS_H_ +#define _QX_TRAITS_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _QX_ENABLE_BOOST +#include +#include +#include +#include +#include +#include +#endif // _QX_ENABLE_BOOST + +#endif // _QX_TRAITS_H_ diff --git a/include/QxTraits/remove_attr.h b/include/QxTraits/remove_attr.h new file mode 100644 index 0000000..818e198 --- /dev/null +++ b/include/QxTraits/remove_attr.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_REMOVE_ATTR_H_ +#define _QX_REMOVE_ATTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file remove_attr.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::remove_attr::type : return a type without pointer, const, reference and/or volatile attributes + */ + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::remove_attr::type : return a type without pointer, const, reference and/or volatile attributes + */ + template + class remove_attr + { + + private: + typedef typename std::conditional::type, T>::type type_1; + typedef typename std::conditional::type, type_1>::type type_2; + typedef typename std::conditional::type, type_2>::type type_3; + typedef typename std::conditional::type, type_3>::type type_4; + + public: + typedef type_4 type; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_REMOVE_ATTR_H_ diff --git a/include/QxTraits/remove_smart_ptr.h b/include/QxTraits/remove_smart_ptr.h new file mode 100644 index 0000000..dd2ff34 --- /dev/null +++ b/include/QxTraits/remove_smart_ptr.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_REMOVE_SMART_PTR_H_ +#define _QX_REMOVE_SMART_PTR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file remove_smart_ptr.h + * \author XDL Team + * \ingroup QxTraits + * \brief qx::trait::remove_smart_ptr::type : return a type without smart-pointer attribute from boost, Qt or QxOrm library + */ + +#include + +#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) +#include +#endif // (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) + +#include + +namespace qx +{ + namespace trait + { + + /*! + * \ingroup QxTraits + * \brief qx::trait::remove_smart_ptr::type : return a type without smart-pointer attribute from boost, Qt or QxOrm library + */ + template + struct remove_smart_ptr + { + typedef T type; + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct remove_smart_ptr> + { + typedef T type; + }; + + template + struct remove_smart_ptr> + { + typedef T type; + }; + + template + struct remove_smart_ptr> + { + typedef T type; + }; + +#endif // _QX_ENABLE_BOOST + + template + struct remove_smart_ptr> + { + typedef T type; + }; + +#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) + template + struct remove_smart_ptr> + { + typedef T type; + }; +#endif // (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) + + template + struct remove_smart_ptr> + { + typedef T type; + }; + + template + struct remove_smart_ptr> + { + typedef T type; + }; + + template + struct remove_smart_ptr> + { + typedef T type; + }; + + } // namespace trait +} // namespace qx + +#endif // _QX_REMOVE_SMART_PTR_H_ diff --git a/include/QxValidator/IxValidator.h b/include/QxValidator/IxValidator.h new file mode 100644 index 0000000..606a78a --- /dev/null +++ b/include/QxValidator/IxValidator.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_VALIDATOR_H_ +#define _IX_VALIDATOR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxValidator.h + * \author XDL Team + * \ingroup QxValidator + * \brief Common interface for validator engine + */ + +#include + +namespace qx +{ + + class IxDataMember; + class QxInvalidValueX; + + /*! + * \ingroup QxValidator + * \brief qx::IxValidator : common interface for validator engine + * + * For more informations about QxValidator module, goto the FAQ of QxOrm website : + * https://www.qxorm.com/qxorm_en/faq.html#faq_250 + */ + class QX_DLL_EXPORT IxValidator : public QxPropertyBag + { + + public: + enum validator_type + { + not_null, + not_empty, + min_value, + max_value, + min_length, + max_length, + date_past, + date_future, + min_decimal, + max_decimal, + regular_expression, + e_mail, + recursive_validator, + custom_validator + }; + + protected: + validator_type m_type; //!< Validator type + QString m_message; //!< Validator message when invalid value is detected + QString m_group; //!< Validator group + QVariantList m_Constraints; //!< List of constraints to verify + IxDataMember *m_pDataMember; //!< Registered property associated to validator + + public: + IxValidator(validator_type type); + virtual ~IxValidator(); + + validator_type getType() const; + QString getMessage() const; + QString getGroup() const; + QVariant getConstraint() const; + QVariantList getConstraints() const; + IxDataMember *getDataMember() const; + + void setMessage(const QString &s); + void setGroup(const QString &s); + void setConstraint(const QVariant &v); + void setConstraints(const QVariantList &lst); + void setDataMember(IxDataMember *p); + + virtual void validate(void *pOwner, QxInvalidValueX &lstInvalidValues) const; + + protected: + void initDefaultMessage(); + + void validateNotNull(const QVariant &v, QxInvalidValueX &lstInvalidValues) const; + void validateNotEmpty(const QVariant &v, QxInvalidValueX &lstInvalidValues) const; + void validateMinValue(const QVariant &v, QxInvalidValueX &lstInvalidValues) const; + void validateMaxValue(const QVariant &v, QxInvalidValueX &lstInvalidValues) const; + void validateMinDecimal(const QVariant &v, QxInvalidValueX &lstInvalidValues) const; + void validateMaxDecimal(const QVariant &v, QxInvalidValueX &lstInvalidValues) const; + void validateMinLength(const QVariant &v, QxInvalidValueX &lstInvalidValues) const; + void validateMaxLength(const QVariant &v, QxInvalidValueX &lstInvalidValues) const; + void validateDatePast(const QVariant &v, QxInvalidValueX &lstInvalidValues) const; + void validateDateFuture(const QVariant &v, QxInvalidValueX &lstInvalidValues) const; + void validateRegularExpression(const QVariant &v, QxInvalidValueX &lstInvalidValues) const; + void validateEMail(const QVariant &v, QxInvalidValueX &lstInvalidValues) const; + }; + + typedef std::shared_ptr IxValidator_ptr; + +} // namespace qx + +#endif // _IX_VALIDATOR_H_ diff --git a/include/QxValidator/IxValidatorX.h b/include/QxValidator/IxValidatorX.h new file mode 100644 index 0000000..968687e --- /dev/null +++ b/include/QxValidator/IxValidatorX.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _IX_VALIDATOR_X_H_ +#define _IX_VALIDATOR_X_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file IxValidatorX.h + * \author XDL Team + * \ingroup QxValidator + * \brief Common interface for a list of validators + */ + +#include + +#include + +namespace qx +{ + + class IxClass; + class IxDataMember; + class QxInvalidValueX; + + /*! + * \ingroup QxValidator + * \brief qx::IxValidatorX : common interface for a list of validators + * + * For more informations about QxValidator module, goto the FAQ of QxOrm website : + * https://www.qxorm.com/qxorm_en/faq.html#faq_250 + */ + class QX_DLL_EXPORT IxValidatorX + { + + friend class IxClass; + + protected: + typedef QList type_lst_validator; + typedef std::shared_ptr type_lst_validator_ptr; + typedef QxCollection type_lst_validator_ptr_by_group; + + type_lst_validator_ptr_by_group m_lstValidatorByGroup; //!< List of validator by group + IxClass *m_pClass; //!< Class registered into QxOrm context + + public: + IxValidatorX(); + virtual ~IxValidatorX() = 0; + + QxInvalidValueX validate(void *pOwner, const QString &sGroup = QString()) const; + + IxValidator *add_NotNull(const QString &sPropertyKey, const QString &sMessage = QString(), const QString &sGroup = QString()); + IxValidator *add_NotEmpty(const QString &sPropertyKey, const QString &sMessage = QString(), const QString &sGroup = QString()); + IxValidator *add_MinValue(const QString &sPropertyKey, long lMinValue, const QString &sMessage = QString(), const QString &sGroup = QString()); + IxValidator *add_MaxValue(const QString &sPropertyKey, long lMaxValue, const QString &sMessage = QString(), const QString &sGroup = QString()); + IxValidator *add_Range(const QString &sPropertyKey, long lMinValue, long lMaxValue, const QString &sMessage = QString(), const QString &sGroup = QString()); + IxValidator *add_MinDecimal(const QString &sPropertyKey, double dMinValue, const QString &sMessage = QString(), const QString &sGroup = QString()); + IxValidator *add_MaxDecimal(const QString &sPropertyKey, double dMaxValue, const QString &sMessage = QString(), const QString &sGroup = QString()); + IxValidator *add_RangeDecimal(const QString &sPropertyKey, double dMinValue, double dMaxValue, const QString &sMessage = QString(), const QString &sGroup = QString()); + IxValidator *add_MinLength(const QString &sPropertyKey, long lMinLength, const QString &sMessage = QString(), const QString &sGroup = QString()); + IxValidator *add_MaxLength(const QString &sPropertyKey, long lMaxLength, const QString &sMessage = QString(), const QString &sGroup = QString()); + IxValidator *add_Size(const QString &sPropertyKey, long lMinLength, long lMaxLength, const QString &sMessage = QString(), const QString &sGroup = QString()); + IxValidator *add_DatePast(const QString &sPropertyKey, const QString &sMessage = QString(), const QString &sGroup = QString()); + IxValidator *add_DateFuture(const QString &sPropertyKey, const QString &sMessage = QString(), const QString &sGroup = QString()); + IxValidator *add_RegExp(const QString &sPropertyKey, const QString &sPattern, const QString &sMessage = QString(), const QString &sGroup = QString()); + IxValidator *add_EMail(const QString &sPropertyKey, const QString &sMessage = QString(), const QString &sGroup = QString()); + + QStringList getAllGroup() const; + QList getAllValidatorByGroup(const QString &group) const; + + protected: + void setClass(IxClass *p); + void insertIntoGroup(IxValidator_ptr pValidator, const QString &sGroup); + IxValidator_ptr createValidator(IxValidator::validator_type type, const QString &sPropertyKey, const QString &sMessage, const QString &sGroup); + IxDataMember *getDataMember(const QString &sPropertyKey) const; + }; + + typedef std::shared_ptr IxValidatorX_ptr; + +} // namespace qx + +#endif // _IX_VALIDATOR_X_H_ diff --git a/include/QxValidator/QxInvalidValue.h b/include/QxValidator/QxInvalidValue.h new file mode 100644 index 0000000..b635f65 --- /dev/null +++ b/include/QxValidator/QxInvalidValue.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_VALIDATOR_INVALID_VALUE_H_ +#define _QX_VALIDATOR_INVALID_VALUE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxInvalidValue.h + * \author XDL Team + * \ingroup QxValidator + * \brief Invalid value when a property fails to pass a constraint + */ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#include +#include +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifndef _QX_NO_JSON +#include +#endif // _QX_NO_JSON + +#include + +#include +#include +#include + +#include + +#include + +#include + +namespace qx +{ + class IxValidator; + class QxInvalidValue; +} // namespace qx + +QX_DLL_EXPORT QDataStream &operator<<(QDataStream &stream, const qx::QxInvalidValue &t) QX_USED; +QX_DLL_EXPORT QDataStream &operator>>(QDataStream &stream, qx::QxInvalidValue &t) QX_USED; + +#ifndef _QX_NO_JSON +namespace qx +{ + namespace cvt + { + namespace detail + { + template <> + struct QxConvert_ToJson; + template <> + struct QxConvert_FromJson; + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const qx::QxInvalidValue &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, qx::QxInvalidValue &t, const QString &format) QX_USED; + } // namespace detail + } // namespace cvt +} // namespace qx +#endif // _QX_NO_JSON + +namespace qx +{ + + /*! + * \ingroup QxValidator + * \brief qx::QxInvalidValue : invalid value when a property fails to pass a constraint + * + * For more informations about QxValidator module, goto the FAQ of QxOrm website : + * https://www.qxorm.com/qxorm_en/faq.html#faq_250 + */ + class QX_DLL_EXPORT QxInvalidValue : public QxPropertyBag + { + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + friend class boost::serialization::access; +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + friend QX_DLL_EXPORT QDataStream & ::operator<<(QDataStream &stream, const qx::QxInvalidValue &t); + friend QX_DLL_EXPORT QDataStream & ::operator>>(QDataStream &stream, qx::QxInvalidValue &t); + +#ifndef _QX_NO_JSON + friend struct qx::cvt::detail::QxConvert_ToJson; + friend struct qx::cvt::detail::QxConvert_FromJson; + friend QX_DLL_EXPORT QJsonValue qx::cvt::detail::QxConvert_ToJson_Helper(const qx::QxInvalidValue &t, const QString &format); + friend QX_DLL_EXPORT qx_bool qx::cvt::detail::QxConvert_FromJson_Helper(const QJsonValue &j, qx::QxInvalidValue &t, const QString &format); +#endif // _QX_NO_JSON + + protected: + QString m_sMessage; //!< Message associated to the invalid value + QString m_sPropertyName; //!< Property name failing to pass the constraint + QString m_sPath; //!< Path of property failing to pass the constraint + const IxValidator *m_pValidator; //!< IxValidator class associated to the invalid value + + public: + QxInvalidValue(); + virtual ~QxInvalidValue(); + + QString getMessage() const { return m_sMessage; } + QString getPropertyName() const { return m_sPropertyName; } + QString getPath() const { return m_sPath; } + QString getFullName() const; + const IxValidator *getValidator() const; + + void setMessage(const QString &s) { m_sMessage = s; } + void setPropertyName(const QString &s) { m_sPropertyName = s; } + void setPath(const QString &s) { m_sPath = s; } + void setValidator(const IxValidator *p); + + private: +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + template + void serialize(Archive &ar, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("message", m_sMessage); + ar &boost::serialization::make_nvp("property_name", m_sPropertyName); + ar &boost::serialization::make_nvp("path", m_sPath); + ar &boost::serialization::make_nvp("list_property_bag", this->m_lstPropertyBag); + } +#endif // _QX_ENABLE_BOOST_SERIALIZATION + }; + +} // namespace qx + +QX_REGISTER_CLASS_NAME(qx::QxInvalidValue) + +#endif // _QX_VALIDATOR_INVALID_VALUE_H_ diff --git a/include/QxValidator/QxInvalidValueX.h b/include/QxValidator/QxInvalidValueX.h new file mode 100644 index 0000000..de5028a --- /dev/null +++ b/include/QxValidator/QxInvalidValueX.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_VALIDATOR_INVALID_VALUE_X_H_ +#define _QX_VALIDATOR_INVALID_VALUE_X_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxInvalidValueX.h + * \author XDL Team + * \ingroup QxValidator + * \brief List of invalid values + */ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#include +#include +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#ifndef _QX_NO_JSON +#include +#endif // _QX_NO_JSON + +#include +#include + +#include + +#include + +#include + +namespace qx +{ + class QxInvalidValueX; +} // namespace qx + +QX_DLL_EXPORT QDataStream &operator<<(QDataStream &stream, const qx::QxInvalidValueX &t) QX_USED; +QX_DLL_EXPORT QDataStream &operator>>(QDataStream &stream, qx::QxInvalidValueX &t) QX_USED; + +#ifndef _QX_NO_JSON +namespace qx +{ + namespace cvt + { + namespace detail + { + template <> + struct QxConvert_ToJson; + template <> + struct QxConvert_FromJson; + QX_DLL_EXPORT QJsonValue QxConvert_ToJson_Helper(const qx::QxInvalidValueX &t, const QString &format) QX_USED; + QX_DLL_EXPORT qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, qx::QxInvalidValueX &t, const QString &format) QX_USED; + } // namespace detail + } // namespace cvt +} // namespace qx +#endif // _QX_NO_JSON + +namespace qx +{ + + /*! + * \ingroup QxValidator + * \brief qx::QxInvalidValueX : list of invalid values + * + * For more informations about QxValidator module, goto the FAQ of QxOrm website : + * https://www.qxorm.com/qxorm_en/faq.html#faq_250 + */ + class QX_DLL_EXPORT QxInvalidValueX + { + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + friend class boost::serialization::access; +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + friend QX_DLL_EXPORT QDataStream & ::operator<<(QDataStream &stream, const qx::QxInvalidValueX &t); + friend QX_DLL_EXPORT QDataStream & ::operator>>(QDataStream &stream, qx::QxInvalidValueX &t); + +#ifndef _QX_NO_JSON + friend struct qx::cvt::detail::QxConvert_ToJson; + friend struct qx::cvt::detail::QxConvert_FromJson; + friend QX_DLL_EXPORT QJsonValue qx::cvt::detail::QxConvert_ToJson_Helper(const qx::QxInvalidValueX &t, const QString &format); + friend QX_DLL_EXPORT qx_bool qx::cvt::detail::QxConvert_FromJson_Helper(const QJsonValue &j, qx::QxInvalidValueX &t, const QString &format); +#endif // _QX_NO_JSON + + protected: + QList m_lstInvalidValues; //!< List of invalid values + QString m_sCurrentPath; //!< Current path of validation process + + public: + QxInvalidValueX(); + virtual ~QxInvalidValueX(); + + QString getCurrentPath() const; + void setCurrentPath(const QString &s); + + long count() const; + QxInvalidValue at(long l) const; + void insert(const IxValidator *pValidator); + void insert(const QString &sMessage); + void insert(const QxInvalidValue &invalidValue); + void insert(const QxInvalidValueX &other); + + QString text() const; + void dump() const; + + inline operator bool() const + { + return (m_lstInvalidValues.count() == 0); + } + + private: +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + template + void serialize(Archive &ar, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("list_invalid_values", m_lstInvalidValues); + ar &boost::serialization::make_nvp("current_path", m_sCurrentPath); + } +#endif // _QX_ENABLE_BOOST_SERIALIZATION + }; + +} // namespace qx + +QX_REGISTER_CLASS_NAME(qx::QxInvalidValueX) + +#endif // _QX_VALIDATOR_INVALID_VALUE_X_H_ diff --git a/include/QxValidator/QxValidator.h b/include/QxValidator/QxValidator.h new file mode 100644 index 0000000..6a93fbd --- /dev/null +++ b/include/QxValidator/QxValidator.h @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_VALIDATOR_H_ +#define _QX_VALIDATOR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxValidator.h + * \author XDL Team + * \ingroup QxValidator + * \brief Concrete class for a custom or recursive validator + */ + +#include +#include + +#include + +namespace qx +{ + + template + QxInvalidValueX validate(T &t, const QString &group); + + /*! + * \ingroup QxValidator + * \brief qx::QxValidator : concrete class for a custom validator + * + * For more informations about QxValidator module, goto the FAQ of QxOrm website : + * https://www.qxorm.com/qxorm_en/faq.html#faq_250 + */ + template + class QxValidator : public IxValidator + { + + public: + typedef std::function type_fct_custom_validator_member; + typedef std::function type_fct_custom_validator_variant; + typedef std::function type_fct_custom_validator_variant_validator; + + protected: + type_fct_custom_validator_member m_fctCustomValidator_Member; //!< Custom validator function : class method + type_fct_custom_validator_variant m_fctCustomValidator_Variant; //!< Custom validator function : global function with value converted to QVariant type + type_fct_custom_validator_variant_validator m_fctCustomValidator_VariantValidator; //!< Custom validator function : global function with value converted to QVariant type and a IxValidator pointer containing all parameters + + public: + QxValidator() : IxValidator(IxValidator::custom_validator) { ; } + virtual ~QxValidator() { ; } + + void setFunction(type_fct_custom_validator_member fct) { m_fctCustomValidator_Member = fct; } + void setFunction(type_fct_custom_validator_variant fct) { m_fctCustomValidator_Variant = fct; } + void setFunction(type_fct_custom_validator_variant_validator fct) { m_fctCustomValidator_VariantValidator = fct; } + + virtual void validate(void *pOwner, QxInvalidValueX &lstInvalidValues) const + { + if (m_fctCustomValidator_Member) + { + m_fctCustomValidator_Member(static_cast(pOwner), lstInvalidValues); + } + else if (m_fctCustomValidator_Variant && m_pDataMember) + { + m_fctCustomValidator_Variant(m_pDataMember->toVariant(pOwner), lstInvalidValues); + } + else if (m_fctCustomValidator_VariantValidator && m_pDataMember) + { + m_fctCustomValidator_VariantValidator(m_pDataMember->toVariant(pOwner), this, lstInvalidValues); + } + } + }; + + /*! + * \ingroup QxValidator + * \brief qx::QxValidator_WithDataType : concrete class for a custom validator with data type + * + * For more informations about QxValidator module, goto the FAQ of QxOrm website : + * https://www.qxorm.com/qxorm_en/faq.html#faq_250 + */ + template + class QxValidator_WithDataType : public IxValidator + { + + public: + typedef std::function type_fct_custom_validator_data_type; + typedef std::function type_fct_custom_validator_data_type_validator; + + protected: + type_fct_custom_validator_data_type m_fctCustomValidator_DataType; //!< Custom validator function : global function with value + type_fct_custom_validator_data_type_validator m_fctCustomValidator_DataTypeValidator; //!< Custom validator function : global function with value and a IxValidator pointer containing all parameters + + public: + QxValidator_WithDataType() : IxValidator(IxValidator::custom_validator) { ; } + virtual ~QxValidator_WithDataType() { ; } + + void setFunction(type_fct_custom_validator_data_type fct) { m_fctCustomValidator_DataType = fct; } + void setFunction(type_fct_custom_validator_data_type_validator fct) { m_fctCustomValidator_DataTypeValidator = fct; } + + virtual void validate(void *pOwner, QxInvalidValueX &lstInvalidValues) const + { + if (!m_pDataMember) + { + return; + } + IxDataMember *pDataMember = const_cast(m_pDataMember); + DataType *val = pDataMember->getValuePtr(pOwner); + if (m_fctCustomValidator_DataType && val) + { + m_fctCustomValidator_DataType((*val), lstInvalidValues); + } + else if (m_fctCustomValidator_DataTypeValidator && val) + { + m_fctCustomValidator_DataTypeValidator((*val), this, lstInvalidValues); + } + } + }; + + /*! + * \ingroup QxValidator + * \brief qx::QxValidator_Recursive : concrete class for a recursive validator + * + * For more informations about QxValidator module, goto the FAQ of QxOrm website : + * https://www.qxorm.com/qxorm_en/faq.html#faq_250 + */ + template + class QxValidator_Recursive : public IxValidator + { + + public: + QxValidator_Recursive() : IxValidator(IxValidator::recursive_validator) { ; } + virtual ~QxValidator_Recursive() { ; } + + virtual void validate(void *pOwner, QxInvalidValueX &lstInvalidValues) const + { + if (!m_pDataMember) + { + qAssert(false); + return; + } + IxDataMember *pDataMember = const_cast(m_pDataMember); + DataType *val = pDataMember->getValuePtr(pOwner); + if (!val) + { + qAssert(false); + return; + } + QxInvalidValueX invalidValues; + invalidValues.setCurrentPath(m_pDataMember->getName()); + invalidValues.insert(qx::validate((*val), m_group)); + lstInvalidValues.insert(invalidValues); + } + }; + +} // namespace qx + +#endif // _QX_VALIDATOR_H_ diff --git a/include/QxValidator/QxValidatorError.h b/include/QxValidator/QxValidatorError.h new file mode 100644 index 0000000..6b7228a --- /dev/null +++ b/include/QxValidator/QxValidatorError.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_VALIDATOR_ERROR_H_ +#define _QX_VALIDATOR_ERROR_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxValidatorError.h + * \author XDL Team + * \ingroup QxValidator + * \brief Define a validator error exception (for example, inserting or updating an element into database) and retrieve list of invalid values + */ + +#include +#include + +#include + +namespace qx +{ + + /*! + * \ingroup QxValidator + * \brief qx::validator_error : define a validator error exception (for example, inserting or updating an element into database) and retrieve list of invalid values + * + * For more informations about QxValidator module, goto the FAQ of QxOrm website : + * https://www.qxorm.com/qxorm_en/faq.html#faq_250 + */ + class validator_error : public std::exception + { + + private: + QxInvalidValueX m_lstInvalidValues; + + public: + validator_error(const QxInvalidValueX &err) : std::exception(), m_lstInvalidValues(err) { ; } + virtual ~validator_error() throw() { ; } + + virtual const char *what() const throw() + { + return qPrintable(m_lstInvalidValues.text()); + } + + QxInvalidValueX get() const + { + return m_lstInvalidValues; + } + }; + +} // namespace qx + +#endif // _QX_VALIDATOR_ERROR_H_ diff --git a/include/QxValidator/QxValidatorFct.h b/include/QxValidator/QxValidatorFct.h new file mode 100644 index 0000000..e605a2f --- /dev/null +++ b/include/QxValidator/QxValidatorFct.h @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_VALIDATOR_FUNCTION_H_ +#define _QX_VALIDATOR_FUNCTION_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxValidatorFct.h + * \author XDL Team + * \ingroup QxValidator + * \brief Implementation of qx::validate() function (validator engine) + */ + +#include +#include + +#include + +#include +#include +#include + +namespace qx +{ + template + QxInvalidValueX validate(T &t, const QString &group); +} // namespace qx + +namespace qx +{ + namespace validator + { + namespace detail + { + + template + struct QxValidator_Helper_Generic + { + + static inline qx::QxInvalidValueX validate(T &t, const QString &group) + { + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + + qx::QxInvalidValueX invalidValues; + qx::IxClass *pClass = qx::QxClass::getSingleton(); + if (!pClass) + { + qAssert(false); + return invalidValues; + } + qx::IxValidatorX *pAllValidator = pClass->getAllValidator(); + if (!pAllValidator) + { + return invalidValues; + } + invalidValues.setCurrentPath(pClass->getName()); + invalidValues.insert(pAllValidator->validate((&t), group)); + return invalidValues; + } + }; + + template + struct QxValidator_Helper_Container + { + + static inline qx::QxInvalidValueX validate(T &t, const QString &group) + { + qx::QxInvalidValueX invalidValues; + long lIndex = 0; + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + invalidValues.setCurrentPath("[" + QString::number(lIndex) + "]"); + invalidValues.insert(validateItem((*it), group)); + lIndex++; + } + return invalidValues; + } + + private: + template + static inline qx::QxInvalidValueX validateItem(U &item, const QString &group) + { + return validateItem_Helper < U, std::is_pointer::value || qx::trait::is_smart_ptr::value > ::validate(item, group); + } + + template + struct validateItem_Helper + { + static inline qx::QxInvalidValueX validate(U &item, const QString &group) + { + return (item ? qx::validator::detail::QxValidator_Helper_Container::validateItem((*item), group) : qx::QxInvalidValueX()); + } + }; + + template + struct validateItem_Helper, false> + { + static inline qx::QxInvalidValueX validate(std::pair &item, const QString &group) + { + return qx::validator::detail::QxValidator_Helper_Container::validateItem(item.second, group); + } + }; + + template + struct validateItem_Helper, false> + { + static inline qx::QxInvalidValueX validate(const std::pair &item, const QString &group) + { + return qx::validator::detail::QxValidator_Helper_Container::validateItem(item.second, group); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct validateItem_Helper, false> + { + static inline qx::QxInvalidValueX validate(QPair &item, const QString &group) + { + return qx::validator::detail::QxValidator_Helper_Container::validateItem(item.second, group); + } + }; + + template + struct validateItem_Helper, false> + { + static inline qx::QxInvalidValueX validate(const QPair &item, const QString &group) + { + return qx::validator::detail::QxValidator_Helper_Container::validateItem(item.second, group); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct validateItem_Helper + { + static qx::QxInvalidValueX validate(U &item, const QString &group) { return qx::validate(item, group); } + }; + }; + + template + struct QxValidator_Helper_Ptr + { + + static inline qx::QxInvalidValueX validate(T &t, const QString &group) + { + return (t ? qx::validate((*t), group) : qx::QxInvalidValueX()); + } + }; + + template + struct QxValidator_Helper + { + + static inline qx::QxInvalidValueX validate(T &t, const QString &group) + { + typedef typename std::conditional::value, qx::validator::detail::QxValidator_Helper_Ptr, qx::validator::detail::QxValidator_Helper_Generic>::type type_validator_1; + typedef typename std::conditional::value, qx::validator::detail::QxValidator_Helper_Ptr, type_validator_1>::type type_validator_2; + typedef typename std::conditional::value, qx::validator::detail::QxValidator_Helper_Container, type_validator_2>::type type_validator_3; + + return type_validator_3::validate(t, group); + } + }; + + } // namespace detail + } // namespace validator +} // namespace qx + +namespace qx +{ + + template + QxInvalidValueX validate(T &t, const QString &group) + { + return qx::validator::detail::QxValidator_Helper::validate(t, group); + } + + template + QxInvalidValueX validate(T &t) + { + return qx::validator::detail::QxValidator_Helper::validate(t, ""); + } + + template + QxInvalidValueX validate(T &t, const QStringList &groups) + { + QxInvalidValueX invalidValues; + if (groups.count() <= 0) + { + return qx::validate(t); + } + for (long l = 0; l < groups.count(); l++) + { + invalidValues.insert(qx::validate(t, groups.at(l))); + } + return invalidValues; + } + +} // namespace qx + +#endif // _QX_VALIDATOR_FUNCTION_H_ diff --git a/include/QxValidator/QxValidatorX.h b/include/QxValidator/QxValidatorX.h new file mode 100644 index 0000000..56df29d --- /dev/null +++ b/include/QxValidator/QxValidatorX.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_VALIDATOR_X_H_ +#define _QX_VALIDATOR_X_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file QxValidatorX.h + * \author XDL Team + * \ingroup QxValidator + * \brief Concrete class for a list of validators associated to a type registered into QxOrm context + */ + +#include +#include + +#include + +namespace qx +{ + + /*! + * \ingroup QxValidator + * \brief qx::QxValidatorX : concrete class for a list of validators associated to a type registered into QxOrm context + * + * For more informations about QxValidator module, goto the FAQ of QxOrm website : + * https://www.qxorm.com/qxorm_en/faq.html#faq_250 + */ + template + class QxValidatorX : public IxValidatorX + { + + public: + QxValidatorX() : IxValidatorX() { ; } + virtual ~QxValidatorX() { ; } + + template + IxValidator *add_RecursiveValidator(const QString &sPropertyKey, const QString &sGroup = QString()) + { + IxValidator_ptr pValidator = std::make_shared>(); + pValidator->setGroup(sGroup); + pValidator->setDataMember(getDataMember(sPropertyKey)); + insertIntoGroup(pValidator, sGroup); + return pValidator.get(); + } + + IxValidator *add_CustomValidator(typename QxValidator::type_fct_custom_validator_member fct, const QString &sGroup = QString()) + { + return add_CustomValidator_Helper(new QxValidator(), fct, "", sGroup); + } + + IxValidator *add_CustomValidator_QVariant(typename QxValidator::type_fct_custom_validator_variant_validator fct, const QString &sPropertyKey, const QString &sGroup = QString()) + { + return add_CustomValidator_Helper(new QxValidator(), fct, sPropertyKey, sGroup); + } + + template + IxValidator *add_CustomValidator_DataType(typename QxValidator_WithDataType::type_fct_custom_validator_data_type_validator fct, const QString &sPropertyKey, const QString &sGroup = QString()) + { + return add_CustomValidator_Helper(new QxValidator_WithDataType(), fct, sPropertyKey, sGroup); + } + + private: + template + IxValidator *add_CustomValidator_Helper(Validator *validator, FunctionType fct, const QString &sPropertyKey, const QString &sGroup) + { + if (!validator) + { + qAssert(false); + return NULL; + } + IxValidator_ptr pValidator; + pValidator.reset(validator); + validator->setGroup(sGroup); + validator->setFunction(fct); + validator->setDataMember(getDataMember(sPropertyKey)); + insertIntoGroup(pValidator, sGroup); + return pValidator.get(); + } + }; + +} // namespace qx + +#endif // _QX_VALIDATOR_X_H_ diff --git a/include/QxXml/QxXml.h b/include/QxXml/QxXml.h new file mode 100644 index 0000000..44bed13 --- /dev/null +++ b/include/QxXml/QxXml.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_XML_H_ +#define _QX_XML_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include + +#include + +namespace qx +{ + + template + class QxXml + { + + private: + QxXml() { ; } + virtual ~QxXml() { ; } + + public: + static QString toXml(T *pOwner); + static bool fromXml(T *pOwner, const QString &sXml); + + static std::shared_ptr toXmlWriter(T *pOwner); + static bool fromXmlReader(T *pOwner, QxXmlReader *pXmlReader); + }; + +#include "../../inl/QxXml/QxXml.inl" + +} // namespace qx + +#endif // _QX_XML_H_ diff --git a/include/QxXml/QxXmlReader.h b/include/QxXml/QxXmlReader.h new file mode 100644 index 0000000..21b91c9 --- /dev/null +++ b/include/QxXml/QxXmlReader.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_XML_READER_H_ +#define _QX_XML_READER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#if (QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)) +#include +#else // (QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)) +#include +#endif // (QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)) + +#include + +namespace qx +{ + + class QX_DLL_EXPORT QxXmlReader : public QXmlStreamReader + { + + public: + typedef std::shared_ptr type_byte_arr_ptr; + typedef QHash type_hash_bin_data; + typedef QHashIterator type_hash_bin_data_itr; + + protected: + type_hash_bin_data m_mapBinaryData; // Collection of binary data associated with xml (<=> attached files) + + public: + QxXmlReader() : QXmlStreamReader() { ; } + QxXmlReader(const QString &data) : QXmlStreamReader(data) { ; } + virtual ~QxXmlReader() { ; } + + void addBinaryData(const type_hash_bin_data &other, bool bClear); + void addBinaryData(const QString &sKey, type_byte_arr_ptr pData); + void removeBinaryData(const QString &sKey); + void removeAllBinaryData(); + + bool isStartBinaryData() const; + type_byte_arr_ptr readBinaryData(); + }; + +} // namespace qx + +QX_DLL_EXPORT QDataStream &operator>>(QDataStream &stream, qx::QxXmlReader &xmlReader); + +#endif // _QX_XML_READER_H_ diff --git a/include/QxXml/QxXmlWriter.h b/include/QxXml/QxXmlWriter.h new file mode 100644 index 0000000..83b8cf4 --- /dev/null +++ b/include/QxXml/QxXmlWriter.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_XML_WRITER_H_ +#define _QX_XML_WRITER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#if (QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)) +#include +#else // (QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)) +#include +#endif // (QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)) + +#define QX_XML_PREFIX_BINARY_DATA_KEY "qx_binary_data_key_" +#define QX_XML_ATTRIBUTE_IS_BINARY_DATA "qx_is_binary_data" + +namespace qx +{ + + class QX_DLL_EXPORT QxXmlWriter : public QXmlStreamWriter + { + + public: + typedef std::shared_ptr type_byte_arr_ptr; + typedef QHash type_hash_bin_data; + typedef QHashIterator type_hash_bin_data_itr; + + protected: + type_hash_bin_data m_mapBinaryData; // Collection of binary data associated with xml (<=> attached files) + long m_lKeyBinaryData; // Current binary data key to write to xml + QString m_pStringWriter; // String containing xml to write + + public: + QxXmlWriter() : QXmlStreamWriter(&m_pStringWriter), m_lKeyBinaryData(0) { initQxXmlWriter(); } + virtual ~QxXmlWriter() { ; } + + QString getXml() const { return m_pStringWriter; } + type_hash_bin_data_itr getBinaryDataItr() const { return type_hash_bin_data_itr(m_mapBinaryData); } + long getBinaryDataCount() const { return m_mapBinaryData.count(); } + + QString writeBinaryData(const QString &namespaceUri, const QString &name, type_byte_arr_ptr pData); + QString writeBinaryData(const QString &qualifiedName, type_byte_arr_ptr pData); + + protected: + inline QString getNextKeyBinaryData() { return (QString(QX_XML_PREFIX_BINARY_DATA_KEY) + QString::number(++m_lKeyBinaryData)); } + inline void initQxXmlWriter() + { + setAutoFormatting(true); + setAutoFormattingIndent(3); + } + }; + +} // namespace qx + +QX_DLL_EXPORT QDataStream &operator<<(QDataStream &stream, const qx::QxXmlWriter &xmlWriter); + +#endif // _QX_XML_WRITER_H_ diff --git a/inl/QxCollection/QxCollection.inl b/inl/QxCollection/QxCollection.inl new file mode 100644 index 0000000..2b4c1b6 --- /dev/null +++ b/inl/QxCollection/QxCollection.inl @@ -0,0 +1,615 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + + template + QxCollection::QxCollection() : IxCollection(), m_batch(false) + { + } + + template + QxCollection::QxCollection(const QxCollection &other) : IxCollection(), m_batch(false) + { + cloneCollection(this, other); + } + + template + QxCollection::~QxCollection() + { + } + + template + QxCollection &QxCollection::operator=(const QxCollection &other) + { + if (this != (&other)) + { + cloneCollection(this, other); + } + return (*this); + } + + template + bool QxCollection::operator==(const QxCollection &other) const + { + return isSameCollection(this, other); + } + + template + bool QxCollection::operator!=(const QxCollection &other) const + { + return (!isSameCollection(this, other)); + } + + template + void QxCollection::cloneCollection(QxCollection *pClone, const QxCollection &pRef) + { + if (!pClone) + { + return; + } + if (pClone == (&pRef)) + { + return; + } + QMutexLocker locker1(&pRef.m_mutex); + QMutexLocker locker2(&pClone->m_mutex); + qAssert(pRef.m_list.size() == pRef.m_hash.size()); + pClone->m_list = pRef.m_list; + pClone->m_hash = pRef.m_hash; + } + + template + bool QxCollection::isSameCollection(const QxCollection *p1, const QxCollection &p2) const + { + if (!p1) + { + return false; + } + if (p1 == (&p2)) + { + return true; + } + if (p1->size() != p2.size()) + { + return false; + } + QMutexLocker locker1(&p2.m_mutex); + QMutexLocker locker2(&p1->m_mutex); + qAssert(p2.m_list.size() == p2.m_hash.size()); + return ((p1->m_list == p2.m_list) && (p1->m_hash == p2.m_hash)); + } + + template + void QxCollection::updateHashPosition(long from /* = 0 */, long to /* = -1 */, bool check /* = false */) + { + if (m_batch) + { + return; + } + QMutexLocker locker(&m_mutex); + qAssert(m_list.size() == m_hash.size()); + if (to == -1) + { + to = (m_list.size() - 1); + } + if ((from < 0) || (to >= m_list.size()) || (from > to)) + { + return; + } + for (long idx = from; idx <= to; idx++) + { + const Key &key = m_list.at(idx).first; + m_hash.insert(key, idx); + } + if (check) + { + qAssert(m_list.size() == m_hash.size()); + } + } + + template + typename QxCollection::iterator QxCollection::begin() + { + QMutexLocker locker(&m_mutex); + return m_list.begin(); + } + + template + typename QxCollection::iterator QxCollection::end() + { + QMutexLocker locker(&m_mutex); + return m_list.end(); + } + + template + typename QxCollection::const_iterator QxCollection::begin() const + { + QMutexLocker locker(&m_mutex); + return m_list.begin(); + } + + template + typename QxCollection::const_iterator QxCollection::end() const + { + QMutexLocker locker(&m_mutex); + return m_list.end(); + } + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + + template + typename QxCollection::reverse_iterator QxCollection::rbegin() + { + QMutexLocker locker(&m_mutex); + return m_list.rbegin(); + } + + template + typename QxCollection::reverse_iterator QxCollection::rend() + { + QMutexLocker locker(&m_mutex); + return m_list.rend(); + } + + template + typename QxCollection::const_reverse_iterator QxCollection::rbegin() const + { + QMutexLocker locker(&m_mutex); + return m_list.rbegin(); + } + + template + typename QxCollection::const_reverse_iterator QxCollection::rend() const + { + QMutexLocker locker(&m_mutex); + return m_list.rend(); + } + +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + + template + void QxCollection::reserve(long size) + { + if (size <= 0) + { + return; + } + QMutexLocker locker(&m_mutex); + qAssert(m_list.size() == m_hash.size()); + m_list.reserve(size); + m_hash.reserve(size); + } + + template + void QxCollection::reverse() + { + { + QMutexLocker locker(&m_mutex); + qAssert(m_list.size() == m_hash.size()); + std::reverse(m_list.begin(), m_list.end()); + } + updateHashPosition(); + } + + template + void QxCollection::clear() + { + QMutexLocker locker(&m_mutex); + m_hash.clear(); + m_list.clear(); + } + + template + long QxCollection::count() const + { + QMutexLocker locker(&m_mutex); + qAssert(m_list.size() == m_hash.size()); + return static_cast(m_list.size()); + } + + template + long QxCollection::size() const + { + return this->count(); + } + + template + bool QxCollection::contains(const Key &key) const + { + QMutexLocker locker(&m_mutex); + qAssert(m_list.size() == m_hash.size()); + return (m_hash.contains(key)); + } + + template + bool QxCollection::exist(const Key &key) const + { + return this->contains(key); + } + + template + bool QxCollection::empty() const + { + QMutexLocker locker(&m_mutex); + qAssert(m_list.size() == m_hash.size()); + return m_list.isEmpty(); + } + + template + bool QxCollection::push_back(const Key &key, const Value &value) + { + return this->insert(key, value); + } + + template + bool QxCollection::push_front(const Key &key, const Value &value) + { + return this->insert(0, key, value); + } + + template + bool QxCollection::insert(const Key &key, const Value &value) + { + qAssert(!exist(key)); + QMutexLocker locker(&m_mutex); + qAssert(m_list.size() == m_hash.size()); + m_list.append(qMakePair(key, value)); + m_hash.insert(key, (m_list.size() - 1)); + return true; + } + + template + bool QxCollection::insert(long index, const Key &key, const Value &value) + { + qAssert(!exist(key)); + if (index < 0) + { + index = 0; + } + if (index >= size()) + { + return this->insert(key, value); + } + + { + QMutexLocker locker(&m_mutex); + qAssert(m_list.size() == m_hash.size()); + m_list.insert(index, qMakePair(key, value)); + m_hash.insert(key, index); + } + + updateHashPosition(index); + return true; + } + + template + bool QxCollection::insert(const QxCollection &other) + { + { + if (this == (&other)) + { + return false; + } + QMutexLocker locker1(&m_mutex); + QMutexLocker locker2(&other.m_mutex); + m_list.append(other.m_list); + m_hash.unite(other.m_hash); + } + + updateHashPosition(0, -1, true); + return true; + } + + template + bool QxCollection::insert(long index, const QxCollection &other) + { + if (index < 0) + { + index = 0; + } + if ((index >= size()) && (index != 0)) + { + index = (size() - 1); + } + if (this == (&other)) + { + return false; + } + QMutexLocker locker1(&other.m_mutex); + + { + QMutexLocker locker2(&m_mutex); + qAssert(m_list.size() == m_hash.size()); + m_batch = true; + } + + for (long l = 0; l < other.size(); l++) + { + const type_pair_key_value &pair = other.m_list.at(l); + this->insert((index + l), pair.first, pair.second); + } + + { + QMutexLocker locker3(&m_mutex); + m_batch = false; + } + updateHashPosition(index); + return true; + } + + template + bool QxCollection::replace(long index, const Key &key, const Value &value) + { + qAssert(!exist(key)); + QMutexLocker locker(&m_mutex); + m_hash.remove(m_list.at(index).first); + m_list.replace(index, qMakePair(key, value)); + m_hash.insert(key, index); + qAssert(m_list.size() == m_hash.size()); + return true; + } + + template + bool QxCollection::swap(long index1, long index2) + { + if (index1 < 0 || index1 >= size()) + { + return false; + } + if (index2 < 0 || index2 >= size()) + { + return false; + } + if (index1 == index2) + { + return true; + } + + QMutexLocker locker(&m_mutex); + const Key &key1 = m_list.at(index1).first; + const Key &key2 = m_list.at(index2).first; + m_hash.insert(key1, index2); + m_hash.insert(key2, index1); + m_list.swap(index1, index2); + qAssert(m_list.size() == m_hash.size()); + return true; + } + + template + bool QxCollection::move(long indexFrom, long indexTo) + { + return swap(indexFrom, indexTo); + } + + template + bool QxCollection::removeByKey(const Key &key) + { + qAssert(exist(key)); + long pos = 0; + + { + QMutexLocker locker(&m_mutex); + pos = m_hash.value(key, -1); + if ((pos < 0) || (pos >= m_list.size())) + { + return false; + } + qAssert(m_list.at(pos).first == key); + m_hash.remove(key); + m_list.removeAt(pos); + } + + updateHashPosition(pos, -1, true); + return true; + } + + template + bool QxCollection::removeByIndex(long index) + { + if (index < 0 || index >= size()) + { + return false; + } + + { + QMutexLocker locker(&m_mutex); + const Key &key = m_list.at(index).first; + qAssert(m_hash.value(key, -1) == index); + m_hash.remove(key); + m_list.removeAt(index); + } + + updateHashPosition(index, -1, true); + return true; + } + + template + bool QxCollection::removeByIndex(long first, long last) + { + if (first < 0 || first >= size()) + { + return false; + } + if (last < 0 || last >= size()) + { + return false; + } + if (first > last) + { + return false; + } + { + QMutexLocker locker(&m_mutex); + m_batch = true; + } + for (long idx = first; idx <= last; idx++) + { + removeByIndex(idx); + } + { + QMutexLocker locker(&m_mutex); + m_batch = false; + } + updateHashPosition(first); + return true; + } + + template + bool QxCollection::removeFirst() + { + return this->removeByIndex(0); + } + + template + bool QxCollection::removeLast() + { + return this->removeByIndex(size() - 1); + } + + template + typename QxCollection::const_reference_value QxCollection::getByKey(const Key &key) const + { + qAssert(exist(key)); + QMutexLocker locker(&m_mutex); + qAssert(m_list.size() == m_hash.size()); + const type_pair_key_value &pair = m_list.at(m_hash.value(key, -1)); + qAssert(pair.first == key); + return pair.second; + } + + template + typename QxCollection::const_reference_value QxCollection::getByIndex(long index) const + { + QMutexLocker locker(&m_mutex); + qAssert(m_list.size() == m_hash.size()); + qAssert((index >= 0) && (index < static_cast(m_list.size()))); + qAssert(m_hash.value(m_list.at(index).first, -1) == index); + return m_list.at(index).second; + } + + template + typename QxCollection::const_reference_value QxCollection::getFirst() const + { + qAssert(!empty()); + QMutexLocker locker(&m_mutex); + qAssert(m_list.size() == m_hash.size()); + return m_list.at(0).second; + } + + template + typename QxCollection::const_reference_value QxCollection::getLast() const + { + qAssert(!empty()); + QMutexLocker locker(&m_mutex); + qAssert(m_list.size() == m_hash.size()); + return m_list.at(m_list.size() - 1).second; + } + + template + typename QxCollection::const_reference_key QxCollection::getKeyByIndex(long index) const + { + QMutexLocker locker(&m_mutex); + qAssert(m_list.size() == m_hash.size()); + qAssert((index >= 0) && (index < static_cast(m_list.size()))); + qAssert(m_hash.value(m_list.at(index).first, -1) == index); + return m_list.at(index).first; + } + + template + void QxCollection::sortByKey(bool bAscending /* = true */) + { + if (bAscending) + { + QMutexLocker locker(&m_mutex); + std::sort(m_list.begin(), m_list.end(), (&compareKeyValue < std::is_pointer::value || qx::trait::is_smart_ptr::value, 0 > ::compareByKeyAscending)); + } + else + { + QMutexLocker locker(&m_mutex); + std::sort(m_list.begin(), m_list.end(), (&compareKeyValue < std::is_pointer::value || qx::trait::is_smart_ptr::value, 0 > ::compareByKeyDescending)); + } + updateHashPosition(0, -1, true); + } + + template + void QxCollection::sortByValue(bool bAscending /* = true */) + { + if (bAscending) + { + QMutexLocker locker(&m_mutex); + std::sort(m_list.begin(), m_list.end(), (&compareKeyValue < std::is_pointer::value || qx::trait::is_smart_ptr::value, 0 > ::compareByValueAscending)); + } + else + { + QMutexLocker locker(&m_mutex); + std::sort(m_list.begin(), m_list.end(), (&compareKeyValue < std::is_pointer::value || qx::trait::is_smart_ptr::value, 0 > ::compareByValueDescending)); + } + updateHashPosition(0, -1, true); + } + +} // namespace qx + +template +QDataStream &operator<<(QDataStream &stream, const qx::QxCollection &t) +{ + long lCount = t.count(); + stream << (qint32)(lCount); + + for (long l = 0; l < lCount; l++) + { + stream << t.getKeyByIndex(l); + stream << t.getByIndex(l); + } + + return stream; +} + +template +QDataStream &operator>>(QDataStream &stream, qx::QxCollection &t) +{ + qint32 lCount = 0; + stream >> lCount; + t.clear(); + t.reserve(lCount); + + for (qint32 l = 0; l < lCount; l++) + { + Key key; + stream >> key; + Value value; + stream >> value; + t.insert(key, value); + } + + return stream; +} diff --git a/inl/QxCollection/QxCollectionIterator.inl b/inl/QxCollection/QxCollectionIterator.inl new file mode 100644 index 0000000..eaf4d10 --- /dev/null +++ b/inl/QxCollection/QxCollectionIterator.inl @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + + template + QxCollectionIterator::QxCollectionIterator(const QxCollection &col) : m_pCollection(&col), m_lCurrIndex(-1) + { + } + + template + QxCollectionIterator::~QxCollectionIterator() + { + } + + template + inline const Key &QxCollectionIterator::key() const + { + qAssert(m_pCollection && (m_lCurrIndex >= 0) && (m_lCurrIndex < m_pCollection->size())); + return m_pCollection->getKeyByIndex(m_lCurrIndex); + } + + template + inline const Value &QxCollectionIterator::value() const + { + qAssert(m_pCollection && (m_lCurrIndex >= 0) && (m_lCurrIndex < m_pCollection->size())); + return m_pCollection->getByIndex(m_lCurrIndex); + } + + template + inline void QxCollectionIterator::toFirst() + { + m_lCurrIndex = -1; + } + + template + inline void QxCollectionIterator::toLast() + { + m_lCurrIndex = m_pCollection->size(); + } + + template + inline bool QxCollectionIterator::next() + { + long lCurrIndex = m_lCurrIndex; + m_lCurrIndex = ((m_lCurrIndex < (m_pCollection->size() - 1)) ? (m_lCurrIndex + 1) : m_lCurrIndex); + return (m_lCurrIndex > lCurrIndex); + } + + template + inline bool QxCollectionIterator::previous() + { + long lCurrIndex = m_lCurrIndex; + m_lCurrIndex = ((m_lCurrIndex > 0) ? (m_lCurrIndex - 1) : m_lCurrIndex); + return (m_lCurrIndex < lCurrIndex); + } + +} // namespace qx diff --git a/inl/QxConvert/QxConvert_FromJson.inl b/inl/QxConvert/QxConvert_FromJson.inl new file mode 100644 index 0000000..0d63cce --- /dev/null +++ b/inl/QxConvert/QxConvert_FromJson.inl @@ -0,0 +1,413 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, qx::trait::no_type &t, const QString &format) + { + Q_UNUSED(j); + Q_UNUSED(t); + Q_UNUSED(format); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, bool &t, const QString &format) + { + Q_UNUSED(format); + t = j.toBool(); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, short &t, const QString &format) + { + Q_UNUSED(format); + t = static_cast(qRound(j.toDouble())); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, int &t, const QString &format) + { + Q_UNUSED(format); + t = qRound(j.toDouble()); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, long &t, const QString &format) + { + Q_UNUSED(format); + t = static_cast(qRound64(j.toDouble())); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, long long &t, const QString &format) + { + Q_UNUSED(format); + t = static_cast(qRound64(j.toDouble())); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, float &t, const QString &format) + { + Q_UNUSED(format); + t = static_cast(j.toDouble()); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, double &t, const QString &format) + { + Q_UNUSED(format); + t = j.toDouble(); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, unsigned short &t, const QString &format) + { + Q_UNUSED(format); + t = static_cast(qRound(j.toDouble())); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, unsigned int &t, const QString &format) + { + Q_UNUSED(format); + t = static_cast(qRound(j.toDouble())); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, unsigned long &t, const QString &format) + { + Q_UNUSED(format); + t = static_cast(qRound64(j.toDouble())); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, unsigned long long &t, const QString &format) + { + Q_UNUSED(format); + t = static_cast(qRound64(j.toDouble())); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QDateTime &t, const QString &format) + { +#ifdef _QX_ENABLE_MONGODB + if (j.isObject() && format.startsWith("mongodb")) + { + QJsonObject obj = j.toObject(); + QString dt; + if (obj.contains("$date")) + { + dt = obj.value("$date").toString(); + } + if (!dt.isEmpty()) + { + t = QDateTime::fromString(dt, QX_JSON_DATE_TIME_FORMAT); + return qx_bool(true); + } + } +#endif // _QX_ENABLE_MONGODB + + Q_UNUSED(format); + t = (j.isNull() ? QDateTime() : QDateTime::fromString(j.toString(), QX_JSON_DATE_TIME_FORMAT)); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QDate &t, const QString &format) + { +#ifdef _QX_ENABLE_MONGODB + if (j.isObject() && format.startsWith("mongodb")) + { + QDateTime dt; + QxConvert_FromJson::fromJson(j, dt, format); + t = dt.date(); + return qx_bool(true); + } +#endif // _QX_ENABLE_MONGODB + + Q_UNUSED(format); + t = (j.isNull() ? QDate() : QDate::fromString(j.toString(), Qt::ISODate)); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QTime &t, const QString &format) + { + Q_UNUSED(format); + t = (j.isNull() ? QTime() : QTime::fromString(j.toString(), QX_JSON_DATE_TIME_FORMAT)); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QByteArray &t, const QString &format) + { + Q_UNUSED(format); + t = QByteArray::fromBase64(j.toString().toLatin1()); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QString &t, const QString &format) + { + Q_UNUSED(format); + t = j.toString(); + +#ifdef _QX_ENABLE_MONGODB + if (t.isEmpty() && j.isObject() && format.startsWith("mongodb")) + { + QJsonObject obj = j.toObject(); + if (obj.contains("$oid")) + { + t = obj.value("$oid").toString(); + } + if (!t.isEmpty()) + { + t = "qx_oid:" + t; + } + } +#endif // _QX_ENABLE_MONGODB + + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QVariant &t, const QString &format) + { + Q_UNUSED(format); + t = j.toVariant(); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, QUuid &t, const QString &format) + { + Q_UNUSED(format); + t = QUuid(j.toString()); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, qx::QxDateNeutral &t, const QString &format) + { + Q_UNUSED(format); + t = qx::QxDateNeutral::fromNeutral(j.toString()); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, qx::QxTimeNeutral &t, const QString &format) + { + Q_UNUSED(format); + t = qx::QxTimeNeutral::fromNeutral(j.toString()); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, qx::QxDateTimeNeutral &t, const QString &format) + { + Q_UNUSED(format); + t = qx::QxDateTimeNeutral::fromNeutral(j.toString()); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, std::string &t, const QString &format) +#ifndef QT_NO_STL + { + Q_UNUSED(format); + t = j.toString().toStdString(); + return qx_bool(true); + } + }; +#else // QT_NO_STL + { + Q_UNUSED(format); + t = j.toString().toLatin1().constData(); + return qx_bool(true); + } + }; +#endif // QT_NO_STL + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, std::wstring &t, const QString &format) +#if ((!defined(QT_NO_STL)) && (!defined(QT_NO_STL_WCHAR))) + { + Q_UNUSED(format); + t = j.toString().toStdWString(); + return qx_bool(true); + } + }; +#else // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + { + Q_UNUSED(format); + Q_UNUSED(t); + Q_UNUSED(j); + qAssert(false); /* Need STL compatibility ! */ + return qx_bool(true); + } + }; +#endif // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + + template <> + struct QxConvert_FromJson + { + static inline qx_bool fromJson(const QJsonValue &j, qx_bool &t, const QString &format) + { + Q_UNUSED(format); + t = qx_bool(); + if (j.isObject()) + { + QJsonObject obj = j.toObject(); + t.setValue(obj["value"].toBool()); + t.setCode(static_cast(qRound64(obj["code"].toDouble()))); + t.setDesc(obj["desc"].toString()); + } + return qx_bool(true); + } + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct QxConvert_FromJson> + { + static inline qx_bool fromJson(const QJsonValue &j, boost::optional &t, const QString &format) + { + if (j.isNull()) + { + t = boost::none; + return qx_bool(true); + } + else if (!t) + { + t = T(); + } + return qx::cvt::from_json(j, (*t), format); + } + }; + +#endif // _QX_ENABLE_BOOST + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_NO_JSON diff --git a/inl/QxConvert/QxConvert_FromString.inl b/inl/QxConvert/QxConvert_FromString.inl new file mode 100644 index 0000000..97bba83 --- /dev/null +++ b/inl/QxConvert/QxConvert_FromString.inl @@ -0,0 +1,979 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, qx::trait::no_type &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(s); + Q_UNUSED(t); + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, QString &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = s; + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, QUuid &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = QUuid(s); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, QDate &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(index); + Q_UNUSED(ctx); + t = QDate::fromString(s, (format.isEmpty() ? QString(QX_STR_CVT_QDATE_FORMAT) : format)); + return t.isValid(); + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, QTime &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(index); + Q_UNUSED(ctx); + t = QTime::fromString(s, (format.isEmpty() ? QString(QX_STR_CVT_QTIME_FORMAT) : format)); + return t.isValid(); + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, QDateTime &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(index); + Q_UNUSED(ctx); + t = QDateTime::fromString(s, (format.isEmpty() ? QString(QX_STR_CVT_QDATETIME_FORMAT) : format)); + return t.isValid(); + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, QByteArray &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = s.toLatin1(); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, QVariant &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + +#ifndef _QX_NO_JSON + if (s.startsWith("$$JSON$$")) + { + QJsonParseError err; + QString stream = s.right(s.size() - 16); // $$JSON$$0000XX$$ + QJsonDocument doc = QJsonDocument::fromJson(stream.toUtf8(), (&err)); + if (err.error == QJsonParseError::NoError) + { + QJsonValue json = (doc.isArray() ? QJsonValue(doc.array()) : QJsonValue(doc.object())); + t = json.toVariant(); + return qx_bool(true); + } + } +#endif // _QX_NO_JSON + + t = QVariant(s); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, qx_bool &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t.fromString(s); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, bool &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = (((s == "0") || s.trimmed().isEmpty()) ? false : true); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, char &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = (s.isEmpty() ? ' ' : s.toLatin1().at(0)); + return (!s.isEmpty()); + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, short &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = s.toShort(&bOk); + return bOk; + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, int &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = s.toInt(&bOk); + return bOk; + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, long &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = static_cast(s.toLongLong(&bOk)); + return bOk; + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, long long &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = static_cast(s.toLongLong(&bOk)); + return bOk; + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, float &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = static_cast(s.toDouble(&bOk)); + return bOk; + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, double &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = s.toDouble(&bOk); + return bOk; + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, unsigned short &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = static_cast(s.toUShort(&bOk)); + return bOk; + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, unsigned int &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = static_cast(s.toUInt(&bOk)); + return bOk; + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, unsigned long &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = static_cast(s.toULongLong(&bOk)); + return bOk; + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, unsigned long long &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = static_cast(s.toULongLong(&bOk)); + return bOk; + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, qx::QxDateNeutral &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = qx::QxDateNeutral::fromNeutral(s); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, qx::QxTimeNeutral &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = qx::QxTimeNeutral::fromNeutral(s); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, qx::QxDateTimeNeutral &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = qx::QxDateTimeNeutral::fromNeutral(s); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, std::string &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) +#ifndef QT_NO_STL + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = s.toStdString(); + return qx_bool(true); + } + }; +#else // QT_NO_STL + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = s.toLatin1().constData(); + return qx_bool(true); + } + }; +#endif // QT_NO_STL + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, std::wstring &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) +#if ((!defined(QT_NO_STL)) && (!defined(QT_NO_STL_WCHAR))) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = s.toStdWString(); + return qx_bool(true); + } + }; +#else // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + Q_UNUSED(t); + Q_UNUSED(s); + qAssert(false); /* Need STL compatibility ! */ + return qx_bool(true); + } + }; +#endif // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + +#ifndef _QX_NO_JSON + + template <> + struct QxConvert_FromString + { + static inline qx_bool fromString(const QString &s, QJsonValue &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + QJsonParseError jsonError; + QByteArray dataAsByteArray = s.toUtf8(); + QJsonDocument doc = QJsonDocument::fromJson(dataAsByteArray, (&jsonError)); + if (jsonError.error != QJsonParseError::NoError) + { + return qx_bool(static_cast(jsonError.error), jsonError.errorString()); + } + t = (doc.isArray() ? QJsonValue(doc.array()) : QJsonValue(doc.object())); + return qx_bool(true); + } + }; + +#endif // _QX_NO_JSON + +#ifdef _QX_ENABLE_BOOST + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, boost::optional &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (!t) + { + t = T(); + }; + return qx::cvt::from_string(s, (*t), format, index, ctx); + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, std::pair &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, QPair &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, std::vector &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, std::list &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, std::set &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, boost::unordered_set &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, boost::unordered_multiset &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, std::unordered_set &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, std::unordered_multiset &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, QVector &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, QList &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, QLinkedList &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, QFlags &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = QFlags(QFlag(s.toInt())); + return true; + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, std::map &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, boost::unordered_map &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, boost::unordered_multimap &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, std::unordered_map &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, std::unordered_multimap &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, QHash &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, QMultiHash &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, QMap &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, QMultiMap &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, qx::QxCollection &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + template + struct QxConvert_FromString> + { + static inline qx_bool fromString(const QString &s, std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::from_string(t, s); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx diff --git a/inl/QxConvert/QxConvert_FromVariant.inl b/inl/QxConvert/QxConvert_FromVariant.inl new file mode 100644 index 0000000..9afc641 --- /dev/null +++ b/inl/QxConvert/QxConvert_FromVariant.inl @@ -0,0 +1,991 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#define QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT QX_CVT_DEFAULT_ARCHIVE::from_string(t, v.toString()) +#else // _QX_ENABLE_BOOST_SERIALIZATION +#define QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT QX_CVT_DEFAULT_ARCHIVE::from_byte_array(t, v.toByteArray()) +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, qx::trait::no_type &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(v); + Q_UNUSED(t); + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, bool &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = v.toBool(); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, short &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = static_cast(v.toInt(&bOk)); + return bOk; + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, int &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = v.toInt(&bOk); + return bOk; + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, long &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = static_cast(v.toLongLong(&bOk)); + return bOk; + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, long long &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = static_cast(v.toLongLong(&bOk)); + return bOk; + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, float &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = static_cast(v.toDouble(&bOk)); + return bOk; + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, double &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = v.toDouble(&bOk); + return bOk; + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, unsigned short &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = static_cast(v.toUInt(&bOk)); + return bOk; + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, unsigned int &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = static_cast(v.toUInt(&bOk)); + return bOk; + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, unsigned long &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = static_cast(v.toULongLong(&bOk)); + return bOk; + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, unsigned long long &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + bool bOk = false; + t = static_cast(v.toULongLong(&bOk)); + return bOk; + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, QDate &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = v.toDate(); + return t.isValid(); + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, QTime &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = v.toTime(); + return t.isValid(); + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, QDateTime &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = v.toDateTime(); + return t.isValid(); + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, QByteArray &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = v.toByteArray(); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, QString &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = v.toString(); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, QVariant &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (ctx != qx::cvt::context::e_database) + { + t = v; + return qx_bool(true); + } + QString s = v.toString(); + if (!s.startsWith("$$JSON$$")) + { + t = v; + return qx_bool(true); + } + return qx::cvt::detail::QxConvert_FromString::fromString(s, t, format, index, ctx); + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, QUuid &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = QUuid(v.toString()); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, qx::QxDateNeutral &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = qx::QxDateNeutral::fromNeutral(v.toString()); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, qx::QxTimeNeutral &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = qx::QxTimeNeutral::fromNeutral(v.toString()); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, qx::QxDateTimeNeutral &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = qx::QxDateTimeNeutral::fromNeutral(v.toString()); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, std::string &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) +#ifndef QT_NO_STL + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = v.toString().toStdString(); + return qx_bool(true); + } + }; +#else // QT_NO_STL + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = v.toString().toLatin1().constData(); + return qx_bool(true); + } + }; +#endif // QT_NO_STL + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, std::wstring &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) +#if ((!defined(QT_NO_STL)) && (!defined(QT_NO_STL_WCHAR))) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = v.toString().toStdWString(); + return qx_bool(true); + } + }; +#else // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + Q_UNUSED(t); + Q_UNUSED(v); + qAssert(false); /* Need STL compatibility ! */ + return qx_bool(true); + } + }; +#endif // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, qx_bool &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + QString s = v.toString(); + t.fromString(s); + return qx_bool(true); + } + }; + +#ifndef _QX_NO_JSON + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, QJsonValue &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = QJsonValue::fromVariant(v); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, QJsonArray &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = v.toJsonArray(); + return qx_bool(true); + } + }; + + template <> + struct QxConvert_FromVariant + { + static inline qx_bool fromVariant(const QVariant &v, QJsonObject &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = v.toJsonObject(); + return qx_bool(true); + } + }; + +#endif // _QX_NO_JSON + +#ifdef _QX_ENABLE_BOOST + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, boost::optional &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (v.isNull()) + { + t = boost::none; + return qx_bool(true); + } + else if (!t) + { + t = T(); + } + return qx::cvt::from_variant(v, (*t), format, index, ctx); + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, std::pair &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, QPair &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, std::vector &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, std::list &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, std::set &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, boost::unordered_set &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, boost::unordered_multiset &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, std::unordered_set &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, std::unordered_multiset &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, QVector &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, QList &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, QLinkedList &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, QFlags &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + t = QFlags(QFlag(v.toInt())); + return true; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, std::map &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, boost::unordered_map &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, boost::unordered_multimap &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, std::unordered_map &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, std::unordered_multimap &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, QHash &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, QMultiHash &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, QMap &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, QMultiMap &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, qx::QxCollection &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + template + struct QxConvert_FromVariant> + { + static inline qx_bool fromVariant(const QVariant &v, std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_FROM_VARIANT; + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx diff --git a/inl/QxConvert/QxConvert_Qt.inl b/inl/QxConvert/QxConvert_Qt.inl new file mode 100644 index 0000000..f43bc28 --- /dev/null +++ b/inl/QxConvert/QxConvert_Qt.inl @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_STRING_CVT_QT_H_ +#define _QX_STRING_CVT_QT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include + +QX_CVT_USING_ARCHIVE_IMPL(QObject) +QX_CVT_USING_ARCHIVE_IMPL(QPoint) +QX_CVT_USING_ARCHIVE_IMPL(QRect) +QX_CVT_USING_ARCHIVE_IMPL(QSize) +QX_CVT_USING_ARCHIVE_IMPL(QStringList) +QX_CVT_USING_ARCHIVE_IMPL(QUrl) +QX_CVT_USING_ARCHIVE_IMPL(QSqlError) + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_CVT_USING_ARCHIVE_IMPL(QRegExp) +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +#ifdef _QX_ENABLE_QT_GUI +QX_CVT_USING_ARCHIVE_IMPL(QBrush) +QX_CVT_USING_ARCHIVE_IMPL(QColor) +QX_CVT_USING_ARCHIVE_IMPL(QFont) +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_CVT_USING_ARCHIVE_IMPL(QMatrix) +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_CVT_USING_ARCHIVE_IMPL(QRegion) +QX_CVT_USING_ARCHIVE_IMPL(QImage) +QX_CVT_USING_ARCHIVE_IMPL(QPicture) +QX_CVT_USING_ARCHIVE_IMPL(QPixmap) +#endif // _QX_ENABLE_QT_GUI + +QX_CVT_USING_ARCHIVE_IMPL(qx::QxSqlQuery) +QX_CVT_USING_ARCHIVE_IMPL(qx::QxInvalidValue) +QX_CVT_USING_ARCHIVE_IMPL(qx::QxInvalidValueX) + +#endif // _QX_STRING_CVT_QT_H_ diff --git a/inl/QxConvert/QxConvert_ToJson.inl b/inl/QxConvert/QxConvert_ToJson.inl new file mode 100644 index 0000000..fa9d0f3 --- /dev/null +++ b/inl/QxConvert/QxConvert_ToJson.inl @@ -0,0 +1,387 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_NO_JSON + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const qx::trait::no_type &t, const QString &format) + { + Q_UNUSED(t); + Q_UNUSED(format); + return QJsonValue(); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const bool &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(t); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const short &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(static_cast(t)); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const int &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(t); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const long &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(static_cast(t)); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const long long &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(static_cast(t)); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const float &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(static_cast(t)); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const double &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(t); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const unsigned short &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(static_cast(t)); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const unsigned int &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(static_cast(t)); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const unsigned long &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(static_cast(t)); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const unsigned long long &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(static_cast(t)); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QDateTime &t, const QString &format) + { +#ifdef _QX_ENABLE_MONGODB + if (t.isValid() && format.startsWith("mongodb")) + { + QString dt = t.toString(QX_JSON_DATE_TIME_FORMAT); + if (dt.count() <= QX_JSON_DATE_TIME_FORMAT_SIZE) + { + dt += "Z"; + } + QJsonObject obj; + obj.insert("$date", QJsonValue(dt)); + return QJsonValue(obj); + } +#endif // _QX_ENABLE_MONGODB + + Q_UNUSED(format); + if (t.isValid()) + { + return QJsonValue(t.toString(QX_JSON_DATE_TIME_FORMAT)); + }; + return QJsonValue(); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QDate &t, const QString &format) + { +#ifdef _QX_ENABLE_MONGODB + if (t.isValid() && format.startsWith("mongodb")) +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + { + QTime time = ((t.isValid()) ? QTime(0, 0) : QTime()); + QDateTime dt(t, time); + return QxConvert_ToJson::toJson(dt, format); + } +#else // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + { + QDateTime dt(t); + return QxConvert_ToJson::toJson(dt, format); + } +#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +#endif // _QX_ENABLE_MONGODB + + Q_UNUSED(format); + if (t.isValid()) + { + return QJsonValue(t.toString(Qt::ISODate)); + }; + return QJsonValue(); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QTime &t, const QString &format) + { + Q_UNUSED(format); + if (t.isValid()) + { + return QJsonValue(t.toString(QX_JSON_DATE_TIME_FORMAT)); + }; + return QJsonValue(); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QByteArray &t, const QString &format) + { + Q_UNUSED(format); + QString s = t.toBase64(); + return QJsonValue(s); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QString &t, const QString &format) + { +#ifdef _QX_ENABLE_MONGODB + if (t.startsWith("qx_oid:") && format.startsWith("mongodb")) + { + QJsonObject obj; + obj.insert("$oid", QJsonValue(t.right(t.size() - 7))); + return QJsonValue(obj); + } +#endif // _QX_ENABLE_MONGODB + + Q_UNUSED(format); + return QJsonValue(t); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QVariant &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue::fromVariant(t); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const QUuid &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(t.toString()); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const qx::QxDateNeutral &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(t.toNeutral()); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const qx::QxTimeNeutral &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(t.toNeutral()); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const qx::QxDateTimeNeutral &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(t.toNeutral()); + } + }; + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const std::string &t, const QString &format) +#ifndef QT_NO_STL + { + Q_UNUSED(format); + return QJsonValue(QString::fromStdString(t)); + } + }; +#else // QT_NO_STL + { + Q_UNUSED(format); + return QJsonValue(QString::fromLatin1(t.data(), int(t.size()))); + } + }; +#endif // QT_NO_STL + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const std::wstring &t, const QString &format) +#if ((!defined(QT_NO_STL)) && (!defined(QT_NO_STL_WCHAR))) + { + Q_UNUSED(format); + return QJsonValue(QString::fromStdWString(t)); + } + }; +#else // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + { + Q_UNUSED(format); + Q_UNUSED(t); + qAssert(false); /* Need STL compatibility ! */ + return QJsonValue(); + } + }; +#endif // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + + template <> + struct QxConvert_ToJson + { + static inline QJsonValue toJson(const qx_bool &t, const QString &format) + { + Q_UNUSED(format); + QJsonObject obj; + obj["value"] = t.getValue(); + obj["code"] = static_cast(t.getCode()); + obj["desc"] = t.getDesc(); + return QJsonValue(obj); + } + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct QxConvert_ToJson> + { + static inline QJsonValue toJson(const boost::optional &t, const QString &format) + { + if (t) + { + return qx::cvt::to_json((*t), format); + }; + return QJsonValue(); + } + }; + +#endif // _QX_ENABLE_BOOST + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_NO_JSON diff --git a/inl/QxConvert/QxConvert_ToString.inl b/inl/QxConvert/QxConvert_ToString.inl new file mode 100644 index 0000000..75efe06 --- /dev/null +++ b/inl/QxConvert/QxConvert_ToString.inl @@ -0,0 +1,923 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template <> + struct QxConvert_ToString + { + static inline QString toString(const qx::trait::no_type &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(t); + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return ""; + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const QString &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return t; + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const QUuid &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return t.toString(); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const QDate &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(index); + Q_UNUSED(ctx); + return t.toString(format.isEmpty() ? QString(QX_STR_CVT_QDATE_FORMAT) : format); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const QTime &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(index); + Q_UNUSED(ctx); + return t.toString(format.isEmpty() ? QString(QX_STR_CVT_QTIME_FORMAT) : format); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const QDateTime &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(index); + Q_UNUSED(ctx); + return t.toString(format.isEmpty() ? QString(QX_STR_CVT_QDATETIME_FORMAT) : format); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const QByteArray &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QString(t); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const QVariant &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + +#ifndef _QX_NO_JSON + if (qx::cvt::detail::helper::checkConvertQVariantToString(t)) + { + QString type = QString("%1").arg(static_cast(t.type()), 6, 10, QChar('0')); + QString val = "$$JSON$$" + type + "$$"; + QJsonValue json = QJsonValue::fromVariant(t); + QJsonDocument doc = (json.isArray() ? QJsonDocument(json.toArray()) : QJsonDocument(json.toObject())); + return (val + QString::fromUtf8(doc.toJson())); + } +#endif // _QX_NO_JSON + + return t.toString(); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const qx_bool &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return t.toString(); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const bool &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return (t ? "1" : "0"); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const char &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QString(t); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const short &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(index); + Q_UNUSED(ctx); + return (format.isEmpty() ? QString::number(t) : QString(format).arg(t)); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const int &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(index); + Q_UNUSED(ctx); + return (format.isEmpty() ? QString::number(t) : QString(format).arg(t)); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const long &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(index); + Q_UNUSED(ctx); + return (format.isEmpty() ? QString::number(t) : QString(format).arg(t)); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const long long &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(index); + Q_UNUSED(ctx); + return (format.isEmpty() ? QString::number(t) : QString(format).arg(t)); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const float &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(index); + Q_UNUSED(ctx); + return (format.isEmpty() ? QString::number(t) : QString(format).arg(t)); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const double &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(index); + Q_UNUSED(ctx); + return (format.isEmpty() ? QString::number(t) : QString(format).arg(t)); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const unsigned short &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(index); + Q_UNUSED(ctx); + return (format.isEmpty() ? QString::number(t) : QString(format).arg(t)); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const unsigned int &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(index); + Q_UNUSED(ctx); + return (format.isEmpty() ? QString::number(t) : QString(format).arg(t)); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const unsigned long &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(index); + Q_UNUSED(ctx); + return (format.isEmpty() ? QString::number(t) : QString(format).arg(t)); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const unsigned long long &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(index); + Q_UNUSED(ctx); + return (format.isEmpty() ? QString::number(t) : QString(format).arg(t)); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const qx::QxDateNeutral &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return t.toNeutral(); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const qx::QxTimeNeutral &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return t.toNeutral(); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const qx::QxDateTimeNeutral &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return t.toNeutral(); + } + }; + + template <> + struct QxConvert_ToString + { + static inline QString toString(const std::string &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) +#ifndef QT_NO_STL + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QString::fromStdString(t); + } + }; +#else // QT_NO_STL + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QString::fromLatin1(t.data(), int(t.size())); + } + }; +#endif // QT_NO_STL + + template <> + struct QxConvert_ToString + { + static inline QString toString(const std::wstring &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) +#if ((!defined(QT_NO_STL)) && (!defined(QT_NO_STL_WCHAR))) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QString::fromStdWString(t); + } + }; +#else // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + Q_UNUSED(t); + qAssert(false); /* Need STL compatibility ! */ + return QString(); + } + }; +#endif // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + +#ifndef _QX_NO_JSON + + template <> + struct QxConvert_ToString + { + static inline QString toString(const QJsonValue &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(index); + Q_UNUSED(ctx); + QJsonDocument::JsonFormat jsonFormat = QJsonDocument::Compact; + if (!format.isEmpty()) + { + jsonFormat = ((format == "indented") ? QJsonDocument::Indented : jsonFormat); + } + QJsonDocument doc = (t.isArray() ? QJsonDocument(t.toArray()) : QJsonDocument(t.toObject())); + return QString::fromUtf8(doc.toJson(jsonFormat)); + } + }; + +#endif // _QX_NO_JSON + +#ifdef _QX_ENABLE_BOOST + + template + struct QxConvert_ToString> + { + static inline QString toString(const boost::optional &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (t) + { + return qx::cvt::to_string((*t), format, index, ctx); + }; + return QString(); + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct QxConvert_ToString> + { + static inline QString toString(const std::pair &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct QxConvert_ToString> + { + static inline QString toString(const QPair &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct QxConvert_ToString> + { + static inline QString toString(const std::vector &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const std::list &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const std::set &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct QxConvert_ToString> + { + static inline QString toString(const boost::unordered_set &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const boost::unordered_multiset &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct QxConvert_ToString> + { + static inline QString toString(const std::unordered_set &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const std::unordered_multiset &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct QxConvert_ToString> + { + static inline QString toString(const QVector &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct QxConvert_ToString> + { + static inline QString toString(const QList &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + template + struct QxConvert_ToString> + { + static inline QString toString(const QLinkedList &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + + template + struct QxConvert_ToString> + { + static inline QString toString(const QFlags &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QString::number(static_cast(t)); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const std::map &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct QxConvert_ToString> + { + static inline QString toString(const boost::unordered_map &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const boost::unordered_multimap &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct QxConvert_ToString> + { + static inline QString toString(const std::unordered_map &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const std::unordered_multimap &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const QHash &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const QMultiHash &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const QMap &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const QMultiMap &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const qx::QxCollection &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct QxConvert_ToString> + { + static inline QString toString(const boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct QxConvert_ToString> + { + static inline QString toString(const std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + template + struct QxConvert_ToString> + { + static inline QString toString(const std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE::to_string(t); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx diff --git a/inl/QxConvert/QxConvert_ToVariant.inl b/inl/QxConvert/QxConvert_ToVariant.inl new file mode 100644 index 0000000..47b64f8 --- /dev/null +++ b/inl/QxConvert/QxConvert_ToVariant.inl @@ -0,0 +1,946 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION +#define QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT QX_CVT_DEFAULT_ARCHIVE::to_string(t) +#else // _QX_ENABLE_BOOST_SERIALIZATION +#define QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT QX_CVT_DEFAULT_ARCHIVE::to_byte_array(t) +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const qx::trait::no_type &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(t); + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const bool &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(t); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const short &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(static_cast(t)); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const int &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(t); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const long &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(static_cast(t)); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const long long &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(static_cast(t)); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const float &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(static_cast(t)); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const double &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(t); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const unsigned short &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(static_cast(t)); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const unsigned int &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(t); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const unsigned long &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(static_cast(t)); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const unsigned long long &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(static_cast(t)); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const QDate &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(t); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const QTime &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(t); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const QDateTime &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(t); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const QByteArray &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(t); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const QString &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(t); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const QVariant &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (ctx != qx::cvt::context::e_database) + { + return t; + } + if (!qx::cvt::detail::helper::checkConvertQVariantToString(t)) + { + return t; + } + return QVariant(qx::cvt::detail::QxConvert_ToString::toString(t, format, index, ctx)); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const QUuid &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(t.toString()); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const qx::QxDateNeutral &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(t.toNeutral()); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const qx::QxTimeNeutral &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(t.toNeutral()); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const qx::QxDateTimeNeutral &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QVariant(t.toNeutral()); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const std::string &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) +#ifndef QT_NO_STL + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QString::fromStdString(t); + } + }; +#else // QT_NO_STL + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QString::fromLatin1(t.data(), int(t.size())); + } + }; +#endif // QT_NO_STL + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const std::wstring &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) +#if ((!defined(QT_NO_STL)) && (!defined(QT_NO_STL_WCHAR))) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QString::fromStdWString(t); + } + }; +#else // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + Q_UNUSED(t); + qAssert(false); /* Need STL compatibility ! */ + return QVariant(); + } + }; +#endif // ((! defined(QT_NO_STL)) && (! defined(QT_NO_STL_WCHAR))) + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const qx_bool &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return t.toString(); + } + }; + +#ifndef _QX_NO_JSON + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const QJsonValue &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return t.toVariant(); + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const QJsonArray &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + QVariant var(t); + return var; + } + }; + + template <> + struct QxConvert_ToVariant + { + static inline QVariant toVariant(const QJsonObject &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + QVariant var(t); + return var; + } + }; + +#endif // _QX_NO_JSON + +#ifdef _QX_ENABLE_BOOST + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const boost::optional &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + if (t) + { + return qx::cvt::to_variant((*t), format, index, ctx); + }; + return qx::trait::construct_null_qvariant::get(); + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const std::pair &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const QPair &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const std::vector &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const std::list &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const std::set &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const boost::unordered_set &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const boost::unordered_multiset &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const std::unordered_set &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const std::unordered_multiset &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const QVector &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const QList &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const QLinkedList &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const QFlags &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QString::number(static_cast(t)); + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const std::map &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const boost::unordered_map &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const boost::unordered_multimap &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const std::unordered_map &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const std::unordered_multimap &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const QHash &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const QMultiHash &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const QMap &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const QMultiMap &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const qx::QxCollection &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + +#ifdef _QX_ENABLE_BOOST + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const boost::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + template + struct QxConvert_ToVariant> + { + static inline QVariant toVariant(const std::tuple &t, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + Q_UNUSED(format); + Q_UNUSED(index); + Q_UNUSED(ctx); + return QX_CVT_DEFAULT_ARCHIVE_TO_VARIANT; + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx diff --git a/inl/QxConvert/QxConvert_WithIndex.inl b/inl/QxConvert/QxConvert_WithIndex.inl new file mode 100644 index 0000000..efd844b --- /dev/null +++ b/inl/QxConvert/QxConvert_WithIndex.inl @@ -0,0 +1,1015 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace cvt + { + namespace detail + { + + template + struct QxConvert_WithIndex_ToString + { + static inline QString toString(const T &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) { return qx::cvt::detail::QxConvert_ToString::toString(data, format, index, ctx); } + }; + + template + struct QxConvert_WithIndex_FromString + { + static inline qx_bool fromString(T &data, const QString &s, const QString &format, int index, qx::cvt::context::ctx_type ctx) { return qx::cvt::detail::QxConvert_FromString::fromString(s, data, format, index, ctx); } + }; + + template + struct QxConvert_WithIndex_ToVariant + { + static inline QVariant toVariant(const T &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) { return qx::cvt::detail::QxConvert_ToVariant::toVariant(data, format, index, ctx); } + }; + + template + struct QxConvert_WithIndex_FromVariant + { + static inline qx_bool fromVariant(T &data, const QVariant &v, const QString &format, int index, qx::cvt::context::ctx_type ctx) { return qx::cvt::detail::QxConvert_FromVariant::fromVariant(v, data, format, index, ctx); } + }; + + template + struct QxConvert_WithIndex_ToString> + { + static inline QString toString(const std::pair &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 2)); + return ((index == 1) ? qx::cvt::to_string(data.second, format, -1, ctx) : ((index == 0) ? qx::cvt::to_string(data.first, format, -1, ctx) : qx::cvt::detail::QxConvert_ToString>::toString(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromString> + { + static inline qx_bool fromString(std::pair &data, const QString &s, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 2)); + return ((index == 1) ? qx::cvt::from_string(s, data.second, format, -1, ctx) : ((index == 0) ? qx::cvt::from_string(s, data.first, format, -1, ctx) : qx::cvt::detail::QxConvert_FromString>::fromString(s, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToVariant> + { + static inline QVariant toVariant(const std::pair &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 2)); + return ((index == 1) ? qx::cvt::to_variant(data.second, format, -1, ctx) : ((index == 0) ? qx::cvt::to_variant(data.first, format, -1, ctx) : qx::cvt::detail::QxConvert_ToVariant>::toVariant(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromVariant> + { + static inline qx_bool fromVariant(std::pair &data, const QVariant &v, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 2)); + return ((index == 1) ? qx::cvt::from_variant(v, data.second, format, -1, ctx) : ((index == 0) ? qx::cvt::from_variant(v, data.first, format, -1, ctx) : qx::cvt::detail::QxConvert_FromVariant>::fromVariant(v, data, format, -1, ctx))); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct QxConvert_WithIndex_ToString> + { + static inline QString toString(const QPair &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 2)); + return ((index == 1) ? qx::cvt::to_string(data.second, format, -1, ctx) : ((index == 0) ? qx::cvt::to_string(data.first, format, -1, ctx) : qx::cvt::detail::QxConvert_ToString>::toString(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromString> + { + static inline qx_bool fromString(QPair &data, const QString &s, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 2)); + return ((index == 1) ? qx::cvt::from_string(s, data.second, format, -1, ctx) : ((index == 0) ? qx::cvt::from_string(s, data.first, format, -1, ctx) : qx::cvt::detail::QxConvert_FromString>::fromString(s, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToVariant> + { + static inline QVariant toVariant(const QPair &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 2)); + return ((index == 1) ? qx::cvt::to_variant(data.second, format, -1, ctx) : ((index == 0) ? qx::cvt::to_variant(data.first, format, -1, ctx) : qx::cvt::detail::QxConvert_ToVariant>::toVariant(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromVariant> + { + static inline qx_bool fromVariant(QPair &data, const QVariant &v, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 2)); + return ((index == 1) ? qx::cvt::from_variant(v, data.second, format, -1, ctx) : ((index == 0) ? qx::cvt::from_variant(v, data.first, format, -1, ctx) : qx::cvt::detail::QxConvert_FromVariant>::fromVariant(v, data, format, -1, ctx))); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +#ifdef _QX_ENABLE_BOOST + + template + struct QxConvert_WithIndex_ToString> + { + static inline QString toString(const boost::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 2)); + return ((index == 1) ? qx::cvt::to_string(boost::tuples::get<1>(data), format, -1, ctx) : ((index == 0) ? qx::cvt::to_string(boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToString>::toString(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromString> + { + static inline qx_bool fromString(boost::tuple &data, const QString &s, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 2)); + return ((index == 1) ? qx::cvt::from_string(s, boost::tuples::get<1>(data), format, -1, ctx) : ((index == 0) ? qx::cvt::from_string(s, boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromString>::fromString(s, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToVariant> + { + static inline QVariant toVariant(const boost::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 2)); + return ((index == 1) ? qx::cvt::to_variant(boost::tuples::get<1>(data), format, -1, ctx) : ((index == 0) ? qx::cvt::to_variant(boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToVariant>::toVariant(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromVariant> + { + static inline qx_bool fromVariant(boost::tuple &data, const QVariant &v, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 2)); + return ((index == 1) ? qx::cvt::from_variant(v, boost::tuples::get<1>(data), format, -1, ctx) : ((index == 0) ? qx::cvt::from_variant(v, boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromVariant>::fromVariant(v, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToString> + { + static inline QString toString(const boost::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 3)); + return ((index == 2) ? qx::cvt::to_string(boost::tuples::get<2>(data), format, -1, ctx) : (index == 1) ? qx::cvt::to_string(boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_string(boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToString>::toString(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromString> + { + static inline qx_bool fromString(boost::tuple &data, const QString &s, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 3)); + return ((index == 2) ? qx::cvt::from_string(s, boost::tuples::get<2>(data), format, -1, ctx) : (index == 1) ? qx::cvt::from_string(s, boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_string(s, boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromString>::fromString(s, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToVariant> + { + static inline QVariant toVariant(const boost::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 3)); + return ((index == 2) ? qx::cvt::to_variant(boost::tuples::get<2>(data), format, -1, ctx) : (index == 1) ? qx::cvt::to_variant(boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_variant(boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToVariant>::toVariant(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromVariant> + { + static inline qx_bool fromVariant(boost::tuple &data, const QVariant &v, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 3)); + return ((index == 2) ? qx::cvt::from_variant(v, boost::tuples::get<2>(data), format, -1, ctx) : (index == 1) ? qx::cvt::from_variant(v, boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_variant(v, boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromVariant>::fromVariant(v, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToString> + { + static inline QString toString(const boost::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 4)); + return ((index == 3) ? qx::cvt::to_string(boost::tuples::get<3>(data), format, -1, ctx) : (index == 2) ? qx::cvt::to_string(boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_string(boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_string(boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToString>::toString(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromString> + { + static inline qx_bool fromString(boost::tuple &data, const QString &s, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 4)); + return ((index == 3) ? qx::cvt::from_string(s, boost::tuples::get<3>(data), format, -1, ctx) : (index == 2) ? qx::cvt::from_string(s, boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_string(s, boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_string(s, boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromString>::fromString(s, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToVariant> + { + static inline QVariant toVariant(const boost::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 4)); + return ((index == 3) ? qx::cvt::to_variant(boost::tuples::get<3>(data), format, -1, ctx) : (index == 2) ? qx::cvt::to_variant(boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_variant(boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_variant(boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToVariant>::toVariant(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromVariant> + { + static inline qx_bool fromVariant(boost::tuple &data, const QVariant &v, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 4)); + return ((index == 3) ? qx::cvt::from_variant(v, boost::tuples::get<3>(data), format, -1, ctx) : (index == 2) ? qx::cvt::from_variant(v, boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_variant(v, boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_variant(v, boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromVariant>::fromVariant(v, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToString> + { + static inline QString toString(const boost::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 5)); + return ((index == 4) ? qx::cvt::to_string(boost::tuples::get<4>(data), format, -1, ctx) : (index == 3) ? qx::cvt::to_string(boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_string(boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_string(boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_string(boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToString>::toString(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromString> + { + static inline qx_bool fromString(boost::tuple &data, const QString &s, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 5)); + return ((index == 4) ? qx::cvt::from_string(s, boost::tuples::get<4>(data), format, -1, ctx) : (index == 3) ? qx::cvt::from_string(s, boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_string(s, boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_string(s, boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_string(s, boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromString>::fromString(s, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToVariant> + { + static inline QVariant toVariant(const boost::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 5)); + return ((index == 4) ? qx::cvt::to_variant(boost::tuples::get<4>(data), format, -1, ctx) : (index == 3) ? qx::cvt::to_variant(boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_variant(boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_variant(boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_variant(boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToVariant>::toVariant(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromVariant> + { + static inline qx_bool fromVariant(boost::tuple &data, const QVariant &v, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 5)); + return ((index == 4) ? qx::cvt::from_variant(v, boost::tuples::get<4>(data), format, -1, ctx) : (index == 3) ? qx::cvt::from_variant(v, boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_variant(v, boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_variant(v, boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_variant(v, boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromVariant>::fromVariant(v, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToString> + { + static inline QString toString(const boost::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 6)); + return ((index == 5) ? qx::cvt::to_string(boost::tuples::get<5>(data), format, -1, ctx) : (index == 4) ? qx::cvt::to_string(boost::tuples::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::to_string(boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_string(boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_string(boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_string(boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToString>::toString(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromString> + { + static inline qx_bool fromString(boost::tuple &data, const QString &s, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 6)); + return ((index == 5) ? qx::cvt::from_string(s, boost::tuples::get<5>(data), format, -1, ctx) : (index == 4) ? qx::cvt::from_string(s, boost::tuples::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::from_string(s, boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_string(s, boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_string(s, boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_string(s, boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromString>::fromString(s, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToVariant> + { + static inline QVariant toVariant(const boost::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 6)); + return ((index == 5) ? qx::cvt::to_variant(boost::tuples::get<5>(data), format, -1, ctx) : (index == 4) ? qx::cvt::to_variant(boost::tuples::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::to_variant(boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_variant(boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_variant(boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_variant(boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToVariant>::toVariant(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromVariant> + { + static inline qx_bool fromVariant(boost::tuple &data, const QVariant &v, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 6)); + return ((index == 5) ? qx::cvt::from_variant(v, boost::tuples::get<5>(data), format, -1, ctx) : (index == 4) ? qx::cvt::from_variant(v, boost::tuples::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::from_variant(v, boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_variant(v, boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_variant(v, boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_variant(v, boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromVariant>::fromVariant(v, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToString> + { + static inline QString toString(const boost::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 7)); + return ((index == 6) ? qx::cvt::to_string(boost::tuples::get<6>(data), format, -1, ctx) : (index == 5) ? qx::cvt::to_string(boost::tuples::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::to_string(boost::tuples::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::to_string(boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_string(boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_string(boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_string(boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToString>::toString(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromString> + { + static inline qx_bool fromString(boost::tuple &data, const QString &s, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 7)); + return ((index == 6) ? qx::cvt::from_string(s, boost::tuples::get<6>(data), format, -1, ctx) : (index == 5) ? qx::cvt::from_string(s, boost::tuples::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::from_string(s, boost::tuples::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::from_string(s, boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_string(s, boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_string(s, boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_string(s, boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromString>::fromString(s, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToVariant> + { + static inline QVariant toVariant(const boost::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 7)); + return ((index == 6) ? qx::cvt::to_variant(boost::tuples::get<6>(data), format, -1, ctx) : (index == 5) ? qx::cvt::to_variant(boost::tuples::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::to_variant(boost::tuples::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::to_variant(boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_variant(boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_variant(boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_variant(boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToVariant>::toVariant(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromVariant> + { + static inline qx_bool fromVariant(boost::tuple &data, const QVariant &v, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 7)); + return ((index == 6) ? qx::cvt::from_variant(v, boost::tuples::get<6>(data), format, -1, ctx) : (index == 5) ? qx::cvt::from_variant(v, boost::tuples::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::from_variant(v, boost::tuples::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::from_variant(v, boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_variant(v, boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_variant(v, boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_variant(v, boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromVariant>::fromVariant(v, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToString> + { + static inline QString toString(const boost::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 8)); + return ((index == 7) ? qx::cvt::to_string(boost::tuples::get<7>(data), format, -1, ctx) : (index == 6) ? qx::cvt::to_string(boost::tuples::get<6>(data), format, -1, ctx) + : (index == 5) ? qx::cvt::to_string(boost::tuples::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::to_string(boost::tuples::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::to_string(boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_string(boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_string(boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_string(boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToString>::toString(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromString> + { + static inline qx_bool fromString(boost::tuple &data, const QString &s, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 8)); + return ((index == 7) ? qx::cvt::from_string(s, boost::tuples::get<7>(data), format, -1, ctx) : (index == 6) ? qx::cvt::from_string(s, boost::tuples::get<6>(data), format, -1, ctx) + : (index == 5) ? qx::cvt::from_string(s, boost::tuples::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::from_string(s, boost::tuples::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::from_string(s, boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_string(s, boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_string(s, boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_string(s, boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromString>::fromString(s, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToVariant> + { + static inline QVariant toVariant(const boost::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 8)); + return ((index == 7) ? qx::cvt::to_variant(boost::tuples::get<7>(data), format, -1, ctx) : (index == 6) ? qx::cvt::to_variant(boost::tuples::get<6>(data), format, -1, ctx) + : (index == 5) ? qx::cvt::to_variant(boost::tuples::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::to_variant(boost::tuples::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::to_variant(boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_variant(boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_variant(boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_variant(boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToVariant>::toVariant(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromVariant> + { + static inline qx_bool fromVariant(boost::tuple &data, const QVariant &v, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 8)); + return ((index == 7) ? qx::cvt::from_variant(v, boost::tuples::get<7>(data), format, -1, ctx) : (index == 6) ? qx::cvt::from_variant(v, boost::tuples::get<6>(data), format, -1, ctx) + : (index == 5) ? qx::cvt::from_variant(v, boost::tuples::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::from_variant(v, boost::tuples::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::from_variant(v, boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_variant(v, boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_variant(v, boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_variant(v, boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromVariant>::fromVariant(v, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToString> + { + static inline QString toString(const boost::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 9)); + return ((index == 8) ? qx::cvt::to_string(boost::tuples::get<8>(data), format, -1, ctx) : (index == 7) ? qx::cvt::to_string(boost::tuples::get<7>(data), format, -1, ctx) + : (index == 6) ? qx::cvt::to_string(boost::tuples::get<6>(data), format, -1, ctx) + : (index == 5) ? qx::cvt::to_string(boost::tuples::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::to_string(boost::tuples::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::to_string(boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_string(boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_string(boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_string(boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToString>::toString(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromString> + { + static inline qx_bool fromString(boost::tuple &data, const QString &s, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 9)); + return ((index == 8) ? qx::cvt::from_string(s, boost::tuples::get<8>(data), format, -1, ctx) : (index == 7) ? qx::cvt::from_string(s, boost::tuples::get<7>(data), format, -1, ctx) + : (index == 6) ? qx::cvt::from_string(s, boost::tuples::get<6>(data), format, -1, ctx) + : (index == 5) ? qx::cvt::from_string(s, boost::tuples::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::from_string(s, boost::tuples::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::from_string(s, boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_string(s, boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_string(s, boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_string(s, boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromString>::fromString(s, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToVariant> + { + static inline QVariant toVariant(const boost::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 9)); + return ((index == 8) ? qx::cvt::to_variant(boost::tuples::get<8>(data), format, -1, ctx) : (index == 7) ? qx::cvt::to_variant(boost::tuples::get<7>(data), format, -1, ctx) + : (index == 6) ? qx::cvt::to_variant(boost::tuples::get<6>(data), format, -1, ctx) + : (index == 5) ? qx::cvt::to_variant(boost::tuples::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::to_variant(boost::tuples::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::to_variant(boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_variant(boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_variant(boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_variant(boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToVariant>::toVariant(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromVariant> + { + static inline qx_bool fromVariant(boost::tuple &data, const QVariant &v, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 9)); + return ((index == 8) ? qx::cvt::from_variant(v, boost::tuples::get<8>(data), format, -1, ctx) : (index == 7) ? qx::cvt::from_variant(v, boost::tuples::get<7>(data), format, -1, ctx) + : (index == 6) ? qx::cvt::from_variant(v, boost::tuples::get<6>(data), format, -1, ctx) + : (index == 5) ? qx::cvt::from_variant(v, boost::tuples::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::from_variant(v, boost::tuples::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::from_variant(v, boost::tuples::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_variant(v, boost::tuples::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_variant(v, boost::tuples::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_variant(v, boost::tuples::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromVariant>::fromVariant(v, data, format, -1, ctx))); + } + }; + +#endif // _QX_ENABLE_BOOST + + template + struct QxConvert_WithIndex_ToString> + { + static inline QString toString(const std::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 2)); + return ((index == 1) ? qx::cvt::to_string(std::get<1>(data), format, -1, ctx) : ((index == 0) ? qx::cvt::to_string(std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToString>::toString(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromString> + { + static inline qx_bool fromString(std::tuple &data, const QString &s, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 2)); + return ((index == 1) ? qx::cvt::from_string(s, std::get<1>(data), format, -1, ctx) : ((index == 0) ? qx::cvt::from_string(s, std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromString>::fromString(s, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToVariant> + { + static inline QVariant toVariant(const std::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 2)); + return ((index == 1) ? qx::cvt::to_variant(std::get<1>(data), format, -1, ctx) : ((index == 0) ? qx::cvt::to_variant(std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToVariant>::toVariant(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromVariant> + { + static inline qx_bool fromVariant(std::tuple &data, const QVariant &v, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 2)); + return ((index == 1) ? qx::cvt::from_variant(v, std::get<1>(data), format, -1, ctx) : ((index == 0) ? qx::cvt::from_variant(v, std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromVariant>::fromVariant(v, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToString> + { + static inline QString toString(const std::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 3)); + return ((index == 2) ? qx::cvt::to_string(std::get<2>(data), format, -1, ctx) : (index == 1) ? qx::cvt::to_string(std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_string(std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToString>::toString(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromString> + { + static inline qx_bool fromString(std::tuple &data, const QString &s, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 3)); + return ((index == 2) ? qx::cvt::from_string(s, std::get<2>(data), format, -1, ctx) : (index == 1) ? qx::cvt::from_string(s, std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_string(s, std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromString>::fromString(s, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToVariant> + { + static inline QVariant toVariant(const std::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 3)); + return ((index == 2) ? qx::cvt::to_variant(std::get<2>(data), format, -1, ctx) : (index == 1) ? qx::cvt::to_variant(std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_variant(std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToVariant>::toVariant(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromVariant> + { + static inline qx_bool fromVariant(std::tuple &data, const QVariant &v, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 3)); + return ((index == 2) ? qx::cvt::from_variant(v, std::get<2>(data), format, -1, ctx) : (index == 1) ? qx::cvt::from_variant(v, std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_variant(v, std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromVariant>::fromVariant(v, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToString> + { + static inline QString toString(const std::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 4)); + return ((index == 3) ? qx::cvt::to_string(std::get<3>(data), format, -1, ctx) : (index == 2) ? qx::cvt::to_string(std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_string(std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_string(std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToString>::toString(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromString> + { + static inline qx_bool fromString(std::tuple &data, const QString &s, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 4)); + return ((index == 3) ? qx::cvt::from_string(s, std::get<3>(data), format, -1, ctx) : (index == 2) ? qx::cvt::from_string(s, std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_string(s, std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_string(s, std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromString>::fromString(s, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToVariant> + { + static inline QVariant toVariant(const std::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 4)); + return ((index == 3) ? qx::cvt::to_variant(std::get<3>(data), format, -1, ctx) : (index == 2) ? qx::cvt::to_variant(std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_variant(std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_variant(std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToVariant>::toVariant(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromVariant> + { + static inline qx_bool fromVariant(std::tuple &data, const QVariant &v, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 4)); + return ((index == 3) ? qx::cvt::from_variant(v, std::get<3>(data), format, -1, ctx) : (index == 2) ? qx::cvt::from_variant(v, std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_variant(v, std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_variant(v, std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromVariant>::fromVariant(v, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToString> + { + static inline QString toString(const std::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 5)); + return ((index == 4) ? qx::cvt::to_string(std::get<4>(data), format, -1, ctx) : (index == 3) ? qx::cvt::to_string(std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_string(std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_string(std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_string(std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToString>::toString(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromString> + { + static inline qx_bool fromString(std::tuple &data, const QString &s, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 5)); + return ((index == 4) ? qx::cvt::from_string(s, std::get<4>(data), format, -1, ctx) : (index == 3) ? qx::cvt::from_string(s, std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_string(s, std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_string(s, std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_string(s, std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromString>::fromString(s, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToVariant> + { + static inline QVariant toVariant(const std::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 5)); + return ((index == 4) ? qx::cvt::to_variant(std::get<4>(data), format, -1, ctx) : (index == 3) ? qx::cvt::to_variant(std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_variant(std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_variant(std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_variant(std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToVariant>::toVariant(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromVariant> + { + static inline qx_bool fromVariant(std::tuple &data, const QVariant &v, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 5)); + return ((index == 4) ? qx::cvt::from_variant(v, std::get<4>(data), format, -1, ctx) : (index == 3) ? qx::cvt::from_variant(v, std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_variant(v, std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_variant(v, std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_variant(v, std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromVariant>::fromVariant(v, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToString> + { + static inline QString toString(const std::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 6)); + return ((index == 5) ? qx::cvt::to_string(std::get<5>(data), format, -1, ctx) : (index == 4) ? qx::cvt::to_string(std::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::to_string(std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_string(std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_string(std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_string(std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToString>::toString(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromString> + { + static inline qx_bool fromString(std::tuple &data, const QString &s, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 6)); + return ((index == 5) ? qx::cvt::from_string(s, std::get<5>(data), format, -1, ctx) : (index == 4) ? qx::cvt::from_string(s, std::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::from_string(s, std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_string(s, std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_string(s, std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_string(s, std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromString>::fromString(s, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToVariant> + { + static inline QVariant toVariant(const std::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 6)); + return ((index == 5) ? qx::cvt::to_variant(std::get<5>(data), format, -1, ctx) : (index == 4) ? qx::cvt::to_variant(std::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::to_variant(std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_variant(std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_variant(std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_variant(std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToVariant>::toVariant(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromVariant> + { + static inline qx_bool fromVariant(std::tuple &data, const QVariant &v, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 6)); + return ((index == 5) ? qx::cvt::from_variant(v, std::get<5>(data), format, -1, ctx) : (index == 4) ? qx::cvt::from_variant(v, std::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::from_variant(v, std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_variant(v, std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_variant(v, std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_variant(v, std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromVariant>::fromVariant(v, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToString> + { + static inline QString toString(const std::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 7)); + return ((index == 6) ? qx::cvt::to_string(std::get<6>(data), format, -1, ctx) : (index == 5) ? qx::cvt::to_string(std::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::to_string(std::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::to_string(std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_string(std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_string(std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_string(std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToString>::toString(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromString> + { + static inline qx_bool fromString(std::tuple &data, const QString &s, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 7)); + return ((index == 6) ? qx::cvt::from_string(s, std::get<6>(data), format, -1, ctx) : (index == 5) ? qx::cvt::from_string(s, std::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::from_string(s, std::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::from_string(s, std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_string(s, std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_string(s, std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_string(s, std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromString>::fromString(s, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToVariant> + { + static inline QVariant toVariant(const std::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 7)); + return ((index == 6) ? qx::cvt::to_variant(std::get<6>(data), format, -1, ctx) : (index == 5) ? qx::cvt::to_variant(std::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::to_variant(std::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::to_variant(std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_variant(std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_variant(std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_variant(std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToVariant>::toVariant(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromVariant> + { + static inline qx_bool fromVariant(std::tuple &data, const QVariant &v, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 7)); + return ((index == 6) ? qx::cvt::from_variant(v, std::get<6>(data), format, -1, ctx) : (index == 5) ? qx::cvt::from_variant(v, std::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::from_variant(v, std::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::from_variant(v, std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_variant(v, std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_variant(v, std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_variant(v, std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromVariant>::fromVariant(v, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToString> + { + static inline QString toString(const std::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 8)); + return ((index == 7) ? qx::cvt::to_string(std::get<7>(data), format, -1, ctx) : (index == 6) ? qx::cvt::to_string(std::get<6>(data), format, -1, ctx) + : (index == 5) ? qx::cvt::to_string(std::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::to_string(std::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::to_string(std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_string(std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_string(std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_string(std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToString>::toString(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromString> + { + static inline qx_bool fromString(std::tuple &data, const QString &s, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 8)); + return ((index == 7) ? qx::cvt::from_string(s, std::get<7>(data), format, -1, ctx) : (index == 6) ? qx::cvt::from_string(s, std::get<6>(data), format, -1, ctx) + : (index == 5) ? qx::cvt::from_string(s, std::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::from_string(s, std::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::from_string(s, std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_string(s, std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_string(s, std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_string(s, std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromString>::fromString(s, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToVariant> + { + static inline QVariant toVariant(const std::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 8)); + return ((index == 7) ? qx::cvt::to_variant(std::get<7>(data), format, -1, ctx) : (index == 6) ? qx::cvt::to_variant(std::get<6>(data), format, -1, ctx) + : (index == 5) ? qx::cvt::to_variant(std::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::to_variant(std::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::to_variant(std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_variant(std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_variant(std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_variant(std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToVariant>::toVariant(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromVariant> + { + static inline qx_bool fromVariant(std::tuple &data, const QVariant &v, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 8)); + return ((index == 7) ? qx::cvt::from_variant(v, std::get<7>(data), format, -1, ctx) : (index == 6) ? qx::cvt::from_variant(v, std::get<6>(data), format, -1, ctx) + : (index == 5) ? qx::cvt::from_variant(v, std::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::from_variant(v, std::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::from_variant(v, std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_variant(v, std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_variant(v, std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_variant(v, std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromVariant>::fromVariant(v, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToString> + { + static inline QString toString(const std::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 9)); + return ((index == 8) ? qx::cvt::to_string(std::get<8>(data), format, -1, ctx) : (index == 7) ? qx::cvt::to_string(std::get<7>(data), format, -1, ctx) + : (index == 6) ? qx::cvt::to_string(std::get<6>(data), format, -1, ctx) + : (index == 5) ? qx::cvt::to_string(std::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::to_string(std::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::to_string(std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_string(std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_string(std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_string(std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToString>::toString(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromString> + { + static inline qx_bool fromString(std::tuple &data, const QString &s, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 9)); + return ((index == 8) ? qx::cvt::from_string(s, std::get<8>(data), format, -1, ctx) : (index == 7) ? qx::cvt::from_string(s, std::get<7>(data), format, -1, ctx) + : (index == 6) ? qx::cvt::from_string(s, std::get<6>(data), format, -1, ctx) + : (index == 5) ? qx::cvt::from_string(s, std::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::from_string(s, std::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::from_string(s, std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_string(s, std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_string(s, std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_string(s, std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromString>::fromString(s, data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_ToVariant> + { + static inline QVariant toVariant(const std::tuple &data, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 9)); + return ((index == 8) ? qx::cvt::to_variant(std::get<8>(data), format, -1, ctx) : (index == 7) ? qx::cvt::to_variant(std::get<7>(data), format, -1, ctx) + : (index == 6) ? qx::cvt::to_variant(std::get<6>(data), format, -1, ctx) + : (index == 5) ? qx::cvt::to_variant(std::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::to_variant(std::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::to_variant(std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::to_variant(std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::to_variant(std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::to_variant(std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_ToVariant>::toVariant(data, format, -1, ctx))); + } + }; + + template + struct QxConvert_WithIndex_FromVariant> + { + static inline qx_bool fromVariant(std::tuple &data, const QVariant &v, const QString &format, int index, qx::cvt::context::ctx_type ctx) + { + qAssert((index >= -1) && (index < 9)); + return ((index == 8) ? qx::cvt::from_variant(v, std::get<8>(data), format, -1, ctx) : (index == 7) ? qx::cvt::from_variant(v, std::get<7>(data), format, -1, ctx) + : (index == 6) ? qx::cvt::from_variant(v, std::get<6>(data), format, -1, ctx) + : (index == 5) ? qx::cvt::from_variant(v, std::get<5>(data), format, -1, ctx) + : (index == 4) ? qx::cvt::from_variant(v, std::get<4>(data), format, -1, ctx) + : (index == 3) ? qx::cvt::from_variant(v, std::get<3>(data), format, -1, ctx) + : (index == 2) ? qx::cvt::from_variant(v, std::get<2>(data), format, -1, ctx) + : (index == 1) ? qx::cvt::from_variant(v, std::get<1>(data), format, -1, ctx) + : ((index == 0) ? qx::cvt::from_variant(v, std::get<0>(data), format, -1, ctx) : qx::cvt::detail::QxConvert_FromVariant>::fromVariant(v, data, format, -1, ctx))); + } + }; + + } // namespace detail + } // namespace cvt +} // namespace qx diff --git a/inl/QxDao/QxDao_Count.inl b/inl/QxDao/QxDao_Count.inl new file mode 100644 index 0000000..e2fe29d --- /dev/null +++ b/inl/QxDao/QxDao_Count.inl @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_Count + { + + static long count(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase) + { + T t; + Q_UNUSED(t); + qx::dao::detail::QxDao_Helper dao(t, pDatabase, "count", new qx::QxSqlQueryBuilder_Count(), (&query)); + if (!dao.isValid()) + { + return 0; + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + long cnt = 0; + qx::dao::mongodb::QxMongoDB_Helper::count((&dao), dao.getDataMemberX()->getClass(), cnt, (&query)); + if (!dao.isValid()) + { + return 0; + } + return cnt; + } +#endif // _QX_ENABLE_MONGODB + + QString sql = dao.builder().buildSql().getSqlQuery(); + if (sql.isEmpty()) + { + dao.errEmpty(); + return 0; + } + if (!query.isEmpty()) + { + dao.addQuery(true); + sql = dao.builder().getSqlQuery(); + } + if (!dao.exec()) + { + dao.errFailed(); + return 0; + } + if (!dao.nextRecord()) + { + dao.errNoData(); + return 0; + } + + return static_cast(dao.query().value(0).toLongLong()); + } + + static QSqlError count(long &lCount, const qx::QxSqlQuery &query, QSqlDatabase *pDatabase) + { + T t; + Q_UNUSED(t); + lCount = 0; + qx::dao::detail::QxDao_Helper dao(t, pDatabase, "count", new qx::QxSqlQueryBuilder_Count(), (&query)); + if (!dao.isValid()) + { + return dao.error(); + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + qx::dao::mongodb::QxMongoDB_Helper::count((&dao), dao.getDataMemberX()->getClass(), lCount, (&query)); + if (!dao.isValid()) + { + return dao.error(); + } + return dao.error(); + } +#endif // _QX_ENABLE_MONGODB + + QString sql = dao.builder().buildSql().getSqlQuery(); + if (sql.isEmpty()) + { + return dao.errEmpty(); + } + if (!query.isEmpty()) + { + dao.addQuery(true); + sql = dao.builder().getSqlQuery(); + } + if (!dao.exec()) + { + return dao.errFailed(); + } + if (!dao.nextRecord()) + { + return dao.errNoData(); + } + lCount = static_cast(dao.query().value(0).toLongLong()); + + return dao.error(); + } + }; + + template + struct QxDao_Count_WithRelation + { + + static QSqlError count(long &lCount, const QStringList &relation, const qx::QxSqlQuery &query, QSqlDatabase *pDatabase) + { + T t; + Q_UNUSED(t); + lCount = 0; + qx::dao::detail::QxDao_Helper dao(t, pDatabase, "count with relation", new qx::QxSqlQueryBuilder_Count_WithRelation(), (&query)); + if (!dao.isValid()) + { + return dao.error(); + } + if (!dao.updateSqlRelationX(relation)) + { + return dao.errInvalidRelation(); + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + qx::dao::mongodb::QxMongoDB_Helper::count((&dao), dao.getDataMemberX()->getClass(), lCount, (&query)); + if (!dao.isValid()) + { + return dao.error(); + } + return dao.error(); + } +#endif // _QX_ENABLE_MONGODB + + QStringList columns; + QString sql = dao.builder().buildSql(columns, dao.getSqlRelationLinked()).getSqlQuery(); + if (sql.isEmpty()) + { + return dao.errEmpty(); + } + if (!query.isEmpty()) + { + dao.addQuery(true); + sql = dao.builder().getSqlQuery(); + } + if (!dao.exec()) + { + return dao.errFailed(); + } + if (!dao.nextRecord()) + { + return dao.errNoData(); + } + lCount = static_cast(dao.query().value(0).toLongLong()); + + return dao.error(); + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxDao_CreateTable.inl b/inl/QxDao/QxDao_CreateTable.inl new file mode 100644 index 0000000..a6694d7 --- /dev/null +++ b/inl/QxDao/QxDao_CreateTable.inl @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_CreateTable + { + + static QSqlError createTable(QSqlDatabase *pDatabase) + { + T t; + Q_UNUSED(t); + qx::dao::detail::QxDao_Helper dao(t, pDatabase, "create table", new qx::QxSqlQueryBuilder_CreateTable()); + if (!dao.isValid()) + { + return dao.error(); + } + + if (dao.database().driverName() != "QSQLITE") + { + QString sWarningMsg = "-- WARNING -- the function qx::dao::create_table() can be used only with a SQLite database to create examples or prototypes, for other databases, it is recommended :"; + sWarningMsg += "\n\t - to use QxEntityEditor application and its DDL SQL database schema export plugin ;"; + sWarningMsg += "\n\t - or to manage the database schema with an external tool provided by the SGBD (SQLite Manager for SQLite, pgAdmin for PostgreSQL, MySQL Workbench for MySQL, etc...) ;"; + sWarningMsg += "\n\t - or to generate database schema using the introspection engine of QxOrm library : go to 'https://www.qxorm.com/qxorm_en/faq.html#faq_230' web page for more details."; + qDebug("[QxOrm] %s", qPrintable(sWarningMsg)); + } + + QString sql = dao.builder().buildSql().getSqlQuery(); + if (sql.isEmpty()) + { + return dao.errEmpty(); + } + if (!dao.query().exec(sql)) + { + return dao.errFailed(); + } + + long index = 0; + qx::IxSqlRelation *pRelation = NULL; + while ((pRelation = dao.builder().nextRelation(index))) + { + QString sqlExtraTable = pRelation->createExtraTable(); + if (sqlExtraTable.isEmpty()) + { + continue; + } + QSqlQuery queryCreateTable(dao.database()); + bool bExtraTable = queryCreateTable.exec(sqlExtraTable); + if (!bExtraTable) + { + dao.updateError(queryCreateTable.lastError()); + break; + } + } + + return dao.error(); + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxDao_DeleteAll.inl b/inl/QxDao/QxDao_DeleteAll.inl new file mode 100644 index 0000000..4f4a287 --- /dev/null +++ b/inl/QxDao/QxDao_DeleteAll.inl @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_DeleteAll + { + + static QSqlError deleteAll(const qx::QxSqlQuery &query, QSqlDatabase *pDatabase, bool bVerifySoftDelete) + { + T t; + Q_UNUSED(t); + qx::IxSqlQueryBuilder *pBuilder = new qx::QxSqlQueryBuilder_DeleteAll(); + pBuilder->init(); + qx::QxSoftDelete oSoftDelete = pBuilder->getSoftDelete(); + if (bVerifySoftDelete && !oSoftDelete.isEmpty()) + { + delete pBuilder; + pBuilder = new qx::QxSqlQueryBuilder_SoftDeleteAll(); + } + + qx::dao::detail::QxDao_Helper dao(t, pDatabase, "delete all", pBuilder, (&query)); + if (!dao.isValid()) + { + return dao.error(); + } + if (dao.isReadOnly()) + { + return dao.errReadOnly(); + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + qx::dao::mongodb::QxMongoDB_Helper::deleteMany((&dao), dao.getDataMemberX()->getClass(), QStringList(), (&query)); + if (!dao.isValid()) + { + return dao.error(); + } + return dao.error(); + } +#endif // _QX_ENABLE_MONGODB + + QString sql = dao.builder().buildSql().getSqlQuery(); + if (sql.isEmpty()) + { + return dao.errEmpty(); + } + if (!pDatabase) + { + dao.transaction(); + } + if (!query.isEmpty()) + { + dao.addQuery(true); + sql = dao.builder().getSqlQuery(); + } + if (!dao.exec()) + { + return dao.errFailed(); + } + + return dao.error(); + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxDao_DeleteById.inl b/inl/QxDao/QxDao_DeleteById.inl new file mode 100644 index 0000000..d45b4e8 --- /dev/null +++ b/inl/QxDao/QxDao_DeleteById.inl @@ -0,0 +1,381 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_DeleteById_Generic + { + + static QSqlError deleteById(T &t, QSqlDatabase *pDatabase, bool bVerifySoftDelete, bool bUseExecBatch) + { + Q_UNUSED(bUseExecBatch); // Useful only with containers + qx::IxSqlQueryBuilder *pBuilder = new qx::QxSqlQueryBuilder_DeleteById(); + pBuilder->init(); + qx::QxSoftDelete oSoftDelete = pBuilder->getSoftDelete(); + if (bVerifySoftDelete && !oSoftDelete.isEmpty()) + { + delete pBuilder; + pBuilder = new qx::QxSqlQueryBuilder_SoftDeleteById(); + } + + qx::dao::detail::QxDao_Helper dao(t, pDatabase, "delete by id", pBuilder); + if (!dao.isValid()) + { + return dao.error(); + } + if (dao.isReadOnly()) + { + return dao.errReadOnly(); + } + if (!dao.isValidPrimaryKey(t)) + { + return dao.errInvalidId(); + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + qx::dao::on_before_delete((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + qx::dao::mongodb::QxMongoDB_Helper::deleteOne((&dao), dao.getDataMemberX()->getClass(), qx::serialization::json::to_string(t, 1, "mongodb:only_id"), NULL); + if (!dao.isValid()) + { + return dao.error(); + } + qx::dao::on_after_delete((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + return dao.error(); + } +#endif // _QX_ENABLE_MONGODB + + QString sql = dao.builder().buildSql().getSqlQuery(); + if (!dao.getDataId() || sql.isEmpty()) + { + return dao.errEmpty(); + } + if (!dao.prepare(sql)) + { + return dao.errFailed(true); + } + + IxSqlGenerator *pSqlGenerator = dao.getSqlGenerator(); + if (pSqlGenerator) + { + pSqlGenerator->onBeforeDelete((&dao), (&t)); + } + qx::dao::on_before_delete((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + + { + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_read_instance); + qx::dao::detail::QxSqlQueryHelper_DeleteById::resolveInput(t, dao.query(), dao.builder()); + } + + if (!dao.exec(true)) + { + return dao.errFailed(); + } + if (pSqlGenerator) + { + pSqlGenerator->onAfterDelete((&dao), (&t)); + } + qx::dao::on_after_delete((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + + return dao.error(); + } + }; + + template + struct QxDao_DeleteById_Container + { + + static QSqlError deleteById(T &t, QSqlDatabase *pDatabase, bool bVerifySoftDelete, bool bUseExecBatch) + { + typedef typename qx::trait::generic_container::type_value_qx type_item; + + qx::IxSqlQueryBuilder *pBuilder = new qx::QxSqlQueryBuilder_DeleteById(); + pBuilder->init(); + qx::QxSoftDelete oSoftDelete = pBuilder->getSoftDelete(); + if (bVerifySoftDelete && !oSoftDelete.isEmpty()) + { + delete pBuilder; + pBuilder = new qx::QxSqlQueryBuilder_SoftDeleteById(); + } + + if (qx::trait::generic_container::size(t) <= 0) + { + return QSqlError(); + } + qx::dao::detail::QxDao_Helper_Container dao(t, pDatabase, "delete by id", pBuilder); + if (!dao.isValid()) + { + return dao.error(); + } + if (dao.isReadOnly()) + { + return dao.errReadOnly(); + } + dao.setUseExecBatch(bUseExecBatch); + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!deleteItem((*it), dao)) + { + return dao.error(); + } + } + QStringList &itemsAsJson = dao.itemsAsJson(); + qx::dao::mongodb::QxMongoDB_Helper::deleteMany((&dao), dao.getDataMemberX()->getClass(), itemsAsJson, NULL); + if (!dao.isValid()) + { + return dao.error(); + } + dao.qxQuery().queryAt(2, ""); + dao.itemsAsJson().clear(); + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!deleteItem((*it), dao)) + { + return dao.error(); + } + } + return dao.error(); + } +#endif // _QX_ENABLE_MONGODB + + QString sql = dao.builder().buildSql().getSqlQuery(); + if (sql.isEmpty()) + { + return dao.errEmpty(); + } + if (!dao.prepare(sql)) + { + return dao.errFailed(true); + } + + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!deleteItem((*it), dao)) + { + return dao.error(); + } + } + + if (bUseExecBatch && (!dao.exec())) + { + return dao.errFailed(); + } + return dao.error(); + } + + private: + template + static inline bool deleteItem(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return deleteItem_Helper < U, std::is_pointer::value || qx::trait::is_smart_ptr::value > ::deleteById(item, dao); + } + + template + struct deleteItem_Helper + { + static inline bool deleteById(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return (item ? qx::dao::detail::QxDao_DeleteById_Container::deleteItem((*item), dao) : true); + } + }; + + template + struct deleteItem_Helper, false> + { + static inline bool deleteById(std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_DeleteById_Container::deleteItem(item.second, dao); + } + }; + + template + struct deleteItem_Helper, false> + { + static inline bool deleteById(const std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_DeleteById_Container::deleteItem(item.second, dao); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct deleteItem_Helper, false> + { + static inline bool deleteById(QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_DeleteById_Container::deleteItem(item.second, dao); + } + }; + + template + struct deleteItem_Helper, false> + { + static inline bool deleteById(const QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_DeleteById_Container::deleteItem(item.second, dao); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct deleteItem_Helper + { + static bool deleteById(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + if (!dao.isValidPrimaryKey(item)) + { + dao.errInvalidId(); + return false; + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + if (dao.qxQuery().queryAt(2) == "") + { + qx::dao::on_after_delete((&item), (&dao)); + return dao.isValid(); + } + qx::dao::on_before_delete((&item), (&dao)); + if (!dao.isValid()) + { + return false; + } + QVariant id = (dao.getDataId() ? dao.getDataId()->toVariant(&item) : QVariant()); + if (!id.isNull() && !id.toString().isEmpty()) + { + dao.itemsAsJson().append(id.toString()); + } + return dao.isValid(); + } +#endif // _QX_ENABLE_MONGODB + + IxSqlGenerator *pSqlGenerator = dao.getSqlGenerator(); + if (pSqlGenerator) + { + pSqlGenerator->onBeforeDelete((&dao), (&item)); + } + qx::dao::on_before_delete((&item), (&dao)); + if (!dao.isValid()) + { + return false; + } + + { + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_read_instance); + qx::dao::detail::QxSqlQueryHelper_DeleteById::resolveInput(item, dao.query(), dao.builder()); + } + + if (dao.getUseExecBatch()) + { + return dao.isValid(); + } + if (!dao.exec(true)) + { + dao.errFailed(); + return false; + } + if (pSqlGenerator) + { + pSqlGenerator->onAfterDelete((&dao), (&item)); + } + qx::dao::on_after_delete((&item), (&dao)); + if (!dao.isValid()) + { + return false; + } + + return dao.isValid(); + } + }; + }; + + template + struct QxDao_DeleteById_Ptr + { + + static inline QSqlError deleteById(T &t, QSqlDatabase *pDatabase, bool bVerifySoftDelete, bool bUseExecBatch) + { + if (!t) + { + return QSqlError(); + } + if (bVerifySoftDelete) + { + return qx::dao::delete_by_id((*t), pDatabase, bUseExecBatch); + } + return qx::dao::destroy_by_id((*t), pDatabase, bUseExecBatch); + } + }; + + template + struct QxDao_DeleteById + { + + static inline QSqlError deleteById(T &t, QSqlDatabase *pDatabase, bool bVerifySoftDelete, bool bUseExecBatch = false) + { + typedef typename std::conditional::value, qx::dao::detail::QxDao_DeleteById_Ptr, qx::dao::detail::QxDao_DeleteById_Generic>::type type_dao_1; + typedef typename std::conditional::value, qx::dao::detail::QxDao_DeleteById_Ptr, type_dao_1>::type type_dao_2; + typedef typename std::conditional::value, qx::dao::detail::QxDao_DeleteById_Container, type_dao_2>::type type_dao_3; + return type_dao_3::deleteById(t, pDatabase, bVerifySoftDelete, bUseExecBatch); + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxDao_ExecuteQuery.inl b/inl/QxDao/QxDao_ExecuteQuery.inl new file mode 100644 index 0000000..ebb202e --- /dev/null +++ b/inl/QxDao/QxDao_ExecuteQuery.inl @@ -0,0 +1,295 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_ExecuteQuery_Generic + { + + static QSqlError executeQuery(qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase) + { + qx::dao::detail::QxDao_Helper dao(t, pDatabase, "execute custom sql query or stored procedure", new qx::QxSqlQueryBuilder_Count()); + if (!dao.isValid()) + { + return dao.error(); + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + qx::dao::on_before_fetch((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + qx::dao::mongodb::QxMongoDB_Helper::executeCommand((&dao), dao.getDataMemberX()->getClass(), (&query)); + if (!dao.isValid()) + { + return dao.error(); + } + QString json = query.response().toString(); + qx::serialization::json::from_string(t, json, 1, "mongodb"); + qx::dao::on_after_fetch((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + return dao.error(); + } +#endif // _QX_ENABLE_MONGODB + + QString sql = query.query(); + if (sql.isEmpty()) + { + return dao.errEmpty(); + } + dao.builder().setSqlQuery(sql); + if (!dao.prepare(sql)) + { + return dao.errFailed(true); + } + query.resolve(dao.query()); + if (!dao.exec(true)) + { + return dao.errFailed(); + } + query.resolveOutput(dao.query(), false); + if (!dao.nextRecord()) + { + return dao.error(); + } + qx::dao::on_before_fetch((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + + qx::IxDataMemberX *pDataMemberX = dao.builder().getDataMemberX(); + if (!pDataMemberX) + { + qAssert(false); + return dao.error(); + } + QSqlRecord record = dao.query().record(); + for (int i = 0; i < record.count(); i++) + { + if (!pDataMemberX->exist_WithDaoStrategy(record.fieldName(i))) + { + continue; + } + qx::IxDataMember *pDataMember = pDataMemberX->get_WithDaoStrategy(record.fieldName(i)); + if (pDataMember) + { + pDataMember->fromVariant((&t), record.value(i), -1, qx::cvt::context::e_database); + } + } + + qx::dao::on_after_fetch((&t), (&dao)); + return dao.error(); + } + }; + + template + struct QxDao_ExecuteQuery_Container + { + + static QSqlError executeQuery(qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase) + { + typedef typename qx::trait::generic_container::type_value_qx type_item; + + qx::trait::generic_container::clear(t); + qx::IxSqlQueryBuilder *pBuilder = new qx::QxSqlQueryBuilder_Count(); + qx::dao::detail::QxDao_Helper_Container dao(t, pDatabase, "execute custom sql query or stored procedure", pBuilder); + if (!dao.isValid()) + { + return dao.error(); + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + qx::dao::mongodb::QxMongoDB_Helper::executeCommand((&dao), dao.getDataMemberX()->getClass(), (&query)); + if (!dao.isValid()) + { + return dao.error(); + } + QString json = query.response().toString(); + qx::serialization::json::from_string(t, json, 1, "mongodb"); + return dao.error(); + } +#endif // _QX_ENABLE_MONGODB + + QString sql = query.query(); + if (sql.isEmpty()) + { + return dao.errEmpty(); + } + dao.builder().setSqlQuery(sql); + if (!dao.prepare(sql)) + { + return dao.errFailed(true); + } + query.resolve(dao.query()); + if (!dao.exec(true)) + { + return dao.errFailed(); + } + query.resolveOutput(dao.query(), false); + + QVector> vColumnToFetch; + bool bSize = (dao.hasFeature(QSqlDriver::QuerySize) && (dao.query().size() > 0)); + if (bSize) + { + qx::trait::generic_container::reserve(t, dao.query().size()); + } + while (dao.nextRecord()) + { + insertNewItem(t, dao, vColumnToFetch); + if (!dao.isValid()) + { + return dao.error(); + } + } + if (bSize) + { + qAssert(qx::trait::generic_container::size(t) == static_cast(dao.query().size())); + } + + return dao.error(); + } + + private: + static void insertNewItem(T &t, qx::dao::detail::QxDao_Helper_Container &dao, QVector> &vColumnToFetch) + { + typedef typename qx::trait::generic_container::type_item type_item; + typedef typename type_item::type_value_qx type_value_qx; + + type_item item = qx::trait::generic_container::createItem(); + type_value_qx &item_val = item.value_qx(); + qx::IxDataMember *pId = dao.getDataId(); + QVariant vId; + qx::dao::on_before_fetch((&item_val), (&dao)); + if (!dao.isValid()) + { + return; + } + + if (vColumnToFetch.count() <= 0) + { + qx::IxDataMemberX *pDataMemberX = dao.builder().getDataMemberX(); + if (!pDataMemberX) + { + qAssert(false); + return; + } + QSqlRecord record = dao.query().record(); + vColumnToFetch.reserve(record.count()); + for (int i = 0; i < record.count(); i++) + { + if (!pDataMemberX->exist_WithDaoStrategy(record.fieldName(i))) + { + continue; + } + qx::IxDataMember *pDataMember = pDataMemberX->get_WithDaoStrategy(record.fieldName(i)); + if (pDataMember) + { + vColumnToFetch.append(qMakePair(i, pDataMember)); + } + } + } + + for (int j = 0; j < vColumnToFetch.count(); j++) + { + QVariant vValue = dao.query().value(vColumnToFetch[j].first); + vColumnToFetch[j].second->fromVariant((&item_val), vValue, -1, qx::cvt::context::e_database); + if (pId == vColumnToFetch[j].second) + { + vId = vValue; + } + } + + if (!vId.isValid()) + { + vId = QVariant(static_cast(qx::trait::generic_container::size(t))); + } + qx::cvt::from_variant(vId, item.key(), "", -1, qx::cvt::context::e_database); + qx::dao::on_after_fetch((&item_val), (&dao)); + if (!dao.isValid()) + { + return; + } + qx::dao::detail::QxDao_Keep_Original::backup(item); + qx::trait::generic_container::insertItem(t, item); + } + }; + + template + struct QxDao_ExecuteQuery_Ptr + { + + static inline QSqlError executeQuery(qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase) + { + if (!t) + { + qx::trait::construct_ptr::get(t); + }; + return qx::dao::execute_query(query, (*t), pDatabase); + } + }; + + template + struct QxDao_ExecuteQuery + { + + static inline QSqlError executeQuery(qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase) + { + typedef typename std::conditional::value, qx::dao::detail::QxDao_ExecuteQuery_Ptr, qx::dao::detail::QxDao_ExecuteQuery_Generic>::type type_dao_1; + typedef typename std::conditional::value, qx::dao::detail::QxDao_ExecuteQuery_Ptr, type_dao_1>::type type_dao_2; + typedef typename std::conditional::value, qx::dao::detail::QxDao_ExecuteQuery_Container, type_dao_2>::type type_dao_3; + + QSqlError error = type_dao_3::executeQuery(query, t, pDatabase); + if (!error.isValid()) + { + qx::dao::detail::QxDao_Keep_Original::backup(t); + } + return error; + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxDao_Exist.inl b/inl/QxDao/QxDao_Exist.inl new file mode 100644 index 0000000..5a7a98a --- /dev/null +++ b/inl/QxDao/QxDao_Exist.inl @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_Exist_Generic + { + + static qx_bool exist(T &t, QSqlDatabase *pDatabase) + { + qx::dao::detail::QxDao_Helper dao(t, pDatabase, "exist", new qx::QxSqlQueryBuilder_Exist()); + if (!dao.isValid()) + { + return qx_bool(false); + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + long cnt = 0; + QString json = qx::serialization::json::to_string(t, 1, "mongodb:only_id"); + qx_query query(json); + qx::dao::mongodb::QxMongoDB_Helper::count((&dao), dao.getDataMemberX()->getClass(), cnt, (&query)); + if (!dao.isValid()) + { + return qx_bool(false); + } + return qx_bool(cnt >= 1); + } +#endif // _QX_ENABLE_MONGODB + + QString sql = dao.builder().buildSql().getSqlQuery(); + if (!dao.getDataId() || sql.isEmpty()) + { + dao.errEmpty(); + return qx_bool(false); + } + if (!dao.prepare(sql)) + { + dao.errFailed(true); + return qx_bool(false); + } + + { + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_read_instance); + qx::dao::detail::QxSqlQueryHelper_Exist::resolveInput(t, dao.query(), dao.builder()); + } + + if (!dao.exec(true)) + { + dao.errFailed(); + return qx_bool(false); + } + return qx_bool(dao.nextRecord()); + } + }; + + template + struct QxDao_Exist_Container + { + + static qx_bool exist(T &t, QSqlDatabase *pDatabase) + { + typedef typename qx::trait::generic_container::type_value_qx type_item; + + if (qx::trait::generic_container::size(t) <= 0) + { + return qx_bool(false); + } + qx::dao::detail::QxDao_Helper_Container dao(t, pDatabase, "exist", new qx::QxSqlQueryBuilder_Exist()); + if (!dao.isValid()) + { + return qx_bool(false); + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!existItem((*it), dao)) + { + return qx_bool(false); + } + } + return qx_bool(true); + } +#endif // _QX_ENABLE_MONGODB + + QString sql = dao.builder().buildSql().getSqlQuery(); + if (sql.isEmpty()) + { + dao.errEmpty(); + return qx_bool(false); + } + if (!dao.prepare(sql)) + { + dao.errFailed(true); + return qx_bool(false); + } + + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!existItem((*it), dao)) + { + return qx_bool(false); + } + } + + return qx_bool(true); + } + + private: + template + static inline bool existItem(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return existItem_Helper < U, std::is_pointer::value || qx::trait::is_smart_ptr::value > ::exist(item, dao); + } + + template + struct existItem_Helper + { + static inline bool exist(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return (item ? qx::dao::detail::QxDao_Exist_Container::existItem((*item), dao) : false); + } + }; + + template + struct existItem_Helper, false> + { + static inline bool exist(std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Exist_Container::existItem(item.second, dao); + } + }; + + template + struct existItem_Helper, false> + { + static inline bool exist(const std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Exist_Container::existItem(item.second, dao); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct existItem_Helper, false> + { + static inline bool exist(QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Exist_Container::existItem(item.second, dao); + } + }; + + template + struct existItem_Helper, false> + { + static inline bool exist(const QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Exist_Container::existItem(item.second, dao); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct existItem_Helper + { + static bool exist(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + long cnt = 0; + QString json = qx::serialization::json::to_string(item, 1, "mongodb:only_id"); + qx_query query(json); + qx::dao::mongodb::QxMongoDB_Helper::count((&dao), dao.getDataMemberX()->getClass(), cnt, (&query)); + if (!dao.isValid()) + { + return false; + } + return (cnt >= 1); + } +#endif // _QX_ENABLE_MONGODB + + { + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_read_instance); + qx::dao::detail::QxSqlQueryHelper_Exist::resolveInput(item, dao.query(), dao.builder()); + } + + if (!dao.exec(true)) + { + dao.errFailed(); + return false; + } + return dao.nextRecord(); + } + }; + }; + + template + struct QxDao_Exist_Ptr + { + + static inline qx_bool exist(T &t, QSqlDatabase *pDatabase) + { + return (t ? qx::dao::exist((*t), pDatabase) : qx_bool(false)); + } + }; + + template + struct QxDao_Exist + { + + static inline qx_bool exist(T &t, QSqlDatabase *pDatabase) + { + typedef typename std::conditional::value, qx::dao::detail::QxDao_Exist_Ptr, qx::dao::detail::QxDao_Exist_Generic>::type type_dao_1; + typedef typename std::conditional::value, qx::dao::detail::QxDao_Exist_Ptr, type_dao_1>::type type_dao_2; + typedef typename std::conditional::value, qx::dao::detail::QxDao_Exist_Container, type_dao_2>::type type_dao_3; + return type_dao_3::exist(t, pDatabase); + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxDao_FetchAll.inl b/inl/QxDao/QxDao_FetchAll.inl new file mode 100644 index 0000000..503882c --- /dev/null +++ b/inl/QxDao/QxDao_FetchAll.inl @@ -0,0 +1,291 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_FetchAll_Generic + { + + static QSqlError fetchAll(const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase, const QStringList &columns) + { + qx::dao::detail::QxDao_Helper dao(t, pDatabase, "fetch all", new qx::QxSqlQueryBuilder_FetchAll(), (&query)); + if (!dao.isValid()) + { + return dao.error(); + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + dao.setSqlColumns(columns); + qx::dao::on_before_fetch((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + QString json = qx::serialization::json::to_string(t, 1, "mongodb:only_id"); + qx::dao::mongodb::QxMongoDB_Helper::findOne((&dao), dao.getDataMemberX()->getClass(), json, (&query)); + if (!dao.isValid()) + { + return dao.error(); + } + + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + QString ctx = QString("mongodb") + ((columns.count() > 0) ? (QString(":columns{,") + columns.join(",") + QString(",}")) : QString()); + qx::serialization::json::from_string(t, json, 1, ctx); + qx::dao::on_after_fetch((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + return dao.error(); + } +#endif // _QX_ENABLE_MONGODB + + QString sql = dao.builder().buildSql(columns).getSqlQuery(); + if (sql.isEmpty()) + { + return dao.errEmpty(); + } + if (!query.isEmpty()) + { + dao.addQuery(true); + sql = dao.builder().getSqlQuery(); + } + if (!dao.exec()) + { + return dao.errFailed(); + } + + if (dao.nextRecord()) + { + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + qx::dao::on_before_fetch((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + qx::dao::detail::QxSqlQueryHelper_FetchAll::resolveOutput(t, dao.query(), dao.builder(), columns); + qx::dao::on_after_fetch((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + } + + return dao.error(); + } + }; + +#ifdef _QX_ENABLE_MONGODB + template + struct QxDao_FetchAll_MongoDB_Fetcher : public qx::dao::mongodb::QxMongoDB_Fetcher + { + + T &m_t; + qx::dao::detail::QxDao_Helper_Container &m_dao; + + QxDao_FetchAll_MongoDB_Fetcher(T &t, qx::dao::detail::QxDao_Helper_Container &dao) : qx::dao::mongodb::QxMongoDB_Fetcher(), m_t(t), m_dao(dao) { ; } + virtual ~QxDao_FetchAll_MongoDB_Fetcher() { ; } + + virtual void fetch(const QString &json) + { + typedef typename qx::trait::generic_container::type_item type_item; + typedef typename type_item::type_value_qx type_value_qx; + type_item item = qx::trait::generic_container::createItem(); + type_value_qx &item_val = item.value_qx(); + qx::IxDataMember *pId = m_dao.getDataId(); + QStringList columns = m_dao.getSqlColumns(); + + if (json.isEmpty()) + { + return; + } + qx::dao::detail::IxDao_Timer timer((&m_dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + qx::dao::on_before_fetch((&item_val), (&m_dao)); + if (!m_dao.isValid()) + { + return; + } + QString ctx = QString("mongodb") + ((columns.count() > 0) ? (QString(":columns{,") + columns.join(",") + QString(",}")) : QString()); + qx::serialization::json::from_string(item_val, json, 1, ctx); + for (int i = 0; i < (pId ? pId->getNameCount() : 0); i++) + { + QVariant id = pId->toVariant((&item_val), "", i, qx::cvt::context::e_database); + qx::cvt::from_variant(id, item.key(), "", i, qx::cvt::context::e_database); + } + qx::dao::on_after_fetch((&item_val), (&m_dao)); + if (!m_dao.isValid()) + { + return; + } + qx::dao::detail::QxDao_Keep_Original::backup(item); + qx::trait::generic_container::insertItem(m_t, item); + } + }; +#endif // _QX_ENABLE_MONGODB + + template + struct QxDao_FetchAll_Container + { + + static QSqlError fetchAll(const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase, const QStringList &columns) + { + typedef typename qx::trait::generic_container::type_value_qx type_item; + + qx::trait::generic_container::clear(t); + qx::dao::detail::QxDao_Helper_Container dao(t, pDatabase, "fetch all", new qx::QxSqlQueryBuilder_FetchAll(), (&query)); + if (!dao.isValid()) + { + return dao.error(); + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + dao.setSqlColumns(columns); + QStringList &itemsAsJson = dao.itemsAsJson(); + QxDao_FetchAll_MongoDB_Fetcher fetcher(t, dao); + qx::dao::mongodb::QxMongoDB_Helper::findMany((&dao), dao.getDataMemberX()->getClass(), itemsAsJson, (&query), (&fetcher)); + return dao.error(); + } +#endif // _QX_ENABLE_MONGODB + + QString sql = dao.builder().buildSql(columns).getSqlQuery(); + if (sql.isEmpty()) + { + return dao.errEmpty(); + } + if (!query.isEmpty()) + { + dao.addQuery(true); + sql = dao.builder().getSqlQuery(); + } + if (!dao.exec()) + { + return dao.errFailed(); + } + dao.setSqlColumns(columns); + + bool bSize = (dao.hasFeature(QSqlDriver::QuerySize) && (dao.query().size() > 0)); + if (bSize) + { + qx::trait::generic_container::reserve(t, dao.query().size()); + } + while (dao.nextRecord()) + { + insertNewItem(t, dao); + if (!dao.isValid()) + { + return dao.error(); + } + } + if (bSize) + { + qAssert(qx::trait::generic_container::size(t) == static_cast(dao.query().size())); + } + + return dao.error(); + } + + private: + static void insertNewItem(T &t, qx::dao::detail::QxDao_Helper_Container &dao) + { + typedef typename qx::trait::generic_container::type_item type_item; + typedef typename type_item::type_value_qx type_value_qx; + type_item item = qx::trait::generic_container::createItem(); + type_value_qx &item_val = item.value_qx(); + qx::IxDataMember *pId = dao.getDataId(); + QStringList columns = dao.getSqlColumns(); + + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + if (pId) + { + for (int i = 0; i < pId->getNameCount(); i++) + { + QVariant v = dao.getIdFromQuery(i); + qx::cvt::from_variant(v, item.key(), "", i, qx::cvt::context::e_database); + } + } + qx::dao::on_before_fetch((&item_val), (&dao)); + if (!dao.isValid()) + { + return; + } + qx::dao::detail::QxSqlQueryHelper_FetchAll::resolveOutput(item_val, dao.query(), dao.builder(), columns); + qx::dao::on_after_fetch((&item_val), (&dao)); + if (!dao.isValid()) + { + return; + } + qx::dao::detail::QxDao_Keep_Original::backup(item); + qx::trait::generic_container::insertItem(t, item); + } + }; + + template + struct QxDao_FetchAll_Ptr + { + + static inline QSqlError fetchAll(const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase, const QStringList &columns) + { + return (t ? qx::dao::fetch_by_query(query, (*t), pDatabase, columns) : QSqlError()); + } + }; + + template + struct QxDao_FetchAll + { + + static inline QSqlError fetchAll(const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase, const QStringList &columns) + { + typedef typename std::conditional::value, qx::dao::detail::QxDao_FetchAll_Ptr, qx::dao::detail::QxDao_FetchAll_Generic>::type type_dao_1; + typedef typename std::conditional::value, qx::dao::detail::QxDao_FetchAll_Ptr, type_dao_1>::type type_dao_2; + typedef typename std::conditional::value, qx::dao::detail::QxDao_FetchAll_Container, type_dao_2>::type type_dao_3; + + QSqlError error = type_dao_3::fetchAll(query, t, pDatabase, columns); + if (!error.isValid()) + { + qx::dao::detail::QxDao_Keep_Original::backup(t); + } + return error; + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxDao_FetchAll_WithRelation.inl b/inl/QxDao/QxDao_FetchAll_WithRelation.inl new file mode 100644 index 0000000..5e27c3e --- /dev/null +++ b/inl/QxDao/QxDao_FetchAll_WithRelation.inl @@ -0,0 +1,451 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_FetchAll_WithRelation_Generic + { + + typedef qx::dao::detail::QxDao_Helper type_dao_helper; + typedef qx::dao::detail::QxSqlQueryHelper_FetchAll_WithRelation type_query_helper; + + static QSqlError fetchAll(const QStringList &relation, const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase) + { + type_dao_helper dao(t, pDatabase, "fetch all with relation", new qx::QxSqlQueryBuilder_FetchAll_WithRelation(), (&query)); + if (!dao.isValid()) + { + return dao.error(); + } + if (!dao.updateSqlRelationX(relation)) + { + return dao.errInvalidRelation(); + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + qx::dao::on_before_fetch((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + QString json = qx::serialization::json::to_string(t, 1, "mongodb:only_id"); + qx::dao::mongodb::QxMongoDB_Helper::findOne((&dao), dao.getDataMemberX()->getClass(), json, (&query)); + if (!dao.isValid()) + { + return dao.error(); + } + + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + qx::serialization::json::from_string(t, json, 1, "mongodb"); + qx::dao::on_after_fetch((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + return dao.error(); + } +#endif // _QX_ENABLE_MONGODB + + QStringList columns; + QString sql = dao.builder().buildSql(columns, dao.getSqlRelationLinked()).getSqlQuery(); + if (sql.isEmpty()) + { + return dao.errEmpty(); + } + if (!query.isEmpty()) + { + dao.addQuery(true); + sql = dao.builder().getSqlQuery(); + } + if (!dao.exec()) + { + return dao.errFailed(); + } + + { + qx::dao::on_before_fetch((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + if (dao.getCartesianProduct()) + { + fetchAll_Complex(t, dao); + } + else + { + fetchAll_Simple(t, dao); + } + if (!dao.isValid()) + { + return dao.error(); + } + qx::dao::on_after_fetch((&t), (&dao)); + } + + return dao.error(); + } + + private: + static inline void fetchAll_Simple(T &t, type_dao_helper &dao) + { + if (!dao.nextRecord()) + { + return; + } + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + type_query_helper::resolveOutput(dao.getSqlRelationLinked(), t, dao.query(), dao.builder()); + } + + static inline void fetchAll_Complex(T &t, type_dao_helper &dao) + { + while (dao.nextRecord()) + { + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + type_query_helper::resolveOutput(dao.getSqlRelationLinked(), t, dao.query(), dao.builder()); + if (!dao.isValid()) + { + return; + } + } + } + }; + +#ifdef _QX_ENABLE_MONGODB + template + struct QxDao_FetchAll_WithRelation_MongoDB_Fetcher : public qx::dao::mongodb::QxMongoDB_Fetcher + { + + typedef qx::dao::detail::QxDao_Helper_Container type_dao_helper; + typedef qx::trait::generic_container type_generic_container; + typedef typename type_generic_container::type_item type_item; + typedef typename type_item::type_value_qx type_value_qx; + typedef typename type_item::type_value type_value; + + T &m_t; + type_dao_helper &m_dao; + + QxDao_FetchAll_WithRelation_MongoDB_Fetcher(T &t, type_dao_helper &dao) : qx::dao::mongodb::QxMongoDB_Fetcher(), m_t(t), m_dao(dao) { ; } + virtual ~QxDao_FetchAll_WithRelation_MongoDB_Fetcher() { ; } + + virtual void fetch(const QString &json) + { + fetcherHelper::insertNewItem(m_t, m_dao, json); + } + + private: + template + struct fetcherHelper + { + static void insertNewItem(T &t, type_dao_helper &dao, const QString &json) + { + type_item item = type_generic_container::createItem(); + qx::IxDataMember *pId = dao.getDataId(); + qAssert(pId); + + if (json.isEmpty()) + { + return; + } + type_value_qx &item_val_tmp = item.value_qx(); + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + qx::serialization::json::from_string(item_val_tmp, json, 1, "mongodb:columns{,_id," + pId->getKey() + ",}"); + for (int i = 0; i < (pId ? pId->getNameCount() : 0); i++) + { + QVariant id = pId->toVariant((&item_val_tmp), "", i, qx::cvt::context::e_database); + qx::cvt::from_variant(id, item.key(), "", i, qx::cvt::context::e_database); + } + type_value *pValue = type_generic_container::insertItem(t, item); + type_value_qx &item_val = (pValue ? (*static_cast(pValue)) : item.value_qx()); + qx::dao::on_before_fetch((&item_val), (&dao)); + if (!dao.isValid()) + { + return; + } + qx::serialization::json::from_string(item_val, json, 1, "mongodb"); + qx::dao::on_after_fetch((&item_val), (&dao)); + if (!dao.isValid()) + { + return; + } + qx::dao::detail::QxDao_Keep_Original::backup(item); + } + }; + + template + struct fetcherHelper + { + static void insertNewItem(T &t, type_dao_helper &dao, const QString &json) + { + type_item item = type_generic_container::createItem(); + type_value_qx &item_val = item.value_qx(); + qx::IxDataMember *pId = dao.getDataId(); + qAssert(pId); + + if (json.isEmpty()) + { + return; + } + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + qx::dao::on_before_fetch((&item_val), (&dao)); + if (!dao.isValid()) + { + return; + } + qx::serialization::json::from_string(item_val, json, 1, "mongodb"); + for (int i = 0; i < (pId ? pId->getNameCount() : 0); i++) + { + QVariant id = pId->toVariant((&item_val), "", i, qx::cvt::context::e_database); + qx::cvt::from_variant(id, item.key(), "", i, qx::cvt::context::e_database); + } + qx::dao::on_after_fetch((&item_val), (&dao)); + if (!dao.isValid()) + { + return; + } + qx::dao::detail::QxDao_Keep_Original::backup(item); + qx::trait::generic_container::insertItem(t, item); + } + }; + }; +#endif // _QX_ENABLE_MONGODB + + template + struct QxDao_FetchAll_WithRelation_Container + { + + typedef qx::dao::detail::QxDao_Helper_Container type_dao_helper; + typedef qx::dao::detail::QxDao_FetchAll_WithRelation_Container type_this; + typedef qx::trait::generic_container type_generic_container; + typedef typename type_generic_container::type_item type_item; + typedef typename type_item::type_value_qx type_value_qx; + typedef typename type_item::type_value type_value; + typedef qx::dao::detail::QxSqlQueryHelper_FetchAll_WithRelation type_query_helper; + + static QSqlError fetchAll(const QStringList &relation, const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase) + { + type_generic_container::clear(t); + type_dao_helper dao(t, pDatabase, "fetch all with relation", new qx::QxSqlQueryBuilder_FetchAll_WithRelation(), (&query)); + if (!dao.isValid()) + { + return dao.error(); + } + if (!dao.updateSqlRelationX(relation)) + { + return dao.errInvalidRelation(); + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + QStringList &itemsAsJson = dao.itemsAsJson(); + QxDao_FetchAll_WithRelation_MongoDB_Fetcher fetcher(t, dao); + qx::dao::mongodb::QxMongoDB_Helper::findMany((&dao), dao.getDataMemberX()->getClass(), itemsAsJson, (&query), (&fetcher)); + return dao.error(); + } +#endif // _QX_ENABLE_MONGODB + + bool bComplex = dao.getCartesianProduct(); + QVariant vId; + QStringList columns; + QString sql = dao.builder().buildSql(columns, dao.getSqlRelationLinked()).getSqlQuery(); + if (sql.isEmpty()) + { + return dao.errEmpty(); + } + if (!query.isEmpty()) + { + dao.addQuery(true); + sql = dao.builder().getSqlQuery(); + } + if (!dao.exec()) + { + return dao.errFailed(); + } + bool bSize = (dao.hasFeature(QSqlDriver::QuerySize) && (dao.query().size() > 0)); + if (bSize) + { + type_generic_container::reserve(t, dao.query().size()); + } + + while (dao.nextRecord()) + { + if (!dao.isValid()) + { + return dao.error(); + } + vId = dao.getIdFromQuery(-1); + void *pItemTmp = (bComplex ? dao.builder().existIdX(0, vId, vId) : NULL); + if (!pItemTmp) + { + insertHelper::insertNewItem(t, dao); + continue; + } + type_value_qx *pItem = static_cast(pItemTmp); + type_query_helper::resolveOutput(dao.getSqlRelationLinked(), (*pItem), dao.query(), dao.builder()); + } + + if (bSize) + { + type_generic_container::reserve(t, type_generic_container::size(t)); + } + return dao.error(); + } + + private: + template + struct insertHelper + { + static void insertNewItem(T &t, type_dao_helper &dao) + { + type_item item = type_generic_container::createItem(); + qx::IxDataMember *pId = dao.getDataId(); + qAssert(pId); + + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + if (pId) + { + for (int i = 0; i < pId->getNameCount(); i++) + { + QVariant v = dao.getIdFromQuery(i); + qx::cvt::from_variant(v, item.key(), "", i, qx::cvt::context::e_database); + } + } + type_value *pValue = type_generic_container::insertItem(t, item); + type_value_qx &item_val = (pValue ? (*static_cast(pValue)) : item.value_qx()); + qx::dao::on_before_fetch((&item_val), (&dao)); + if (!dao.isValid()) + { + return; + } + type_query_helper::resolveOutput(dao.getSqlRelationLinked(), item_val, dao.query(), dao.builder()); + if (!dao.isValid()) + { + return; + } + qx::dao::on_after_fetch((&item_val), (&dao)); + if (!dao.isValid()) + { + return; + } + qx::dao::detail::QxDao_Keep_Original::backup(item); + } + }; + + template + struct insertHelper + { + static void insertNewItem(T &t, type_dao_helper &dao) + { + type_item item = type_generic_container::createItem(); + type_value_qx &item_val = item.value_qx(); + qx::IxDataMember *pId = dao.getDataId(); + qAssert(pId); + + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + if (pId) + { + for (int i = 0; i < pId->getNameCount(); i++) + { + QVariant v = dao.getIdFromQuery(i); + qx::cvt::from_variant(v, item.key(), "", i, qx::cvt::context::e_database); + } + } + qx::dao::on_before_fetch((&item_val), (&dao)); + if (!dao.isValid()) + { + return; + } + type_query_helper::resolveOutput(dao.getSqlRelationLinked(), item_val, dao.query(), dao.builder()); + if (!dao.isValid()) + { + return; + } + qx::dao::on_after_fetch((&item_val), (&dao)); + if (!dao.isValid()) + { + return; + } + type_generic_container::insertItem(t, item); + qx::dao::detail::QxDao_Keep_Original::backup(item); + } + }; + }; + + template + struct QxDao_FetchAll_WithRelation_Ptr + { + + static inline QSqlError fetchAll(const QStringList &relation, const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase) + { + return (t ? qx::dao::fetch_by_query_with_relation(relation, query, (*t), pDatabase) : QSqlError()); + } + }; + + template + struct QxDao_FetchAll_WithRelation + { + + static inline QSqlError fetchAll(const QString &relation, const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase) + { + QStringList lst; + if (!relation.isEmpty()) + { + lst = relation.split("|"); + } + return QxDao_FetchAll_WithRelation::fetchAll(lst, query, t, pDatabase); + } + + static inline QSqlError fetchAll(const QStringList &relation, const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase) + { + typedef typename std::conditional::value, qx::dao::detail::QxDao_FetchAll_WithRelation_Ptr, qx::dao::detail::QxDao_FetchAll_WithRelation_Generic>::type type_dao_1; + typedef typename std::conditional::value, qx::dao::detail::QxDao_FetchAll_WithRelation_Ptr, type_dao_1>::type type_dao_2; + typedef typename std::conditional::value, qx::dao::detail::QxDao_FetchAll_WithRelation_Container, type_dao_2>::type type_dao_3; + + QSqlError error = type_dao_3::fetchAll(relation, query, t, pDatabase); + if (!error.isValid()) + { + qx::dao::detail::QxDao_Keep_Original::backup(t); + } + return error; + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxDao_FetchById.inl b/inl/QxDao/QxDao_FetchById.inl new file mode 100644 index 0000000..6e5ec25 --- /dev/null +++ b/inl/QxDao/QxDao_FetchById.inl @@ -0,0 +1,363 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_FetchById_Generic + { + + static QSqlError fetchById(T &t, QSqlDatabase *pDatabase, const QStringList &columns) + { + qx::dao::detail::QxDao_Helper dao(t, pDatabase, "fetch by id", new qx::QxSqlQueryBuilder_FetchById()); + if (!dao.isValid()) + { + return dao.error(); + } + if (!dao.isValidPrimaryKey(t)) + { + return dao.errInvalidId(); + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + dao.setSqlColumns(columns); + qx::dao::on_before_fetch((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + QString json = qx::serialization::json::to_string(t, 1, "mongodb:only_id"); + qx::dao::mongodb::QxMongoDB_Helper::findOne((&dao), dao.getDataMemberX()->getClass(), json, NULL); + if (!dao.isValid()) + { + return dao.error(); + } + + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + QString ctx = QString("mongodb") + ((columns.count() > 0) ? (QString(":columns{,") + columns.join(",") + QString(",}")) : QString()); + qx::serialization::json::from_string(t, json, 1, ctx); + qx::dao::on_after_fetch((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + return dao.error(); + } +#endif // _QX_ENABLE_MONGODB + + QString sql = dao.builder().buildSql(columns).getSqlQuery(); + if (!dao.getDataId() || sql.isEmpty()) + { + return dao.errEmpty(); + } + if (!dao.prepare(sql)) + { + return dao.errFailed(true); + } + + { + qx::dao::on_before_fetch((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_read_instance); + qx::dao::detail::QxSqlQueryHelper_FetchById::resolveInput(t, dao.query(), dao.builder(), columns); + } + + if (!dao.exec(true)) + { + return dao.errFailed(); + } + if (!dao.nextRecord()) + { + return dao.errNoData(); + } + + { + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + qx::dao::detail::QxSqlQueryHelper_FetchById::resolveOutput(t, dao.query(), dao.builder(), columns); + qx::dao::on_after_fetch((&t), (&dao)); + } + + return dao.error(); + } + }; + + template + struct QxDao_FetchById_Container + { + + static QSqlError fetchById(T &t, QSqlDatabase *pDatabase, const QStringList &columns) + { + typedef typename qx::trait::generic_container::type_value_qx type_item; + + if (qx::trait::generic_container::size(t) <= 0) + { + return QSqlError(); + } + qx::dao::detail::QxDao_Helper_Container dao(t, pDatabase, "fetch by id", new qx::QxSqlQueryBuilder_FetchById()); + if (!dao.isValid()) + { + return dao.error(); + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + dao.setSqlColumns(columns); + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!fetchItem((*it), dao)) + { + return dao.error(); + } + } + QStringList &itemsAsJson = dao.itemsAsJson(); + qx::dao::mongodb::QxMongoDB_Helper::findMany((&dao), dao.getDataMemberX()->getClass(), itemsAsJson, NULL, NULL); + if (!dao.isValid()) + { + return dao.error(); + } + dao.qxQuery().queryAt(2, ""); + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!fetchItem((*it), dao)) + { + return dao.error(); + } + } + return dao.error(); + } +#endif // _QX_ENABLE_MONGODB + + QString sql = dao.builder().buildSql(columns).getSqlQuery(); + if (!dao.getDataId() || sql.isEmpty()) + { + return dao.errEmpty(); + } + if (!dao.prepare(sql)) + { + return dao.errFailed(true); + } + dao.setSqlColumns(columns); + + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!fetchItem((*it), dao)) + { + return dao.error(); + } + } + + return dao.error(); + } + + private: + template + static inline bool fetchItem(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + bool bFetchOk = fetchItem_Helper < U, std::is_pointer::value || qx::trait::is_smart_ptr::value > ::fetch(item, dao); + if (bFetchOk) + { + qx::dao::detail::QxDao_Keep_Original::backup(item); + } + return bFetchOk; + } + + template + struct fetchItem_Helper + { + static inline bool fetch(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return (item ? qx::dao::detail::QxDao_FetchById_Container::fetchItem((*item), dao) : true); + } + }; + + template + struct fetchItem_Helper, false> + { + static inline bool fetch(std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_FetchById_Container::fetchItem(item.second, dao); + } + }; + + template + struct fetchItem_Helper, false> + { + static inline bool fetch(const std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_FetchById_Container::fetchItem(item.second, dao); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct fetchItem_Helper, false> + { + static inline bool fetch(QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_FetchById_Container::fetchItem(item.second, dao); + } + }; + + template + struct fetchItem_Helper, false> + { + static inline bool fetch(const QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_FetchById_Container::fetchItem(item.second, dao); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct fetchItem_Helper + { + static bool fetch(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + QStringList columns = dao.getSqlColumns(); + if (!dao.isValidPrimaryKey(item)) + { + dao.errInvalidId(); + return false; + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + if (dao.qxQuery().queryAt(2) == "") + { + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + QString ctx = QString("mongodb") + ((columns.count() > 0) ? (QString(":columns{,") + columns.join(",") + QString(",}")) : QString()); + if (!dao.itemsAsJson().isEmpty()) + { + QString json = dao.itemsAsJson().takeFirst(); + if (!json.isEmpty()) + { + qx::serialization::json::from_string(item, json, 1, ctx); + } + } + qx::dao::on_after_fetch((&item), (&dao)); + return dao.isValid(); + } + { + qx::dao::on_before_fetch((&item), (&dao)); + if (!dao.isValid()) + { + return false; + } + QVariant id = (dao.getDataId() ? dao.getDataId()->toVariant(&item) : QVariant()); + if (!id.isNull() && !id.toString().isEmpty()) + { + dao.itemsAsJson().append(id.toString()); + } + } + return dao.isValid(); + } +#endif // _QX_ENABLE_MONGODB + + { + qx::dao::on_before_fetch((&item), (&dao)); + if (!dao.isValid()) + { + return false; + } + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_read_instance); + qx::dao::detail::QxSqlQueryHelper_FetchById::resolveInput(item, dao.query(), dao.builder(), columns); + } + + if (!dao.exec(true)) + { + dao.errFailed(); + return false; + } + if (!dao.nextRecord()) + { + dao.errNoData(); + return false; + } + + { + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + qx::dao::detail::QxSqlQueryHelper_FetchById::resolveOutput(item, dao.query(), dao.builder(), columns); + qx::dao::on_after_fetch((&item), (&dao)); + } + + return dao.isValid(); + } + }; + }; + + template + struct QxDao_FetchById_Ptr + { + + static inline QSqlError fetchById(T &t, QSqlDatabase *pDatabase, const QStringList &columns) + { + if (!t) + { + qx::trait::construct_ptr::get(t); + }; + return qx::dao::fetch_by_id((*t), pDatabase, columns); + } + }; + + template + struct QxDao_FetchById + { + + static inline QSqlError fetchById(T &t, QSqlDatabase *pDatabase, const QStringList &columns) + { + typedef typename std::conditional::value, qx::dao::detail::QxDao_FetchById_Ptr, qx::dao::detail::QxDao_FetchById_Generic>::type type_dao_1; + typedef typename std::conditional::value, qx::dao::detail::QxDao_FetchById_Ptr, type_dao_1>::type type_dao_2; + typedef typename std::conditional::value, qx::dao::detail::QxDao_FetchById_Container, type_dao_2>::type type_dao_3; + + QSqlError error = type_dao_3::fetchById(t, pDatabase, columns); + if (!error.isValid()) + { + qx::dao::detail::QxDao_Keep_Original::backup(t); + } + return error; + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxDao_FetchById_WithRelation.inl b/inl/QxDao/QxDao_FetchById_WithRelation.inl new file mode 100644 index 0000000..cfcca0c --- /dev/null +++ b/inl/QxDao/QxDao_FetchById_WithRelation.inl @@ -0,0 +1,459 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_FetchById_WithRelation_Generic + { + + typedef qx::dao::detail::QxDao_Helper type_dao_helper; + typedef qx::dao::detail::QxSqlQueryHelper_FetchById_WithRelation type_query_helper; + + static QSqlError fetchById(const QStringList &relation, T &t, QSqlDatabase *pDatabase) + { + type_dao_helper dao(t, pDatabase, "fetch by id with relation", new qx::QxSqlQueryBuilder_FetchById_WithRelation()); + if (!dao.isValid()) + { + return dao.error(); + } + if (!dao.isValidPrimaryKey(t)) + { + return dao.errInvalidId(); + } + if (!dao.updateSqlRelationX(relation)) + { + return dao.errInvalidRelation(); + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + qx::dao::on_before_fetch((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + QString json = qx::serialization::json::to_string(t, 1, "mongodb:only_id"); + qx::dao::mongodb::QxMongoDB_Helper::findOne((&dao), dao.getDataMemberX()->getClass(), json, NULL); + if (!dao.isValid()) + { + return dao.error(); + } + + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + qx::serialization::json::from_string(t, json, 1, "mongodb"); + qx::dao::on_after_fetch((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + return dao.error(); + } +#endif // _QX_ENABLE_MONGODB + + QStringList columns; + QString sql = dao.builder().buildSql(columns, dao.getSqlRelationLinked()).getSqlQuery(); + if (!dao.getDataId() || sql.isEmpty()) + { + return dao.errEmpty(); + } + if (!dao.prepare(sql)) + { + return dao.errFailed(true); + } + + { + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_read_instance); + type_query_helper::resolveInput(dao.getSqlRelationLinked(), t, dao.query(), dao.builder()); + } + + if (!dao.exec(true)) + { + return dao.errFailed(); + } + + { + qx::dao::on_before_fetch((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + if (dao.getCartesianProduct()) + { + fetchById_Complex(t, dao); + } + else + { + fetchById_Simple(t, dao); + } + if (!dao.isValid()) + { + return dao.error(); + } + qx::dao::on_after_fetch((&t), (&dao)); + } + + return dao.error(); + } + + private: + static inline void fetchById_Simple(T &t, type_dao_helper &dao) + { + if (!dao.nextRecord()) + { + dao.errNoData(); + return; + } + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + type_query_helper::resolveOutput(dao.getSqlRelationLinked(), t, dao.query(), dao.builder()); + } + + static inline void fetchById_Complex(T &t, type_dao_helper &dao) + { + if (!dao.nextRecord()) + { + dao.errNoData(); + return; + } + do + { + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + type_query_helper::resolveOutput(dao.getSqlRelationLinked(), t, dao.query(), dao.builder()); + if (!dao.isValid()) + { + return; + } + } while (dao.nextRecord()); + } + }; + + template + struct QxDao_FetchById_WithRelation_Container + { + + typedef qx::dao::detail::QxDao_Helper_Container type_dao_helper; + typedef qx::dao::detail::QxDao_FetchById_WithRelation_Container type_this; + typedef qx::trait::generic_container type_generic_container; + typedef typename type_generic_container::type_item type_item; + typedef typename type_item::type_value_qx type_value_qx; + typedef typename type_item::type_value type_value; + + static QSqlError fetchById(const QStringList &relation, T &t, QSqlDatabase *pDatabase) + { + if (qx::trait::generic_container::size(t) <= 0) + { + return QSqlError(); + } + type_dao_helper dao(t, pDatabase, "fetch by id with relation", new qx::QxSqlQueryBuilder_FetchById_WithRelation()); + if (!dao.isValid()) + { + return dao.error(); + } + if (!dao.updateSqlRelationX(relation)) + { + return dao.errInvalidRelation(); + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!fetchItem((*it), dao)) + { + return dao.error(); + } + } + QStringList &itemsAsJson = dao.itemsAsJson(); + qx::dao::mongodb::QxMongoDB_Helper::findMany((&dao), dao.getDataMemberX()->getClass(), itemsAsJson, NULL, NULL); + if (!dao.isValid()) + { + return dao.error(); + } + dao.qxQuery().queryAt(2, ""); + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!fetchItem((*it), dao)) + { + return dao.error(); + } + } + return dao.error(); + } +#endif // _QX_ENABLE_MONGODB + + QStringList columns; + QString sql = dao.builder().buildSql(columns, dao.getSqlRelationLinked()).getSqlQuery(); + if (!dao.getDataId() || sql.isEmpty()) + { + return dao.errEmpty(); + } + if (!dao.prepare(sql)) + { + return dao.errFailed(true); + } + + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!fetchItem((*it), dao)) + { + return dao.error(); + } + } + + return dao.error(); + } + + private: + template + static inline bool fetchItem(U &item, type_dao_helper &dao) + { + bool bFetchOk = fetchItem_Helper < U, std::is_pointer::value || qx::trait::is_smart_ptr::value > ::fetch(item, dao); + if (bFetchOk) + { + qx::dao::detail::QxDao_Keep_Original::backup(item); + } + return bFetchOk; + } + + template + struct fetchItem_Helper + { + static inline bool fetch(U &item, type_dao_helper &dao) + { + return (item ? type_this::fetchItem((*item), dao) : true); + } + }; + + template + struct fetchItem_Helper, false> + { + static inline bool fetch(std::pair &item, type_dao_helper &dao) + { + return type_this::fetchItem(item.second, dao); + } + }; + + template + struct fetchItem_Helper, false> + { + static inline bool fetch(const std::pair &item, type_dao_helper &dao) + { + return type_this::fetchItem(item.second, dao); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct fetchItem_Helper, false> + { + static inline bool fetch(QPair &item, type_dao_helper &dao) + { + return type_this::fetchItem(item.second, dao); + } + }; + + template + struct fetchItem_Helper, false> + { + static inline bool fetch(const QPair &item, type_dao_helper &dao) + { + return type_this::fetchItem(item.second, dao); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct fetchItem_Helper + { + + typedef qx::dao::detail::QxSqlQueryHelper_FetchById_WithRelation type_query_helper; + + static bool fetch(U &item, type_dao_helper &dao) + { + if (!dao.isValidPrimaryKey(item)) + { + dao.errInvalidId(); + return false; + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + if (dao.qxQuery().queryAt(2) == "") + { + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + if (!dao.itemsAsJson().isEmpty()) + { + QString json = dao.itemsAsJson().takeFirst(); + if (!json.isEmpty()) + { + qx::serialization::json::from_string(item, json, 1, "mongodb"); + } + } + qx::dao::on_after_fetch((&item), (&dao)); + return dao.isValid(); + } + { + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + qx::dao::on_before_fetch((&item), (&dao)); + if (!dao.isValid()) + { + return false; + } + QVariant id = (dao.getDataId() ? dao.getDataId()->toVariant(&item) : QVariant()); + if (!id.isNull() && !id.toString().isEmpty()) + { + dao.itemsAsJson().append(id.toString()); + } + } + return dao.isValid(); + } +#endif // _QX_ENABLE_MONGODB + + { + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_read_instance); + type_query_helper::resolveInput(dao.getSqlRelationLinked(), item, dao.query(), dao.builder()); + } + + if (!dao.exec(true)) + { + dao.errFailed(); + return false; + } + + { + qx::dao::on_before_fetch((&item), (&dao)); + if (!dao.isValid()) + { + return false; + } + if (dao.getCartesianProduct()) + { + fetch_Complex(item, dao); + } + else + { + fetch_Simple(item, dao); + } + if (!dao.isValid()) + { + return false; + } + qx::dao::on_after_fetch((&item), (&dao)); + } + + return dao.isValid(); + } + + static inline void fetch_Simple(U &item, type_dao_helper &dao) + { + if (!dao.nextRecord()) + { + dao.errNoData(); + return; + } + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + type_query_helper::resolveOutput(dao.getSqlRelationLinked(), item, dao.query(), dao.builder()); + } + + static inline void fetch_Complex(U &item, type_dao_helper &dao) + { + if (!dao.nextRecord()) + { + dao.errNoData(); + return; + } + do + { + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_build_instance); + type_query_helper::resolveOutput(dao.getSqlRelationLinked(), item, dao.query(), dao.builder()); + if (!dao.isValid()) + { + return; + } + } while (dao.nextRecord()); + } + }; + }; + + template + struct QxDao_FetchById_WithRelation_Ptr + { + + static inline QSqlError fetchById(const QStringList &relation, T &t, QSqlDatabase *pDatabase) + { + if (!t) + { + qx::trait::construct_ptr::get(t); + }; + return qx::dao::fetch_by_id_with_relation(relation, (*t), pDatabase); + } + }; + + template + struct QxDao_FetchById_WithRelation + { + + static inline QSqlError fetchById(const QString &relation, T &t, QSqlDatabase *pDatabase) + { + QStringList lst; + if (!relation.isEmpty()) + { + lst = relation.split("|"); + } + return QxDao_FetchById_WithRelation::fetchById(lst, t, pDatabase); + } + + static inline QSqlError fetchById(const QStringList &relation, T &t, QSqlDatabase *pDatabase) + { + typedef typename std::conditional::value, qx::dao::detail::QxDao_FetchById_WithRelation_Ptr, qx::dao::detail::QxDao_FetchById_WithRelation_Generic>::type type_dao_1; + typedef typename std::conditional::value, qx::dao::detail::QxDao_FetchById_WithRelation_Ptr, type_dao_1>::type type_dao_2; + typedef typename std::conditional::value, qx::dao::detail::QxDao_FetchById_WithRelation_Container, type_dao_2>::type type_dao_3; + + QSqlError error = type_dao_3::fetchById(relation, t, pDatabase); + if (!error.isValid()) + { + qx::dao::detail::QxDao_Keep_Original::backup(t); + } + return error; + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxDao_Helper.inl b/inl/QxDao/QxDao_Helper.inl new file mode 100644 index 0000000..6330247 --- /dev/null +++ b/inl/QxDao/QxDao_Helper.inl @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + class QxDao_Helper : public IxDao_Helper + { + + public: + QxDao_Helper(T &t, QSqlDatabase *pDatabase, const QString &sContext, qx::IxSqlQueryBuilder *pBuilder, const qx::QxSqlQuery *pQuery = NULL) : IxDao_Helper(pBuilder, pQuery) + { + Q_UNUSED(t); + init(pDatabase, sContext); + } + virtual ~QxDao_Helper() { static_assert(qx::trait::is_qx_registered::type_sql>::value, "qx::trait::is_qx_registered::type_sql>::value"); } + }; + + template + struct QxDao_Keep_Original + { + static inline void backup(T &t) { Q_UNUSED(t); } + }; + + template + struct QxDao_Keep_Original> + { + static inline void backup(qx::dao::ptr &t) + { + if (t) + { + t.resetOriginal(qx::clone_to_qt_shared_ptr(*t)); + } + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx + +#include "../../inl/QxDao/QxDao_Helper_Container.inl" diff --git a/inl/QxDao/QxDao_Helper_Container.inl b/inl/QxDao/QxDao_Helper_Container.inl new file mode 100644 index 0000000..01b49c0 --- /dev/null +++ b/inl/QxDao/QxDao_Helper_Container.inl @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#define QX_DAO_HELPER_CONTAINER(className) \ + namespace qx \ + { \ + namespace dao \ + { \ + namespace detail \ + { \ + template \ + class QxDao_Helper_Container> : public IxDao_Helper \ + { \ + public: \ + QxDao_Helper_Container(className &t, QSqlDatabase *pDatabase, const QString &sContext, qx::IxSqlQueryBuilder *pBuilder, const qx::QxSqlQuery *pQuery = NULL) : IxDao_Helper(pBuilder, pQuery) \ + { \ + Q_UNUSED(t); \ + init(pDatabase, sContext); \ + } \ + virtual ~QxDao_Helper_Container() { static_assert(qx::trait::is_qx_registered::type_sql>::value, "qx::trait::is_qx_registered::type_sql>::value"); } \ + }; \ + } \ + } \ + } // namespace qx::dao::detail + +#define QX_DAO_HELPER_CONTAINER_KEY_VALUE(className) \ + namespace qx \ + { \ + namespace dao \ + { \ + namespace detail \ + { \ + template \ + class QxDao_Helper_Container> : public IxDao_Helper \ + { \ + public: \ + QxDao_Helper_Container(className &t, QSqlDatabase *pDatabase, const QString &sContext, qx::IxSqlQueryBuilder *pBuilder, const qx::QxSqlQuery *pQuery = NULL) : IxDao_Helper(pBuilder, pQuery) \ + { \ + Q_UNUSED(t); \ + init(pDatabase, sContext); \ + } \ + virtual ~QxDao_Helper_Container() { static_assert(qx::trait::is_qx_registered::type_sql>::value, "qx::trait::is_qx_registered::type_sql>::value"); } \ + }; \ + } \ + } \ + } // namespace qx::dao::detail + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_Helper_Container + { + ; + }; + + } // namespace detail + } // namespace dao +} // namespace qx + +QX_DAO_HELPER_CONTAINER(std::vector) +QX_DAO_HELPER_CONTAINER(std::list) +QX_DAO_HELPER_CONTAINER(std::set) +QX_DAO_HELPER_CONTAINER_KEY_VALUE(std::map) + +#ifdef _QX_ENABLE_BOOST + +QX_DAO_HELPER_CONTAINER(boost::unordered_set) +QX_DAO_HELPER_CONTAINER(boost::unordered_multiset) +QX_DAO_HELPER_CONTAINER_KEY_VALUE(boost::unordered_map) +QX_DAO_HELPER_CONTAINER_KEY_VALUE(boost::unordered_multimap) + +#endif // _QX_ENABLE_BOOST + +QX_DAO_HELPER_CONTAINER(std::unordered_set) +QX_DAO_HELPER_CONTAINER(std::unordered_multiset) +QX_DAO_HELPER_CONTAINER_KEY_VALUE(std::unordered_map) +QX_DAO_HELPER_CONTAINER_KEY_VALUE(std::unordered_multimap) + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_DAO_HELPER_CONTAINER(QVector) +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +QX_DAO_HELPER_CONTAINER(QList) +QX_DAO_HELPER_CONTAINER(QSet) +QX_DAO_HELPER_CONTAINER_KEY_VALUE(QMap) +QX_DAO_HELPER_CONTAINER_KEY_VALUE(QMultiMap) +QX_DAO_HELPER_CONTAINER_KEY_VALUE(QHash) +QX_DAO_HELPER_CONTAINER_KEY_VALUE(QMultiHash) + +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) +QX_DAO_HELPER_CONTAINER(QLinkedList) +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + +QX_DAO_HELPER_CONTAINER_KEY_VALUE(qx::QxCollection) diff --git a/inl/QxDao/QxDao_Insert.inl b/inl/QxDao/QxDao_Insert.inl new file mode 100644 index 0000000..bc17cc0 --- /dev/null +++ b/inl/QxDao/QxDao_Insert.inl @@ -0,0 +1,394 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_Insert_Generic + { + + static QSqlError insert(T &t, QSqlDatabase *pDatabase, bool bUseExecBatch) + { + Q_UNUSED(bUseExecBatch); // Useful only with containers + qx::dao::detail::QxDao_Helper dao(t, pDatabase, "insert", new qx::QxSqlQueryBuilder_Insert()); + if (!dao.isValid()) + { + return dao.error(); + } + if (dao.isReadOnly()) + { + return dao.errReadOnly(); + } + if (!dao.validateInstance(t)) + { + return dao.error(); + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + qx::dao::on_before_insert((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + QString insertedId; + qx::dao::mongodb::QxMongoDB_Helper::insertOne((&dao), dao.getDataMemberX()->getClass(), qx::serialization::json::to_string(t, 1, "mongodb"), insertedId); + if (!dao.isValid()) + { + return dao.error(); + } + if (!insertedId.isEmpty() && dao.getDataId()) + { + dao.getDataId()->fromVariant((&t), insertedId, -1, qx::cvt::context::e_database); + } + qx::dao::on_after_insert((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + return dao.error(); + } +#endif // _QX_ENABLE_MONGODB + + IxSqlGenerator *pSqlGenerator = dao.getSqlGenerator(); + QString sql = dao.builder().buildSql().getSqlQuery(); + if (sql.isEmpty()) + { + return dao.errEmpty(); + } + if (!pDatabase) + { + dao.transaction(); + } + if (pSqlGenerator) + { + pSqlGenerator->checkSqlInsert((&dao), sql); + } + if (!dao.prepare(sql)) + { + return dao.errFailed(true); + } + + if (pSqlGenerator) + { + pSqlGenerator->onBeforeInsert((&dao), (&t)); + } + qx::dao::on_before_insert((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + + { + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_read_instance); + qx::dao::detail::QxSqlQueryHelper_Insert::resolveInput(t, dao.query(), dao.builder()); + } + + if (!dao.exec(true)) + { + return dao.errFailed(); + } + dao.updateLastInsertId(t); + if (pSqlGenerator) + { + pSqlGenerator->onAfterInsert((&dao), (&t)); + } + qx::dao::on_after_insert((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + + return dao.error(); + } + }; + + template + struct QxDao_Insert_Container + { + + static QSqlError insert(T &t, QSqlDatabase *pDatabase, bool bUseExecBatch) + { + typedef typename qx::trait::generic_container::type_value_qx type_item; + + if (qx::trait::generic_container::size(t) <= 0) + { + return QSqlError(); + } + qx::dao::detail::QxDao_Helper_Container dao(t, pDatabase, "insert", new qx::QxSqlQueryBuilder_Insert()); + if (!dao.isValid()) + { + return dao.error(); + } + if (dao.isReadOnly()) + { + return dao.errReadOnly(); + } + if (!dao.validateInstance(t)) + { + return dao.error(); + } + dao.setUseExecBatch(bUseExecBatch); + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!insertItem((*it), dao)) + { + return dao.error(); + } + } + QStringList &itemsAsJson = dao.itemsAsJson(); + QStringList insertedId; + qx::dao::mongodb::QxMongoDB_Helper::insertMany((&dao), dao.getDataMemberX()->getClass(), itemsAsJson, insertedId); + if (!dao.isValid()) + { + return dao.error(); + } + dao.qxQuery().queryAt(2, ""); + dao.itemsAsJson().clear(); + dao.itemsAsJson().append(insertedId); + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!insertItem((*it), dao)) + { + return dao.error(); + } + } + return dao.error(); + } +#endif // _QX_ENABLE_MONGODB + + IxSqlGenerator *pSqlGenerator = dao.getSqlGenerator(); + QString sql = dao.builder().buildSql().getSqlQuery(); + if (sql.isEmpty()) + { + return dao.errEmpty(); + } + if (!pDatabase) + { + dao.transaction(); + } + if (pSqlGenerator) + { + pSqlGenerator->checkSqlInsert((&dao), sql); + } + if (!dao.prepare(sql)) + { + return dao.errFailed(true); + } + + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!insertItem((*it), dao)) + { + return dao.error(); + } + } + + if (bUseExecBatch && (!dao.exec())) + { + return dao.errFailed(); + } + return dao.error(); + } + + private: + template + static inline bool insertItem(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + bool bInsertOk = insertItem_Helper < U, std::is_pointer::value || qx::trait::is_smart_ptr::value > ::insert(item, dao); + if (bInsertOk) + { + qx::dao::detail::QxDao_Keep_Original::backup(item); + } + return bInsertOk; + } + + template + struct insertItem_Helper + { + static inline bool insert(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return (item ? qx::dao::detail::QxDao_Insert_Container::insertItem((*item), dao) : true); + } + }; + + template + struct insertItem_Helper, false> + { + static inline bool insert(std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Insert_Container::insertItem(item.second, dao); + } + }; + + template + struct insertItem_Helper, false> + { + static inline bool insert(const std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Insert_Container::insertItem(item.second, dao); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct insertItem_Helper, false> + { + static inline bool insert(QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Insert_Container::insertItem(item.second, dao); + } + }; + + template + struct insertItem_Helper, false> + { + static inline bool insert(const QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Insert_Container::insertItem(item.second, dao); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct insertItem_Helper + { + static bool insert(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + if (dao.qxQuery().queryAt(2) == "") + { + if (!dao.itemsAsJson().isEmpty()) + { + QString oid = dao.itemsAsJson().takeFirst(); + if (!oid.isEmpty() && dao.getDataId()) + { + dao.getDataId()->fromVariant((&item), oid, -1, qx::cvt::context::e_database); + } + } + qx::dao::on_after_insert((&item), (&dao)); + return dao.isValid(); + } + qx::dao::on_before_insert((&item), (&dao)); + if (!dao.isValid()) + { + return false; + } + dao.itemsAsJson().append(qx::serialization::json::to_string(item, 1, "mongodb")); + return dao.isValid(); + } +#endif // _QX_ENABLE_MONGODB + + IxSqlGenerator *pSqlGenerator = dao.getSqlGenerator(); + if (pSqlGenerator) + { + pSqlGenerator->onBeforeInsert((&dao), (&item)); + } + qx::dao::on_before_insert((&item), (&dao)); + if (!dao.isValid()) + { + return false; + } + + { + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_read_instance); + qx::dao::detail::QxSqlQueryHelper_Insert::resolveInput(item, dao.query(), dao.builder()); + } + + if (dao.getUseExecBatch()) + { + return dao.isValid(); + } + if (!dao.exec(true)) + { + dao.errFailed(); + return false; + } + dao.updateLastInsertId(item); + if (pSqlGenerator) + { + pSqlGenerator->onAfterInsert((&dao), (&item)); + } + qx::dao::on_after_insert((&item), (&dao)); + if (!dao.isValid()) + { + return false; + } + + return dao.isValid(); + } + }; + }; + + template + struct QxDao_Insert_Ptr + { + + static inline QSqlError insert(T &t, QSqlDatabase *pDatabase, bool bUseExecBatch) + { + return (t ? qx::dao::insert((*t), pDatabase, bUseExecBatch) : QSqlError()); + } + }; + + template + struct QxDao_Insert + { + + static inline QSqlError insert(T &t, QSqlDatabase *pDatabase, bool bUseExecBatch = false) + { + typedef typename std::conditional::value, qx::dao::detail::QxDao_Insert_Ptr, qx::dao::detail::QxDao_Insert_Generic>::type type_dao_1; + typedef typename std::conditional::value, qx::dao::detail::QxDao_Insert_Ptr, type_dao_1>::type type_dao_2; + typedef typename std::conditional::value, qx::dao::detail::QxDao_Insert_Container, type_dao_2>::type type_dao_3; + + QSqlError error = type_dao_3::insert(t, pDatabase, bUseExecBatch); + if (!error.isValid()) + { + qx::dao::detail::QxDao_Keep_Original::backup(t); + } + return error; + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxDao_Insert_WithRelation.inl b/inl/QxDao/QxDao_Insert_WithRelation.inl new file mode 100644 index 0000000..9bbb9f3 --- /dev/null +++ b/inl/QxDao/QxDao_Insert_WithRelation.inl @@ -0,0 +1,276 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_Insert_WithRelation_Generic + { + + static QSqlError insert(const QStringList &relation, T &t, QSqlDatabase *pDatabase) + { + qx::dao::detail::QxDao_Helper dao(t, pDatabase, "insert with relation", new qx::QxSqlQueryBuilder_Insert()); + if (!dao.isValid()) + { + return dao.error(); + } + if (dao.isReadOnly()) + { + return dao.errReadOnly(); + } + if (!dao.updateSqlRelationX(relation)) + { + return dao.errInvalidRelation(); + } + if (!pDatabase) + { + dao.transaction(); + } + dao.quiet(); + + qx::QxSqlRelationParams params(0, 0, NULL, (&dao.builder()), (&dao.query()), (&t)); + params.setDatabase((&dao.database())); + + qx::QxSqlRelationLinked *pRelationLinked = dao.getSqlRelationLinked(); + if (pRelationLinked) + { + dao.updateError(pRelationLinked->hierarchyOnBeforeSave(params)); + } + if (!dao.isValid()) + { + return dao.error(); + } + + dao.updateError(qx::dao::insert(t, (&dao.database()))); + if (!dao.isValid()) + { + return dao.error(); + } + + if (pRelationLinked) + { + dao.updateError(pRelationLinked->hierarchyOnAfterSave(params)); + } + if (!dao.isValid()) + { + return dao.error(); + } + + return dao.error(); + } + }; + + template + struct QxDao_Insert_WithRelation_Container + { + + static QSqlError insert(const QStringList &relation, T &t, QSqlDatabase *pDatabase) + { + typedef typename qx::trait::generic_container::type_value_qx type_item; + + if (qx::trait::generic_container::size(t) <= 0) + { + return QSqlError(); + } + qx::dao::detail::QxDao_Helper_Container dao(t, pDatabase, "insert with relation", new qx::QxSqlQueryBuilder_Insert()); + if (!dao.isValid()) + { + return dao.error(); + } + if (dao.isReadOnly()) + { + return dao.errReadOnly(); + } + if (!dao.updateSqlRelationX(relation)) + { + return dao.errInvalidRelation(); + } + if (!pDatabase) + { + dao.transaction(); + } + dao.quiet(); + + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!insertItem((*it), dao)) + { + return dao.error(); + } + } + + return dao.error(); + } + + private: + template + static inline bool insertItem(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + bool bInsertOk = insertItem_Helper < U, std::is_pointer::value || qx::trait::is_smart_ptr::value > ::insert(item, dao); + if (bInsertOk) + { + qx::dao::detail::QxDao_Keep_Original::backup(item); + } + return bInsertOk; + } + + template + struct insertItem_Helper + { + static inline bool insert(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return (item ? qx::dao::detail::QxDao_Insert_WithRelation_Container::insertItem((*item), dao) : true); + } + }; + + template + struct insertItem_Helper, false> + { + static inline bool insert(std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Insert_WithRelation_Container::insertItem(item.second, dao); + } + }; + + template + struct insertItem_Helper, false> + { + static inline bool insert(const std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Insert_WithRelation_Container::insertItem(item.second, dao); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct insertItem_Helper, false> + { + static inline bool insert(QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Insert_WithRelation_Container::insertItem(item.second, dao); + } + }; + + template + struct insertItem_Helper, false> + { + static inline bool insert(const QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Insert_WithRelation_Container::insertItem(item.second, dao); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct insertItem_Helper + { + static bool insert(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + qx::QxSqlRelationParams params(0, 0, NULL, (&dao.builder()), (&dao.query()), (&item)); + params.setDatabase((&dao.database())); + + qx::QxSqlRelationLinked *pRelationLinked = dao.getSqlRelationLinked(); + if (pRelationLinked) + { + dao.updateError(pRelationLinked->hierarchyOnBeforeSave(params)); + } + if (!dao.isValid()) + { + return false; + } + + dao.updateError(qx::dao::insert(item, (&dao.database()))); + if (!dao.isValid()) + { + return false; + } + + if (pRelationLinked) + { + dao.updateError(pRelationLinked->hierarchyOnAfterSave(params)); + } + if (!dao.isValid()) + { + return false; + } + + return dao.isValid(); + } + }; + }; + + template + struct QxDao_Insert_WithRelation_Ptr + { + + static inline QSqlError insert(const QStringList &relation, T &t, QSqlDatabase *pDatabase) + { + return (t ? qx::dao::insert_with_relation(relation, (*t), pDatabase) : QSqlError()); + } + }; + + template + struct QxDao_Insert_WithRelation + { + + static inline QSqlError insert(const QString &relation, T &t, QSqlDatabase *pDatabase) + { + QStringList lst; + if (!relation.isEmpty()) + { + lst = relation.split("|"); + } + return QxDao_Insert_WithRelation::insert(lst, t, pDatabase); + } + + static inline QSqlError insert(const QStringList &relation, T &t, QSqlDatabase *pDatabase) + { + typedef typename std::conditional::value, qx::dao::detail::QxDao_Insert_WithRelation_Ptr, qx::dao::detail::QxDao_Insert_WithRelation_Generic>::type type_dao_1; + typedef typename std::conditional::value, qx::dao::detail::QxDao_Insert_WithRelation_Ptr, type_dao_1>::type type_dao_2; + typedef typename std::conditional::value, qx::dao::detail::QxDao_Insert_WithRelation_Container, type_dao_2>::type type_dao_3; + + QSqlError error = type_dao_3::insert(relation, t, pDatabase); + if (!error.isValid()) + { + qx::dao::detail::QxDao_Keep_Original::backup(t); + } + return error; + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxDao_Save.inl b/inl/QxDao/QxDao_Save.inl new file mode 100644 index 0000000..632d125 --- /dev/null +++ b/inl/QxDao/QxDao_Save.inl @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_Save_Generic + { + + static QSqlError save(T &t, QSqlDatabase *pDatabase) + { + qx::dao::detail::QxDao_Helper dao(t, pDatabase, "save", new qx::QxSqlQueryBuilder_Update()); + if (!dao.isValid()) + { + return dao.error(); + } + if (!pDatabase) + { + dao.transaction(); + } + dao.quiet(); + + qx_bool bExist = dao.isValidPrimaryKey(t); + if (bExist) + { + bExist = qx::dao::exist(t, (&dao.database())); + } + if (bExist) + { + dao.updateError(qx::dao::update(t, (&dao.database()))); + } + else + { + dao.updateError(qx::dao::insert(t, (&dao.database()))); + } + + return dao.error(); + } + }; + + template + struct QxDao_Save_Container + { + + static QSqlError save(T &t, QSqlDatabase *pDatabase) + { + typedef typename qx::trait::generic_container::type_value_qx type_item; + + if (qx::trait::generic_container::size(t) <= 0) + { + return QSqlError(); + } + qx::dao::detail::QxDao_Helper_Container dao(t, pDatabase, "save", new qx::QxSqlQueryBuilder_Update()); + if (!dao.isValid()) + { + return dao.error(); + } + if (!pDatabase) + { + dao.transaction(); + } + dao.quiet(); + + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!saveItem((*it), dao)) + { + return dao.error(); + } + } + + return dao.error(); + } + + private: + template + static inline bool saveItem(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + bool bSaveOk = saveItem_Helper < U, std::is_pointer::value || qx::trait::is_smart_ptr::value > ::save(item, dao); + if (bSaveOk) + { + qx::dao::detail::QxDao_Keep_Original::backup(item); + } + return bSaveOk; + } + + template + struct saveItem_Helper + { + static inline bool save(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return (item ? qx::dao::detail::QxDao_Save_Container::saveItem((*item), dao) : false); + } + }; + + template + struct saveItem_Helper, false> + { + static inline bool save(std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_Container::saveItem(item.second, dao); + } + }; + + template + struct saveItem_Helper, false> + { + static inline bool save(const std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_Container::saveItem(item.second, dao); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct saveItem_Helper, false> + { + static inline bool save(QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_Container::saveItem(item.second, dao); + } + }; + + template + struct saveItem_Helper, false> + { + static inline bool save(const QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_Container::saveItem(item.second, dao); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct saveItem_Helper + { + static bool save(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + qx_bool bExist = dao.isValidPrimaryKey(item); + if (bExist) + { + bExist = qx::dao::exist(item, (&dao.database())); + } + if (bExist) + { + dao.updateError(qx::dao::update(item, (&dao.database()))); + } + else + { + dao.updateError(qx::dao::insert(item, (&dao.database()))); + } + + return dao.isValid(); + } + }; + }; + + template + struct QxDao_Save_Ptr + { + + static inline QSqlError save(T &t, QSqlDatabase *pDatabase) + { + return (t ? qx::dao::save((*t), pDatabase) : QSqlError()); + } + }; + + template + struct QxDao_Save + { + + static inline QSqlError save(T &t, QSqlDatabase *pDatabase) + { + typedef typename std::conditional::value, qx::dao::detail::QxDao_Save_Ptr, qx::dao::detail::QxDao_Save_Generic>::type type_dao_1; + typedef typename std::conditional::value, qx::dao::detail::QxDao_Save_Ptr, type_dao_1>::type type_dao_2; + typedef typename std::conditional::value, qx::dao::detail::QxDao_Save_Container, type_dao_2>::type type_dao_3; + + QSqlError error = type_dao_3::save(t, pDatabase); + if (!error.isValid()) + { + qx::dao::detail::QxDao_Keep_Original::backup(t); + } + return error; + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxDao_Save_WithRelation.inl b/inl/QxDao/QxDao_Save_WithRelation.inl new file mode 100644 index 0000000..0909ce4 --- /dev/null +++ b/inl/QxDao/QxDao_Save_WithRelation.inl @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_Save_WithRelation_Generic + { + + static QSqlError save(const QStringList &relation, T &t, QSqlDatabase *pDatabase) + { + qx::dao::detail::QxDao_Helper dao(t, pDatabase, "save with relation", new qx::QxSqlQueryBuilder_Update()); + if (!dao.isValid()) + { + return dao.error(); + } + if (!dao.updateSqlRelationX(relation)) + { + return dao.errInvalidRelation(); + } + if (!pDatabase) + { + dao.transaction(); + } + dao.quiet(); + + qx_bool bExist = dao.isValidPrimaryKey(t); + if (bExist) + { + bExist = qx::dao::exist(t, (&dao.database())); + } + if (bExist) + { + dao.updateError(qx::dao::update_with_relation(relation, t, (&dao.database()))); + } + else + { + dao.updateError(qx::dao::insert_with_relation(relation, t, (&dao.database()))); + } + + return dao.error(); + } + }; + + template + struct QxDao_Save_WithRelation_Container + { + + static QSqlError save(const QStringList &relation, T &t, QSqlDatabase *pDatabase) + { + typedef typename qx::trait::generic_container::type_value_qx type_item; + + if (qx::trait::generic_container::size(t) <= 0) + { + return QSqlError(); + } + qx::dao::detail::QxDao_Helper_Container dao(t, pDatabase, "save with relation", new qx::QxSqlQueryBuilder_Update()); + if (!dao.isValid()) + { + return dao.error(); + } + if (!dao.updateSqlRelationX(relation)) + { + return dao.errInvalidRelation(); + } + if (!pDatabase) + { + dao.transaction(); + } + dao.quiet(); + + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!saveItem(relation, (*it), dao)) + { + return dao.error(); + } + } + + return dao.error(); + } + + private: + template + static inline bool saveItem(const QStringList &relation, U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + bool bSaveOk = saveItem_Helper < U, std::is_pointer::value || qx::trait::is_smart_ptr::value > ::save(relation, item, dao); + if (bSaveOk) + { + qx::dao::detail::QxDao_Keep_Original::backup(item); + } + return bSaveOk; + } + + template + struct saveItem_Helper + { + static inline bool save(const QStringList &relation, U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return (item ? qx::dao::detail::QxDao_Save_WithRelation_Container::saveItem(relation, (*item), dao) : false); + } + }; + + template + struct saveItem_Helper, false> + { + static inline bool save(const QStringList &relation, std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_WithRelation_Container::saveItem(relation, item.second, dao); + } + }; + + template + struct saveItem_Helper, false> + { + static inline bool save(const QStringList &relation, const std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_WithRelation_Container::saveItem(relation, item.second, dao); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct saveItem_Helper, false> + { + static inline bool save(const QStringList &relation, QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_WithRelation_Container::saveItem(relation, item.second, dao); + } + }; + + template + struct saveItem_Helper, false> + { + static inline bool save(const QStringList &relation, const QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_WithRelation_Container::saveItem(relation, item.second, dao); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct saveItem_Helper + { + static bool save(const QStringList &relation, U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + qx_bool bExist = dao.isValidPrimaryKey(item); + if (bExist) + { + bExist = qx::dao::exist(item, (&dao.database())); + } + if (bExist) + { + dao.updateError(qx::dao::update_with_relation(relation, item, (&dao.database()))); + } + else + { + dao.updateError(qx::dao::insert_with_relation(relation, item, (&dao.database()))); + } + + return dao.isValid(); + } + }; + }; + + template + struct QxDao_Save_WithRelation_Ptr + { + + static inline QSqlError save(const QStringList &relation, T &t, QSqlDatabase *pDatabase) + { + return (t ? qx::dao::save_with_relation(relation, (*t), pDatabase) : QSqlError()); + } + }; + + template + struct QxDao_Save_WithRelation + { + + static inline QSqlError save(const QString &relation, T &t, QSqlDatabase *pDatabase) + { + QStringList lst; + if (!relation.isEmpty()) + { + lst = relation.split("|"); + } + return QxDao_Save_WithRelation::save(lst, t, pDatabase); + } + + static inline QSqlError save(const QStringList &relation, T &t, QSqlDatabase *pDatabase) + { + typedef typename std::conditional::value, qx::dao::detail::QxDao_Save_WithRelation_Ptr, qx::dao::detail::QxDao_Save_WithRelation_Generic>::type type_dao_1; + typedef typename std::conditional::value, qx::dao::detail::QxDao_Save_WithRelation_Ptr, type_dao_1>::type type_dao_2; + typedef typename std::conditional::value, qx::dao::detail::QxDao_Save_WithRelation_Container, type_dao_2>::type type_dao_3; + + QSqlError error = type_dao_3::save(relation, t, pDatabase); + if (!error.isValid()) + { + qx::dao::detail::QxDao_Keep_Original::backup(t); + } + return error; + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxDao_Save_WithRelation_Recursive.inl b/inl/QxDao/QxDao_Save_WithRelation_Recursive.inl new file mode 100644 index 0000000..659519e --- /dev/null +++ b/inl/QxDao/QxDao_Save_WithRelation_Recursive.inl @@ -0,0 +1,516 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_Save_WithRelation_Recursive_Generic + { + + static QSqlError save(T &t, qx::dao::save_mode::e_save_mode eSaveMode, QSqlDatabase *pDatabase, qx::QxSqlRelationParams *pRelationParams) + { + QStringList relation("*"); + qx::dao::detail::QxDao_Helper dao(t, pDatabase, "save with relation recursive", new qx::QxSqlQueryBuilder_Update()); + if (!dao.isValid()) + { + return dao.error(); + } + if (dao.isReadOnly()) + { + return dao.errReadOnly(); + } + if (!dao.updateSqlRelationX(relation)) + { + return dao.errInvalidRelation(); + } + if (!pDatabase) + { + dao.transaction(); + } + dao.quiet(); + + qx::QxSqlRelationParams params(0, 0, NULL, (&dao.builder()), (&dao.query()), (&t)); + if (!pRelationParams) + { + params.setDatabase((&dao.database())); + params.setSaveMode(eSaveMode); + params.setRecursiveMode(true); + } + else + { + params = (*pRelationParams); + params.setOwner(&t); + } + if (params.existRecursiveItem(&t)) + { + return dao.error(); + } + params.insertRecursiveItem(&t); + + qx::QxSqlRelationLinked *pRelationLinked = dao.getSqlRelationLinked(); + if (pRelationLinked) + { + dao.updateError(pRelationLinked->hierarchyOnBeforeSave(params)); + } + if (!dao.isValid()) + { + return dao.error(); + } + + if (eSaveMode == qx::dao::save_mode::e_check_insert_or_update) + { + qx_bool bExist = dao.isValidPrimaryKey(t); + if (bExist) + { + bExist = qx::dao::exist(t, (&dao.database())); + } + if (bExist) + { + dao.updateError(qx::dao::update(t, (&dao.database()))); + } + else + { + dao.updateError(qx::dao::insert(t, (&dao.database()))); + } + } + else if (eSaveMode == qx::dao::save_mode::e_insert_only) + { + dao.updateError(qx::dao::insert(t, (&dao.database()))); + } + else if (eSaveMode == qx::dao::save_mode::e_update_only) + { + dao.updateError(qx::dao::update(t, (&dao.database()))); + } + else + { + qAssert(false); + } + + if (!dao.isValid()) + { + return dao.error(); + } + if (pRelationLinked) + { + dao.updateError(pRelationLinked->hierarchyOnAfterSave(params)); + } + if (!dao.isValid()) + { + return dao.error(); + } + + return dao.error(); + } + }; + + template + struct QxDao_Save_WithRelation_Recursive_Container + { + + static QSqlError save(T &t, qx::dao::save_mode::e_save_mode eSaveMode, QSqlDatabase *pDatabase, qx::QxSqlRelationParams *pRelationParams) + { + typedef typename qx::trait::generic_container::type_value_qx type_item; + + QStringList relation("*"); + if (qx::trait::generic_container::size(t) <= 0) + { + return QSqlError(); + } + qx::dao::detail::QxDao_Helper_Container dao(t, pDatabase, "save with relation recursive", new qx::QxSqlQueryBuilder_Update()); + if (!dao.isValid()) + { + return dao.error(); + } + if (dao.isReadOnly()) + { + return dao.errReadOnly(); + } + if (!dao.updateSqlRelationX(relation)) + { + return dao.errInvalidRelation(); + } + if (!pDatabase) + { + dao.transaction(); + } + dao.quiet(); + + if (eSaveMode == qx::dao::save_mode::e_check_insert_or_update) + { + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!saveItem(eSaveMode, pRelationParams, (*it), dao)) + { + return dao.error(); + } + } + } + else if (eSaveMode == qx::dao::save_mode::e_insert_only) + { + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!hierarchyOnBeforeSave(eSaveMode, pRelationParams, (*it), dao)) + { + return dao.error(); + } + } + dao.updateError(qx::dao::insert(t, (&dao.database()))); + if (!dao.isValid()) + { + return dao.error(); + } + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!hierarchyOnAfterSave(eSaveMode, pRelationParams, (*it), dao)) + { + return dao.error(); + } + } + } + else if (eSaveMode == qx::dao::save_mode::e_update_only) + { + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!hierarchyOnBeforeSave(eSaveMode, pRelationParams, (*it), dao)) + { + return dao.error(); + } + } + dao.updateError(qx::dao::update(t, (&dao.database()))); + if (!dao.isValid()) + { + return dao.error(); + } + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!hierarchyOnAfterSave(eSaveMode, pRelationParams, (*it), dao)) + { + return dao.error(); + } + } + } + else + { + qAssert(false); + } + + return dao.error(); + } + + private: + template + static inline bool saveItem(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + bool bSaveOk = saveItem_Helper < U, std::is_pointer::value || qx::trait::is_smart_ptr::value > ::save(eSaveMode, pRelationParams, item, dao); + if (bSaveOk) + { + qx::dao::detail::QxDao_Keep_Original::backup(item); + } + return bSaveOk; + } + + template + static inline bool hierarchyOnBeforeSave(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + bool bBeforeSaveOk = saveItem_Helper < U, std::is_pointer::value || qx::trait::is_smart_ptr::value > ::hierarchyOnBeforeSave(eSaveMode, pRelationParams, item, dao); + if (bBeforeSaveOk) + { + qx::dao::detail::QxDao_Keep_Original::backup(item); + } + return bBeforeSaveOk; + } + + template + static inline bool hierarchyOnAfterSave(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + bool bAfterSaveOk = saveItem_Helper < U, std::is_pointer::value || qx::trait::is_smart_ptr::value > ::hierarchyOnAfterSave(eSaveMode, pRelationParams, item, dao); + if (bAfterSaveOk) + { + qx::dao::detail::QxDao_Keep_Original::backup(item); + } + return bAfterSaveOk; + } + + template + struct saveItem_Helper + { + static inline bool save(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return (item ? qx::dao::detail::QxDao_Save_WithRelation_Recursive_Container::saveItem(eSaveMode, pRelationParams, (*item), dao) : false); + } + static inline bool hierarchyOnBeforeSave(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return (item ? qx::dao::detail::QxDao_Save_WithRelation_Recursive_Container::hierarchyOnBeforeSave(eSaveMode, pRelationParams, (*item), dao) : false); + } + static inline bool hierarchyOnAfterSave(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return (item ? qx::dao::detail::QxDao_Save_WithRelation_Recursive_Container::hierarchyOnAfterSave(eSaveMode, pRelationParams, (*item), dao) : false); + } + }; + + template + struct saveItem_Helper, false> + { + static inline bool save(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_WithRelation_Recursive_Container::saveItem(eSaveMode, pRelationParams, item.second, dao); + } + static inline bool hierarchyOnBeforeSave(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_WithRelation_Recursive_Container::hierarchyOnBeforeSave(eSaveMode, pRelationParams, item.second, dao); + } + static inline bool hierarchyOnAfterSave(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_WithRelation_Recursive_Container::hierarchyOnAfterSave(eSaveMode, pRelationParams, item.second, dao); + } + }; + + template + struct saveItem_Helper, false> + { + static inline bool save(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, const std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_WithRelation_Recursive_Container::saveItem(eSaveMode, pRelationParams, item.second, dao); + } + static inline bool hierarchyOnBeforeSave(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, const std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_WithRelation_Recursive_Container::hierarchyOnBeforeSave(eSaveMode, pRelationParams, item.second, dao); + } + static inline bool hierarchyOnAfterSave(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, const std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_WithRelation_Recursive_Container::hierarchyOnAfterSave(eSaveMode, pRelationParams, item.second, dao); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct saveItem_Helper, false> + { + static inline bool save(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_WithRelation_Recursive_Container::saveItem(eSaveMode, pRelationParams, item.second, dao); + } + static inline bool hierarchyOnBeforeSave(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_WithRelation_Recursive_Container::hierarchyOnBeforeSave(eSaveMode, pRelationParams, item.second, dao); + } + static inline bool hierarchyOnAfterSave(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_WithRelation_Recursive_Container::hierarchyOnAfterSave(eSaveMode, pRelationParams, item.second, dao); + } + }; + + template + struct saveItem_Helper, false> + { + static inline bool save(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, const QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_WithRelation_Recursive_Container::saveItem(eSaveMode, pRelationParams, item.second, dao); + } + static inline bool hierarchyOnBeforeSave(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, const QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_WithRelation_Recursive_Container::hierarchyOnBeforeSave(eSaveMode, pRelationParams, item.second, dao); + } + static inline bool hierarchyOnAfterSave(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, const QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Save_WithRelation_Recursive_Container::hierarchyOnAfterSave(eSaveMode, pRelationParams, item.second, dao); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct saveItem_Helper + { + static bool save(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + qx::QxSqlRelationParams params(0, 0, NULL, (&dao.builder()), (&dao.query()), (&item)); + if (!pRelationParams) + { + params.setDatabase((&dao.database())); + params.setSaveMode(eSaveMode); + params.setRecursiveMode(true); + } + else + { + params = (*pRelationParams); + params.setOwner(&item); + } + if (params.existRecursiveItem(&item)) + { + return true; + } + params.insertRecursiveItem(&item); + + qx::QxSqlRelationLinked *pRelationLinked = dao.getSqlRelationLinked(); + if (pRelationLinked) + { + dao.updateError(pRelationLinked->hierarchyOnBeforeSave(params)); + } + if (!dao.isValid()) + { + return false; + } + + if (eSaveMode == qx::dao::save_mode::e_check_insert_or_update) + { + qx_bool bExist = dao.isValidPrimaryKey(item); + if (bExist) + { + bExist = qx::dao::exist(item, (&dao.database())); + } + if (bExist) + { + dao.updateError(qx::dao::update(item, (&dao.database()))); + } + else + { + dao.updateError(qx::dao::insert(item, (&dao.database()))); + } + } + else if (eSaveMode == qx::dao::save_mode::e_insert_only) + { + dao.updateError(qx::dao::insert(item, (&dao.database()))); + } + else if (eSaveMode == qx::dao::save_mode::e_update_only) + { + dao.updateError(qx::dao::update(item, (&dao.database()))); + } + else + { + qAssert(false); + } + + if (!dao.isValid()) + { + return false; + } + if (pRelationLinked) + { + dao.updateError(pRelationLinked->hierarchyOnAfterSave(params)); + } + if (!dao.isValid()) + { + return false; + } + + return dao.isValid(); + } + + static bool hierarchyOnBeforeSave(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + qx::QxSqlRelationParams params(0, 0, NULL, (&dao.builder()), (&dao.query()), (&item)); + if (!pRelationParams) + { + params.setDatabase((&dao.database())); + params.setSaveMode(eSaveMode); + params.setRecursiveMode(true); + } + else + { + params = (*pRelationParams); + params.setOwner(&item); + } + + qx::QxSqlRelationLinked *pRelationLinked = dao.getSqlRelationLinked(); + if (pRelationLinked) + { + dao.updateError(pRelationLinked->hierarchyOnBeforeSave(params)); + } + return dao.isValid(); + } + + static bool hierarchyOnAfterSave(qx::dao::save_mode::e_save_mode eSaveMode, qx::QxSqlRelationParams *pRelationParams, U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + qx::QxSqlRelationParams params(0, 0, NULL, (&dao.builder()), (&dao.query()), (&item)); + if (!pRelationParams) + { + params.setDatabase((&dao.database())); + params.setSaveMode(eSaveMode); + params.setRecursiveMode(true); + } + else + { + params = (*pRelationParams); + params.setOwner(&item); + } + if (params.existRecursiveItem(&item)) + { + return true; + } + params.insertRecursiveItem(&item); + + qx::QxSqlRelationLinked *pRelationLinked = dao.getSqlRelationLinked(); + if (pRelationLinked) + { + dao.updateError(pRelationLinked->hierarchyOnAfterSave(params)); + } + return dao.isValid(); + } + }; + }; + + template + struct QxDao_Save_WithRelation_Recursive_Ptr + { + + static inline QSqlError save(T &t, qx::dao::save_mode::e_save_mode eSaveMode, QSqlDatabase *pDatabase, qx::QxSqlRelationParams *pRelationParams) + { + return (t ? qx::dao::save_with_relation_recursive((*t), eSaveMode, pDatabase, pRelationParams) : QSqlError()); + } + }; + + template + struct QxDao_Save_WithRelation_Recursive + { + + static inline QSqlError save(T &t, qx::dao::save_mode::e_save_mode eSaveMode, QSqlDatabase *pDatabase, qx::QxSqlRelationParams *pRelationParams) + { + typedef typename std::conditional::value, qx::dao::detail::QxDao_Save_WithRelation_Recursive_Ptr, qx::dao::detail::QxDao_Save_WithRelation_Recursive_Generic>::type type_dao_1; + typedef typename std::conditional::value, qx::dao::detail::QxDao_Save_WithRelation_Recursive_Ptr, type_dao_1>::type type_dao_2; + typedef typename std::conditional::value, qx::dao::detail::QxDao_Save_WithRelation_Recursive_Container, type_dao_2>::type type_dao_3; + + QSqlError error = type_dao_3::save(t, eSaveMode, pDatabase, pRelationParams); + if (!error.isValid()) + { + qx::dao::detail::QxDao_Keep_Original::backup(t); + } + return error; + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxDao_Trigger.inl b/inl/QxDao/QxDao_Trigger.inl new file mode 100644 index 0000000..cc27cb8 --- /dev/null +++ b/inl/QxDao/QxDao_Trigger.inl @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_Trigger + { + + private: + typedef typename qx::trait::get_base_class::type type_base; + enum + { + is_valid_base_class = (!std::is_same::value) + }; + + public: + static inline void onBeforeInsert(T *t, qx::dao::detail::IxDao_Helper *dao) { TriggerHelper::onBeforeInsert(t, dao); } + static inline void onBeforeUpdate(T *t, qx::dao::detail::IxDao_Helper *dao) { TriggerHelper::onBeforeUpdate(t, dao); } + static inline void onBeforeDelete(T *t, qx::dao::detail::IxDao_Helper *dao) { TriggerHelper::onBeforeDelete(t, dao); } + static inline void onBeforeFetch(T *t, qx::dao::detail::IxDao_Helper *dao) { TriggerHelper::onBeforeFetch(t, dao); } + static inline void onAfterInsert(T *t, qx::dao::detail::IxDao_Helper *dao) { TriggerHelper::onAfterInsert(t, dao); } + static inline void onAfterUpdate(T *t, qx::dao::detail::IxDao_Helper *dao) { TriggerHelper::onAfterUpdate(t, dao); } + static inline void onAfterDelete(T *t, qx::dao::detail::IxDao_Helper *dao) { TriggerHelper::onAfterDelete(t, dao); } + static inline void onAfterFetch(T *t, qx::dao::detail::IxDao_Helper *dao) { TriggerHelper::onAfterFetch(t, dao); } + + private: + template + struct TriggerHelper + { + static inline void onBeforeInsert(T *t, qx::dao::detail::IxDao_Helper *dao) + { + Q_UNUSED(t); + Q_UNUSED(dao); + } + static inline void onBeforeUpdate(T *t, qx::dao::detail::IxDao_Helper *dao) + { + Q_UNUSED(t); + Q_UNUSED(dao); + } + static inline void onBeforeDelete(T *t, qx::dao::detail::IxDao_Helper *dao) + { + Q_UNUSED(t); + Q_UNUSED(dao); + } + static inline void onBeforeFetch(T *t, qx::dao::detail::IxDao_Helper *dao) + { + Q_UNUSED(t); + Q_UNUSED(dao); + } + static inline void onAfterInsert(T *t, qx::dao::detail::IxDao_Helper *dao) + { + Q_UNUSED(t); + Q_UNUSED(dao); + } + static inline void onAfterUpdate(T *t, qx::dao::detail::IxDao_Helper *dao) + { + Q_UNUSED(t); + Q_UNUSED(dao); + } + static inline void onAfterDelete(T *t, qx::dao::detail::IxDao_Helper *dao) + { + Q_UNUSED(t); + Q_UNUSED(dao); + } + static inline void onAfterFetch(T *t, qx::dao::detail::IxDao_Helper *dao) + { + Q_UNUSED(t); + Q_UNUSED(dao); + } + }; + + template + struct TriggerHelper + { + static inline void onBeforeInsert(T *t, qx::dao::detail::IxDao_Helper *dao) { qx::dao::detail::QxDao_Trigger::onBeforeInsert(static_cast(t), dao); } + static inline void onBeforeUpdate(T *t, qx::dao::detail::IxDao_Helper *dao) { qx::dao::detail::QxDao_Trigger::onBeforeUpdate(static_cast(t), dao); } + static inline void onBeforeDelete(T *t, qx::dao::detail::IxDao_Helper *dao) { qx::dao::detail::QxDao_Trigger::onBeforeDelete(static_cast(t), dao); } + static inline void onBeforeFetch(T *t, qx::dao::detail::IxDao_Helper *dao) { qx::dao::detail::QxDao_Trigger::onBeforeFetch(static_cast(t), dao); } + static inline void onAfterInsert(T *t, qx::dao::detail::IxDao_Helper *dao) { qx::dao::detail::QxDao_Trigger::onAfterInsert(static_cast(t), dao); } + static inline void onAfterUpdate(T *t, qx::dao::detail::IxDao_Helper *dao) { qx::dao::detail::QxDao_Trigger::onAfterUpdate(static_cast(t), dao); } + static inline void onAfterDelete(T *t, qx::dao::detail::IxDao_Helper *dao) { qx::dao::detail::QxDao_Trigger::onAfterDelete(static_cast(t), dao); } + static inline void onAfterFetch(T *t, qx::dao::detail::IxDao_Helper *dao) { qx::dao::detail::QxDao_Trigger::onAfterFetch(static_cast(t), dao); } + }; + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxDao_Update.inl b/inl/QxDao/QxDao_Update.inl new file mode 100644 index 0000000..89b419b --- /dev/null +++ b/inl/QxDao/QxDao_Update.inl @@ -0,0 +1,398 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_Update_Generic + { + + static QSqlError update(const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase, const QStringList &columns, bool bUseExecBatch) + { + Q_UNUSED(bUseExecBatch); // Useful only with containers + qx::dao::detail::QxDao_Helper dao(t, pDatabase, "update", new qx::QxSqlQueryBuilder_Update(), (&query)); + if (!dao.isValid()) + { + return dao.error(); + } + if (dao.isReadOnly()) + { + return dao.errReadOnly(); + } + if (!dao.isValidPrimaryKey(t)) + { + return dao.errInvalidId(); + } + if (!dao.validateInstance(t)) + { + return dao.error(); + } + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + qx::dao::on_before_update((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + QString ctx = QString("mongodb") + ((columns.count() > 0) ? (QString(":columns{,") + columns.join(",") + QString(",}")) : QString()); + qx::dao::mongodb::QxMongoDB_Helper::updateOne((&dao), dao.getDataMemberX()->getClass(), qx::serialization::json::to_string(t, 1, ctx), (&query)); + if (!dao.isValid()) + { + return dao.error(); + } + qx::dao::on_after_update((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + return dao.error(); + } +#endif // _QX_ENABLE_MONGODB + + QString sql = dao.builder().buildSql(columns).getSqlQuery(); + if (!dao.getDataId() || sql.isEmpty()) + { + return dao.errEmpty(); + } + if (!query.isEmpty()) + { + dao.addQuery(false); + sql = dao.builder().getSqlQuery(); + } + if (!pDatabase) + { + dao.transaction(); + } + if (!dao.prepare(sql)) + { + return dao.errFailed(true); + } + + IxSqlGenerator *pSqlGenerator = dao.getSqlGenerator(); + if (pSqlGenerator) + { + pSqlGenerator->onBeforeUpdate((&dao), (&t)); + } + qx::dao::on_before_update((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + + { + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_read_instance); + qx::dao::detail::QxSqlQueryHelper_Update::resolveInput(t, dao.query(), dao.builder(), columns); + } + + if (!query.isEmpty()) + { + query.resolve(dao.query()); + } + if (!dao.exec(true)) + { + return dao.errFailed(); + } + if (pSqlGenerator) + { + pSqlGenerator->onAfterUpdate((&dao), (&t)); + } + qx::dao::on_after_update((&t), (&dao)); + if (!dao.isValid()) + { + return dao.error(); + } + + return dao.error(); + } + }; + + template + struct QxDao_Update_Container + { + + static QSqlError update(const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase, const QStringList &columns, bool bUseExecBatch) + { + typedef typename qx::trait::generic_container::type_value_qx type_item; + + if (qx::trait::generic_container::size(t) <= 0) + { + return QSqlError(); + } + qx::dao::detail::QxDao_Helper_Container dao(t, pDatabase, "update", new qx::QxSqlQueryBuilder_Update(), (&query)); + if (!dao.isValid()) + { + return dao.error(); + } + if (dao.isReadOnly()) + { + return dao.errReadOnly(); + } + if (!dao.validateInstance(t)) + { + return dao.error(); + } + dao.setUseExecBatch(bUseExecBatch); + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + dao.setSqlColumns(columns); + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!updateItem((*it), dao)) + { + return dao.error(); + } + } + QStringList &itemsAsJson = dao.itemsAsJson(); + qx::dao::mongodb::QxMongoDB_Helper::updateMany((&dao), dao.getDataMemberX()->getClass(), itemsAsJson, (&query)); + if (!dao.isValid()) + { + return dao.error(); + } + dao.qxQuery().queryAt(2, ""); + dao.itemsAsJson().clear(); + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!updateItem((*it), dao)) + { + return dao.error(); + } + } + return dao.error(); + } +#endif // _QX_ENABLE_MONGODB + + QString sql = dao.builder().buildSql(columns).getSqlQuery(); + if (!dao.getDataId() || sql.isEmpty()) + { + return dao.errEmpty(); + } + if (!query.isEmpty()) + { + dao.addQuery(false); + sql = dao.builder().getSqlQuery(); + } + if (!pDatabase) + { + dao.transaction(); + } + if (!dao.prepare(sql)) + { + return dao.errFailed(true); + } + dao.setSqlColumns(columns); + + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!updateItem((*it), dao)) + { + return dao.error(); + } + } + + if (bUseExecBatch && (!dao.exec())) + { + return dao.errFailed(); + } + return dao.error(); + } + + private: + template + static inline bool updateItem(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + bool bUpdateOk = updateItem_Helper < U, std::is_pointer::value || qx::trait::is_smart_ptr::value > ::update(item, dao); + if (bUpdateOk) + { + qx::dao::detail::QxDao_Keep_Original::backup(item); + } + return bUpdateOk; + } + + template + struct updateItem_Helper + { + static inline bool update(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return (item ? qx::dao::detail::QxDao_Update_Container::updateItem((*item), dao) : true); + } + }; + + template + struct updateItem_Helper, false> + { + static inline bool update(std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Update_Container::updateItem(item.second, dao); + } + }; + + template + struct updateItem_Helper, false> + { + static inline bool update(const std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Update_Container::updateItem(item.second, dao); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct updateItem_Helper, false> + { + static inline bool update(QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Update_Container::updateItem(item.second, dao); + } + }; + + template + struct updateItem_Helper, false> + { + static inline bool update(const QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Update_Container::updateItem(item.second, dao); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct updateItem_Helper + { + static bool update(U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + QStringList columns = dao.getSqlColumns(); + +#ifdef _QX_ENABLE_MONGODB + if (dao.isMongoDB()) + { + if (dao.qxQuery().queryAt(2) == "") + { + qx::dao::on_after_update((&item), (&dao)); + return dao.isValid(); + } + QString ctx = QString("mongodb") + ((columns.count() > 0) ? (QString(":columns{,") + columns.join(",") + QString(",}")) : QString()); + qx::dao::on_before_update((&item), (&dao)); + if (!dao.isValid()) + { + return false; + } + dao.itemsAsJson().append(qx::serialization::json::to_string(item, 1, ctx)); + return dao.isValid(); + } +#endif // _QX_ENABLE_MONGODB + + if (!dao.isValidPrimaryKey(item)) + { + dao.errInvalidId(); + return false; + } + IxSqlGenerator *pSqlGenerator = dao.getSqlGenerator(); + if (pSqlGenerator) + { + pSqlGenerator->onBeforeUpdate((&dao), (&item)); + } + qx::dao::on_before_update((&item), (&dao)); + if (!dao.isValid()) + { + return false; + } + + { + qx::dao::detail::IxDao_Timer timer((&dao), qx::dao::detail::IxDao_Helper::timer_cpp_read_instance); + qx::dao::detail::QxSqlQueryHelper_Update::resolveInput(item, dao.query(), dao.builder(), columns); + } + + dao.resolveQuery(); + if (dao.getUseExecBatch()) + { + return dao.isValid(); + } + if (!dao.exec(true)) + { + dao.errFailed(); + return false; + } + if (pSqlGenerator) + { + pSqlGenerator->onAfterUpdate((&dao), (&item)); + } + qx::dao::on_after_update((&item), (&dao)); + if (!dao.isValid()) + { + return false; + } + + return dao.isValid(); + } + }; + }; + + template + struct QxDao_Update_Ptr + { + + static inline QSqlError update(const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase, const QStringList &columns, bool bUseExecBatch) + { + return (t ? qx::dao::update_by_query(query, (*t), pDatabase, columns, bUseExecBatch) : QSqlError()); + } + }; + + template + struct QxDao_Update + { + + static inline QSqlError update(const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase, const QStringList &columns, bool bUseExecBatch = false) + { + typedef typename std::conditional::value, qx::dao::detail::QxDao_Update_Ptr, qx::dao::detail::QxDao_Update_Generic>::type type_dao_1; + typedef typename std::conditional::value, qx::dao::detail::QxDao_Update_Ptr, type_dao_1>::type type_dao_2; + typedef typename std::conditional::value, qx::dao::detail::QxDao_Update_Container, type_dao_2>::type type_dao_3; + + QSqlError error = type_dao_3::update(query, t, pDatabase, columns, bUseExecBatch); + if (!error.isValid()) + { + qx::dao::detail::QxDao_Keep_Original::backup(t); + } + return error; + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxDao_Update_Optimized.inl b/inl/QxDao/QxDao_Update_Optimized.inl new file mode 100644 index 0000000..a4e28ad --- /dev/null +++ b/inl/QxDao/QxDao_Update_Optimized.inl @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_Update_Optimized_Generic + { + + static inline QSqlError update_optimized(const qx::QxSqlQuery &query, qx::dao::ptr &ptr, QSqlDatabase *pDatabase, bool bUseExecBatch) + { + Q_UNUSED(bUseExecBatch); // Useful only with containers + QStringList lstDiff; + if (ptr.isNull() || !ptr.isDirty(lstDiff)) + { + return QSqlError(); + } + return qx::dao::update_by_query(query, (*ptr), pDatabase, lstDiff); + } + }; + + template + struct QxDao_Update_Optimized_Container + { + + static inline QSqlError update_optimized(const qx::QxSqlQuery &query, qx::dao::ptr &ptr, QSqlDatabase *pDatabase, bool bUseExecBatch) + { + if (ptr.isNull() || (qx::trait::generic_container::size(*ptr) <= 0)) + { + return QSqlError(); + } + if (!ptr.getOriginal() || (qx::trait::generic_container::size(*ptr) != qx::trait::generic_container::size(*ptr.getOriginal()))) + { + return qx::dao::update_by_query(query, (*ptr), pDatabase); + } + + QStringList lstDiffItem; + QSqlError errorItem; + QSqlDatabase db; + bool bCheckDatabaseTransaction = true; + +#ifdef _QX_ENABLE_MONGODB + if (qx::QxSqlDatabase::getSingleton()->getDriverName() == "QXMONGODB") + { + bCheckDatabaseTransaction = false; + } +#endif // _QX_ENABLE_MONGODB + + if (bCheckDatabaseTransaction) + { + db = (pDatabase ? (*pDatabase) : qx::QxSqlDatabase::getDatabase(errorItem)); + if (errorItem.isValid()) + { + return errorItem; + } + if (!pDatabase) + { + db.transaction(); + } + } + + typename T::const_iterator it2 = ptr.getOriginal()->begin(); + for (typename T::const_iterator it1 = ptr->begin(); it1 != ptr->end(); ++it1) + { + lstDiffItem.clear(); + qx::dao::detail::is_dirty((*it1), (*it2), lstDiffItem); + if (lstDiffItem.count() > 0) + { + errorItem = qx::dao::update_by_query(query, (*it1), (&db), lstDiffItem, bUseExecBatch); + } + if (errorItem.isValid()) + { + break; + } + else + { + ++it2; + } + } + + if (bCheckDatabaseTransaction) + { + if (!pDatabase && !errorItem.isValid()) + { + db.commit(); + } + else if (!pDatabase) + { + db.rollback(); + } + } + + return errorItem; + } + }; + + template + struct QxDao_Update_Optimized + { + + static inline QSqlError update_optimized(const qx::QxSqlQuery &query, qx::dao::ptr &ptr, QSqlDatabase *pDatabase, bool bUseExecBatch = false) + { + typedef typename std::conditional::value, qx::dao::detail::QxDao_Update_Optimized_Container, qx::dao::detail::QxDao_Update_Optimized_Generic>::type type_dao_1; + + QSqlError error = type_dao_1::update_optimized(query, ptr, pDatabase, bUseExecBatch); + if (!error.isValid()) + { + qx::dao::detail::QxDao_Keep_Original>::backup(ptr); + } + return error; + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxDao_Update_WithRelation.inl b/inl/QxDao/QxDao_Update_WithRelation.inl new file mode 100644 index 0000000..7667dc3 --- /dev/null +++ b/inl/QxDao/QxDao_Update_WithRelation.inl @@ -0,0 +1,280 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxDao_Update_WithRelation_Generic + { + + static QSqlError update(const QStringList &relation, const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase) + { + qx::dao::detail::QxDao_Helper dao(t, pDatabase, "update with relation", new qx::QxSqlQueryBuilder_Update()); + if (!dao.isValid()) + { + return dao.error(); + } + if (dao.isReadOnly()) + { + return dao.errReadOnly(); + } + if (!dao.isValidPrimaryKey(t)) + { + return dao.errInvalidId(); + } + if (!dao.updateSqlRelationX(relation)) + { + return dao.errInvalidRelation(); + } + if (!pDatabase) + { + dao.transaction(); + } + dao.quiet(); + + qx::QxSqlRelationParams params(0, 0, NULL, (&dao.builder()), (&dao.query()), (&t)); + params.setDatabase((&dao.database())); + + qx::QxSqlRelationLinked *pRelationLinked = dao.getSqlRelationLinked(); + if (pRelationLinked) + { + dao.updateError(pRelationLinked->hierarchyOnBeforeSave(params)); + } + if (!dao.isValid()) + { + return dao.error(); + } + + dao.updateError(qx::dao::update_by_query(query, t, (&dao.database()))); + if (!dao.isValid()) + { + return dao.error(); + } + + if (pRelationLinked) + { + dao.updateError(pRelationLinked->hierarchyOnAfterSave(params)); + } + if (!dao.isValid()) + { + return dao.error(); + } + + return dao.error(); + } + }; + + template + struct QxDao_Update_WithRelation_Container + { + + static QSqlError update(const QStringList &relation, const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase) + { + typedef typename qx::trait::generic_container::type_value_qx type_item; + + if (qx::trait::generic_container::size(t) <= 0) + { + return QSqlError(); + } + qx::dao::detail::QxDao_Helper_Container dao(t, pDatabase, "update with relation", new qx::QxSqlQueryBuilder_Update()); + if (!dao.isValid()) + { + return dao.error(); + } + if (dao.isReadOnly()) + { + return dao.errReadOnly(); + } + if (!dao.updateSqlRelationX(relation)) + { + return dao.errInvalidRelation(); + } + if (!pDatabase) + { + dao.transaction(); + } + dao.quiet(); + + for (typename T::iterator it = t.begin(); it != t.end(); ++it) + { + if (!updateItem(query, (*it), dao)) + { + return dao.error(); + } + } + + return dao.error(); + } + + private: + template + static inline bool updateItem(const qx::QxSqlQuery &query, U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + bool bUpdateOk = updateItem_Helper < U, std::is_pointer::value || qx::trait::is_smart_ptr::value > ::update(query, item, dao); + if (bUpdateOk) + { + qx::dao::detail::QxDao_Keep_Original::backup(item); + } + return bUpdateOk; + } + + template + struct updateItem_Helper + { + static inline bool update(const qx::QxSqlQuery &query, U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return (item ? qx::dao::detail::QxDao_Update_WithRelation_Container::updateItem(query, (*item), dao) : true); + } + }; + + template + struct updateItem_Helper, false> + { + static inline bool update(const qx::QxSqlQuery &query, std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Update_WithRelation_Container::updateItem(query, item.second, dao); + } + }; + + template + struct updateItem_Helper, false> + { + static inline bool update(const qx::QxSqlQuery &query, const std::pair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Update_WithRelation_Container::updateItem(query, item.second, dao); + } + }; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + template + struct updateItem_Helper, false> + { + static inline bool update(const qx::QxSqlQuery &query, QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Update_WithRelation_Container::updateItem(query, item.second, dao); + } + }; + + template + struct updateItem_Helper, false> + { + static inline bool update(const qx::QxSqlQuery &query, const QPair &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + return qx::dao::detail::QxDao_Update_WithRelation_Container::updateItem(query, item.second, dao); + } + }; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + template + struct updateItem_Helper + { + static bool update(const qx::QxSqlQuery &query, U &item, qx::dao::detail::QxDao_Helper_Container &dao) + { + qx::QxSqlRelationParams params(0, 0, NULL, (&dao.builder()), (&dao.query()), (&item)); + params.setDatabase((&dao.database())); + + qx::QxSqlRelationLinked *pRelationLinked = dao.getSqlRelationLinked(); + if (pRelationLinked) + { + dao.updateError(pRelationLinked->hierarchyOnBeforeSave(params)); + } + if (!dao.isValid()) + { + return false; + } + + dao.updateError(qx::dao::update_by_query(query, item, (&dao.database()))); + if (!dao.isValid()) + { + return false; + } + + if (pRelationLinked) + { + dao.updateError(pRelationLinked->hierarchyOnAfterSave(params)); + } + if (!dao.isValid()) + { + return false; + } + + return dao.isValid(); + } + }; + }; + + template + struct QxDao_Update_WithRelation_Ptr + { + + static inline QSqlError update(const QStringList &relation, const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase) + { + return (t ? qx::dao::update_by_query_with_relation(relation, query, (*t), pDatabase) : QSqlError()); + } + }; + + template + struct QxDao_Update_WithRelation + { + + static inline QSqlError update(const QString &relation, const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase) + { + QStringList lst; + if (!relation.isEmpty()) + { + lst = relation.split("|"); + } + return QxDao_Update_WithRelation::update(lst, query, t, pDatabase); + } + + static inline QSqlError update(const QStringList &relation, const qx::QxSqlQuery &query, T &t, QSqlDatabase *pDatabase) + { + typedef typename std::conditional::value, qx::dao::detail::QxDao_Update_WithRelation_Ptr, qx::dao::detail::QxDao_Update_WithRelation_Generic>::type type_dao_1; + typedef typename std::conditional::value, qx::dao::detail::QxDao_Update_WithRelation_Ptr, type_dao_1>::type type_dao_2; + typedef typename std::conditional::value, qx::dao::detail::QxDao_Update_WithRelation_Container, type_dao_2>::type type_dao_3; + + QSqlError error = type_dao_3::update(relation, query, t, pDatabase); + if (!error.isValid()) + { + qx::dao::detail::QxDao_Keep_Original::backup(t); + } + return error; + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxSqlQueryHelper_CreateTable.inl b/inl/QxDao/QxSqlQueryHelper_CreateTable.inl new file mode 100644 index 0000000..c066bf5 --- /dev/null +++ b/inl/QxDao/QxSqlQueryHelper_CreateTable.inl @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxSqlQueryHelper_CreateTable + { + + static void sql(QString &sql, qx::IxSqlQueryBuilder &builder) + { + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + qx::IxSqlQueryBuilder::sql_CreateTable(sql, builder); + } + + static void resolveInput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder) + { + Q_UNUSED(t); + Q_UNUSED(query); + Q_UNUSED(builder); + } + + static void resolveOutput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder) + { + Q_UNUSED(t); + Q_UNUSED(query); + Q_UNUSED(builder); + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxSqlQueryHelper_DeleteById.inl b/inl/QxDao/QxSqlQueryHelper_DeleteById.inl new file mode 100644 index 0000000..1a3b082 --- /dev/null +++ b/inl/QxDao/QxSqlQueryHelper_DeleteById.inl @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxSqlQueryHelper_DeleteById + { + + static void sql(QString &sql, qx::IxSqlQueryBuilder &builder, bool bSoftDelete) + { + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + qx::IxSqlQueryBuilder::sql_DeleteById(sql, builder, bSoftDelete); + } + + static void resolveInput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder) + { + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + qx::IxSqlQueryBuilder::resolveInput_DeleteById((&t), query, builder); + } + + static void resolveOutput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder) + { + Q_UNUSED(t); + Q_UNUSED(query); + Q_UNUSED(builder); + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxSqlQueryHelper_Exist.inl b/inl/QxDao/QxSqlQueryHelper_Exist.inl new file mode 100644 index 0000000..6eb21c3 --- /dev/null +++ b/inl/QxDao/QxSqlQueryHelper_Exist.inl @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxSqlQueryHelper_Exist + { + + static void sql(QString &sql, qx::IxSqlQueryBuilder &builder) + { + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + qx::IxSqlQueryBuilder::sql_Exist(sql, builder); + } + + static void resolveInput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder) + { + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + qx::IxDataMember *pId = builder.getDataId(); + qAssert(pId); + pId->setSqlPlaceHolder(query, (&t)); + } + + static void resolveOutput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder) + { + Q_UNUSED(t); + Q_UNUSED(query); + Q_UNUSED(builder); + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxSqlQueryHelper_FetchAll.inl b/inl/QxDao/QxSqlQueryHelper_FetchAll.inl new file mode 100644 index 0000000..16e8598 --- /dev/null +++ b/inl/QxDao/QxSqlQueryHelper_FetchAll.inl @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxSqlQueryHelper_FetchAll + { + + static void sql(QString &sql, qx::IxSqlQueryBuilder &builder) + { + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + qx::IxSqlQueryBuilder::sql_FetchAll(sql, builder); + } + + static void resolveInput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder) + { + Q_UNUSED(t); + Q_UNUSED(query); + Q_UNUSED(builder); + } + + static void resolveOutput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder) + { + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + qx::IxSqlQueryBuilder::resolveOutput_FetchAll((&t), query, builder); + } + + static void sql(QString &sql, qx::IxSqlQueryBuilder &builder, const QStringList &columns) + { + if ((columns.count() <= 0) || (columns.at(0) == "*")) + { + QxSqlQueryHelper_FetchAll::sql(sql, builder); + return; + } + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + qx::IxSqlQueryBuilder::sql_FetchAll(sql, builder, columns); + } + + static void resolveInput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder, const QStringList &columns) + { + if ((columns.count() <= 0) || (columns.at(0) == "*")) + { + QxSqlQueryHelper_FetchAll::resolveInput(t, query, builder); + return; + } + } + + static void resolveOutput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder, const QStringList &columns) + { + if ((columns.count() <= 0) || (columns.at(0) == "*")) + { + QxSqlQueryHelper_FetchAll::resolveOutput(t, query, builder); + return; + } + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + qx::IxSqlQueryBuilder::resolveOutput_FetchAll((&t), query, builder, columns); + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxSqlQueryHelper_FetchAll_WithRelation.inl b/inl/QxDao/QxSqlQueryHelper_FetchAll_WithRelation.inl new file mode 100644 index 0000000..72d5034 --- /dev/null +++ b/inl/QxDao/QxSqlQueryHelper_FetchAll_WithRelation.inl @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxSqlQueryHelper_FetchAll_WithRelation + { + + static void sql(qx::QxSqlRelationLinked *pRelationX, QString &sql, qx::IxSqlQueryBuilder &builder) + { + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + if (!pRelationX) + { + qAssert(false); + QxSqlQueryHelper_FetchAll::sql(sql, builder); + return; + } + qx::IxSqlQueryBuilder::sql_FetchAll_WithRelation(pRelationX, sql, builder); + } + + static void resolveInput(qx::QxSqlRelationLinked *pRelationX, T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder) + { + Q_UNUSED(pRelationX); + Q_UNUSED(t); + Q_UNUSED(query); + Q_UNUSED(builder); + } + + static void resolveOutput(qx::QxSqlRelationLinked *pRelationX, T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder) + { + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + if (!pRelationX) + { + qAssert(false); + QxSqlQueryHelper_FetchAll::resolveOutput(t, query, builder); + return; + } + qx::IxSqlQueryBuilder::resolveOutput_FetchAll_WithRelation(pRelationX, (&t), query, builder); + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxSqlQueryHelper_FetchById.inl b/inl/QxDao/QxSqlQueryHelper_FetchById.inl new file mode 100644 index 0000000..1efd288 --- /dev/null +++ b/inl/QxDao/QxSqlQueryHelper_FetchById.inl @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxSqlQueryHelper_FetchById + { + + static void sql(QString &sql, qx::IxSqlQueryBuilder &builder) + { + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + qx::IxSqlQueryBuilder::sql_FetchById(sql, builder); + } + + static void resolveInput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder) + { + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + qx::IxDataMember *pId = builder.getDataId(); + qAssert(pId); + pId->setSqlPlaceHolder(query, (&t)); + } + + static void resolveOutput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder) + { + QxSqlQueryHelper_FetchAll::resolveOutput(t, query, builder); + } + + static void sql(QString &sql, qx::IxSqlQueryBuilder &builder, const QStringList &columns) + { + if ((columns.count() <= 0) || (columns.at(0) == "*")) + { + QxSqlQueryHelper_FetchById::sql(sql, builder); + return; + } + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + qx::IxSqlQueryBuilder::sql_FetchById(sql, builder, columns); + } + + static void resolveInput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder, const QStringList &columns) + { + if ((columns.count() <= 0) || (columns.at(0) == "*")) + { + QxSqlQueryHelper_FetchById::resolveInput(t, query, builder); + return; + } + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + qx::IxDataMember *pId = builder.getDataId(); + qAssert(pId); + pId->setSqlPlaceHolder(query, (&t)); + } + + static void resolveOutput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder, const QStringList &columns) + { + QxSqlQueryHelper_FetchAll::resolveOutput(t, query, builder, columns); + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxSqlQueryHelper_FetchById_WithRelation.inl b/inl/QxDao/QxSqlQueryHelper_FetchById_WithRelation.inl new file mode 100644 index 0000000..f231b58 --- /dev/null +++ b/inl/QxDao/QxSqlQueryHelper_FetchById_WithRelation.inl @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxSqlQueryHelper_FetchById_WithRelation + { + + static void sql(qx::QxSqlRelationLinked *pRelationX, QString &sql, qx::IxSqlQueryBuilder &builder) + { + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + if (!pRelationX) + { + qAssert(false); + QxSqlQueryHelper_FetchById::sql(sql, builder); + return; + } + qx::IxSqlQueryBuilder::sql_FetchById_WithRelation(pRelationX, sql, builder); + } + + static void resolveInput(qx::QxSqlRelationLinked *pRelationX, T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder) + { + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + if (!pRelationX) + { + qAssert(false); + QxSqlQueryHelper_FetchById::resolveInput(t, query, builder); + return; + } + qx::IxDataMember *pId = builder.getDataId(); + qAssert(pId); + pId->setSqlPlaceHolder(query, (&t)); + } + + static void resolveOutput(qx::QxSqlRelationLinked *pRelationX, T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder) + { + QxSqlQueryHelper_FetchAll_WithRelation::resolveOutput(pRelationX, t, query, builder); + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxSqlQueryHelper_Insert.inl b/inl/QxDao/QxSqlQueryHelper_Insert.inl new file mode 100644 index 0000000..015507c --- /dev/null +++ b/inl/QxDao/QxSqlQueryHelper_Insert.inl @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxSqlQueryHelper_Insert + { + + static void sql(QString &sql, qx::IxSqlQueryBuilder &builder) + { + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + qx::IxSqlQueryBuilder::sql_Insert(sql, builder); + } + + static void resolveInput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder) + { + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + qx::IxSqlQueryBuilder::resolveInput_Insert((&t), query, builder); + } + + static void resolveOutput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder) + { + Q_UNUSED(t); + Q_UNUSED(query); + Q_UNUSED(builder); + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDao/QxSqlQueryHelper_Update.inl b/inl/QxDao/QxSqlQueryHelper_Update.inl new file mode 100644 index 0000000..c5f2fec --- /dev/null +++ b/inl/QxDao/QxSqlQueryHelper_Update.inl @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + namespace dao + { + namespace detail + { + + template + struct QxSqlQueryHelper_Update + { + + static void sql(QString &sql, qx::IxSqlQueryBuilder &builder) + { + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + qx::IxSqlQueryBuilder::sql_Update(sql, builder); + } + + static void resolveInput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder) + { + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + qx::IxSqlQueryBuilder::resolveInput_Update((&t), query, builder); + } + + static void resolveOutput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder) + { + Q_UNUSED(t); + Q_UNUSED(query); + Q_UNUSED(builder); + } + + static void sql(QString &sql, qx::IxSqlQueryBuilder &builder, const QStringList &columns) + { + if ((columns.count() <= 0) || (columns.at(0) == "*")) + { + QxSqlQueryHelper_Update::sql(sql, builder); + return; + } + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + qx::IxSqlQueryBuilder::sql_Update(sql, builder, columns); + } + + static void resolveInput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder, const QStringList &columns) + { + if ((columns.count() <= 0) || (columns.at(0) == "*")) + { + QxSqlQueryHelper_Update::resolveInput(t, query, builder); + return; + } + static_assert(qx::trait::is_qx_registered::value, "qx::trait::is_qx_registered::value"); + qx::IxSqlQueryBuilder::resolveInput_Update((&t), query, builder, columns); + } + + static void resolveOutput(T &t, QSqlQuery &query, qx::IxSqlQueryBuilder &builder, const QStringList &columns) + { + if ((columns.count() <= 0) || (columns.at(0) == "*")) + { + QxSqlQueryHelper_Update::resolveOutput(t, query, builder); + return; + } + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/inl/QxDataMember/QxDataMember.inl b/inl/QxDataMember/QxDataMember.inl new file mode 100644 index 0000000..b9484b7 --- /dev/null +++ b/inl/QxDataMember/QxDataMember.inl @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + +} // namespace qx diff --git a/inl/QxDataMember/QxDataMemberX.inl b/inl/QxDataMember/QxDataMemberX.inl new file mode 100644 index 0000000..6c638ec --- /dev/null +++ b/inl/QxDataMember/QxDataMemberX.inl @@ -0,0 +1,463 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + + template + IxDataMember *QxDataMemberX::initId(IxDataMember *pId, long lVersion) + { + if (!pId) + { + qAssert(false); + return NULL; + } + qAssert(lVersion <= getVersion()); + QString sKey = pId->getKey(); + + pId->setSqlType(qx::trait::get_sql_type::type_primary_key>::get()); + pId->setAutoIncrement(std::is_integral::type_primary_key>::value); + pId->setNameParent(getName()); + pId->setIsPrimaryKey(true); + pId->setNotNull(true); + pId->setVersion(lVersion); + pId->setParent(this); + this->setId(pId); + this->getListDataMemberRef().insert(sKey, pId); + + return pId; + } + + template + IxDataMember *QxDataMemberX::initData(IxDataMember *pData, long lVersion) + { + if (!pData) + { + qAssert(false); + return NULL; + } + qAssert(lVersion <= getVersion()); + QString sKey = pData->getKey(); + + pData->setVersion(lVersion); + pData->setNameParent(getName()); + pData->setParent(this); + this->getListDataMemberRef().insert(sKey, pData); + + return pData; + } + + template + IxDataMember *QxDataMemberX::initPImpl(IxDataMember *pImpl) + { + if (!pImpl) + { + qAssert(false); + return NULL; + } + QString sKey = pImpl->getKey(); + if (this->getListPImplRef().exist(sKey)) + { + qAssert(false); + return NULL; + } + + pImpl->setNameParent(getName()); + pImpl->setParent(this); + this->getListPImplRef().insert(sKey, pImpl); + + return pImpl; + } + + template + template + IxDataMember *QxDataMemberX::add(V U::*pData, const QString &sKey, long lVersion /* = 0 */, bool bSerialize /* = true */, bool bDao /* = true */) + { + typedef std::is_base_of is_valid_class_tmp; + static_assert(is_valid_class_tmp::value, "is_valid_class_tmp::value"); + if (exist_WithDaoStrategy(sKey)) + { + qAssert(false); + return NULL; + } + + IxDataMember *pNewDataMember = new QxDataMember(pData, sKey, lVersion, bSerialize, bDao); + pNewDataMember->setSqlType(qx::trait::get_sql_type::get()); + return this->initData(pNewDataMember, lVersion); + } + + template + IxDataMember *QxDataMemberX::add(const QString &sKey, long lVersion) + { + if (!qx::trait::qt_meta_object::is_valid) + { + qDebug("[QxOrm] qx::QxDataMemberX::add() : '%s'", "Qt introspection engine works only with QObject class"); + qAssert(false); + return NULL; + } + if (exist_WithDaoStrategy(sKey)) + { + qAssert(false); + return NULL; + } + return this->initData(new QxDataMember_QObject(qx::trait::qt_meta_object::get(), sKey), lVersion); + } + + template + IxDataMember *QxDataMemberX::id(typename QxDataMemberX::type_primary_key T::*pDataMemberId, const QString &sKey, long lVersion /* = 0 */) + { + if (getId_WithDaoStrategy()) + { + qDebug("[QxOrm] qx::QxDataMemberX id (primary key) already defined '%s'", qPrintable(getId_WithDaoStrategy()->getName())); + } + if (exist_WithDaoStrategy(sKey) || getId_WithDaoStrategy()) + { + qAssert(false); + return getId_WithDaoStrategy(); + } + return this->initId(new QxDataMember::type_primary_key, T>(pDataMemberId, sKey, lVersion, true, true), lVersion); + } + + template + IxDataMember *QxDataMemberX::id(const QString &sKey, long lVersion) + { + if (!qx::trait::qt_meta_object::is_valid) + { + qDebug("[QxOrm] qx::QxDataMemberX::id() : '%s'", "Qt introspection engine works only with QObject class"); + qAssert(false); + return NULL; + } + if (getId_WithDaoStrategy()) + { + qDebug("[QxOrm] qx::QxDataMemberX id (primary key) already defined '%s'", qPrintable(getId_WithDaoStrategy()->getName())); + } + if (exist_WithDaoStrategy(sKey) || getId_WithDaoStrategy()) + { + qAssert(false); + return getId_WithDaoStrategy(); + } + return this->initId(new QxDataMember_QObject(qx::trait::qt_meta_object::get(), sKey), lVersion); + } + + template + template + IxSqlRelation *QxDataMemberX::relationOneToOne(V U::*pData, const QString &sKey, long lVersion /* = 0 */) + { + typedef std::is_base_of is_valid_class_tmp; + static_assert(is_valid_class_tmp::value, "is_valid_class_tmp::value"); + if (exist_WithDaoStrategy(sKey)) + { + qAssert(false); + return NULL; + } + + IxDataMember *pDataMember = this->add(pData, sKey, lVersion); + IxSqlRelation *pSqlRelation = new QxSqlRelation_OneToOne(pDataMember); + pDataMember->setSqlRelation(pSqlRelation); + + return pSqlRelation; + } + + template + template + IxSqlRelation *QxDataMemberX::relationManyToOne(V U::*pData, const QString &sKey, long lVersion /* = 0 */) + { + typedef std::is_base_of is_valid_class_tmp; + static_assert(is_valid_class_tmp::value, "is_valid_class_tmp::value"); + if (exist_WithDaoStrategy(sKey)) + { + qAssert(false); + return NULL; + } + + IxDataMember *pDataMember = this->add(pData, sKey, lVersion); + IxSqlRelation *pSqlRelation = new QxSqlRelation_ManyToOne(pDataMember); + pDataMember->setSqlRelation(pSqlRelation); + + return pSqlRelation; + } + + template + template + IxSqlRelation *QxDataMemberX::relationOneToMany(V U::*pData, const QString &sKey, const QString &sForeignKey, long lVersion /* = 0 */) + { + typedef std::is_base_of is_valid_class_tmp; + static_assert(is_valid_class_tmp::value, "is_valid_class_tmp::value"); + if (exist_WithDaoStrategy(sKey)) + { + qAssert(false); + return NULL; + } + + IxDataMember *pDataMember = this->add(pData, sKey, lVersion); + IxSqlRelation *pSqlRelation = new QxSqlRelation_OneToMany(pDataMember, sForeignKey); + pDataMember->setSqlRelation(pSqlRelation); + + return pSqlRelation; + } + + template + template + IxSqlRelation *QxDataMemberX::relationManyToMany(V U::*pData, const QString &sKey, const QString &sExtraTable, const QString &sForeignKeyOwner, const QString &sForeignKeyDataType, long lVersion /* = 0 */) + { + typedef std::is_base_of is_valid_class_tmp; + static_assert(is_valid_class_tmp::value, "is_valid_class_tmp::value"); + if (exist_WithDaoStrategy(sKey)) + { + qAssert(false); + return NULL; + } + + IxDataMember *pDataMember = this->add(pData, sKey, lVersion); + IxSqlRelation *pSqlRelation = new QxSqlRelation_ManyToMany(pDataMember, sExtraTable, sForeignKeyOwner, sForeignKeyDataType); + pDataMember->setSqlRelation(pSqlRelation); + + return pSqlRelation; + } + + template + template + IxDataMember *QxDataMemberX::pimpl(V U::*pData, const QString &sKey) + { + IxDataMember *pNewDataMember = new QxDataMember_PImpl(pData, sKey); + return this->initPImpl(pNewDataMember); + } + + template + template + IxDataMember *QxDataMemberX::id(typename QxDataMemberX::type_primary_key U::*pDataMemberId, const QString &sKey, long lVersion, IxDataMember *pImpl) + { + if (pImpl) + { + qAssert((pImpl->getParent()) && (pImpl->getParent()->getClass() == this->getClass())); + } + if (getId_WithDaoStrategy()) + { + qDebug("[QxOrm] qx::QxDataMemberX id (primary key) already defined '%s'", qPrintable(getId_WithDaoStrategy()->getName())); + } + if (exist_WithDaoStrategy(sKey) || getId_WithDaoStrategy()) + { + qAssert(false); + return getId_WithDaoStrategy(); + } + return this->initId(new QxDataMember::type_primary_key, U>(pDataMemberId, sKey, lVersion, true, true, pImpl), lVersion); + } + + template + template + IxDataMember *QxDataMemberX::add(V U::*pData, const QString &sKey, long lVersion, bool bSerialize, bool bDao, IxDataMember *pImpl) + { + if (pImpl) + { + qAssert((pImpl->getParent()) && (pImpl->getParent()->getClass() == this->getClass())); + } + if (exist_WithDaoStrategy(sKey)) + { + qAssert(false); + return NULL; + } + + IxDataMember *pNewDataMember = new QxDataMember(pData, sKey, lVersion, bSerialize, bDao, pImpl); + pNewDataMember->setSqlType(qx::trait::get_sql_type::get()); + return this->initData(pNewDataMember, lVersion); + } + + template + template + IxSqlRelation *QxDataMemberX::relationOneToOne(V U::*pData, const QString &sKey, long lVersion, IxDataMember *pImpl) + { + if (pImpl) + { + qAssert((pImpl->getParent()) && (pImpl->getParent()->getClass() == this->getClass())); + } + if (exist_WithDaoStrategy(sKey)) + { + qAssert(false); + return NULL; + } + + IxDataMember *pDataMember = this->add(pData, sKey, lVersion, true, true, pImpl); + IxSqlRelation *pSqlRelation = new QxSqlRelation_OneToOne(pDataMember); + pDataMember->setSqlRelation(pSqlRelation); + + return pSqlRelation; + } + + template + template + IxSqlRelation *QxDataMemberX::relationManyToOne(V U::*pData, const QString &sKey, long lVersion, IxDataMember *pImpl) + { + if (pImpl) + { + qAssert((pImpl->getParent()) && (pImpl->getParent()->getClass() == this->getClass())); + } + if (exist_WithDaoStrategy(sKey)) + { + qAssert(false); + return NULL; + } + + IxDataMember *pDataMember = this->add(pData, sKey, lVersion, true, true, pImpl); + IxSqlRelation *pSqlRelation = new QxSqlRelation_ManyToOne(pDataMember); + pDataMember->setSqlRelation(pSqlRelation); + + return pSqlRelation; + } + + template + template + IxSqlRelation *QxDataMemberX::relationOneToMany(V U::*pData, const QString &sKey, const QString &sForeignKey, long lVersion, IxDataMember *pImpl) + { + if (pImpl) + { + qAssert((pImpl->getParent()) && (pImpl->getParent()->getClass() == this->getClass())); + } + if (exist_WithDaoStrategy(sKey)) + { + qAssert(false); + return NULL; + } + + IxDataMember *pDataMember = this->add(pData, sKey, lVersion, true, true, pImpl); + IxSqlRelation *pSqlRelation = new QxSqlRelation_OneToMany(pDataMember, sForeignKey); + pDataMember->setSqlRelation(pSqlRelation); + + return pSqlRelation; + } + + template + template + IxSqlRelation *QxDataMemberX::relationManyToMany(V U::*pData, const QString &sKey, const QString &sExtraTable, const QString &sForeignKeyOwner, const QString &sForeignKeyDataType, long lVersion, IxDataMember *pImpl) + { + if (pImpl) + { + qAssert((pImpl->getParent()) && (pImpl->getParent()->getClass() == this->getClass())); + } + if (exist_WithDaoStrategy(sKey)) + { + qAssert(false); + return NULL; + } + + IxDataMember *pDataMember = this->add(pData, sKey, lVersion, true, true, pImpl); + IxSqlRelation *pSqlRelation = new QxSqlRelation_ManyToMany(pDataMember, sExtraTable, sForeignKeyOwner, sForeignKeyDataType); + pDataMember->setSqlRelation(pSqlRelation); + + return pSqlRelation; + } + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + + template + template + inline void QxDataMemberX::toArchive(const T *pOwner, Archive &ar, const unsigned int file_version) const + { + Q_UNUSED(file_version); + const QxCollection &lstDataMember = this->getListDataMemberRef(); + _foreach_if(IxDataMember * pDataMember, lstDataMember, (pDataMember->getSerialize())) + pDataMember->toArchive(pOwner, ar); + } + + template + template + inline void QxDataMemberX::fromArchive(T *pOwner, Archive &ar, const unsigned int file_version) + { + Q_UNUSED(file_version); + QxCollection &lstDataMember = this->getListDataMemberRef(); + _foreach_if(IxDataMember * pDataMember, lstDataMember, (pDataMember->getSerialize() && (pDataMember->getVersion() <= static_cast(file_version)))) + pDataMember->fromArchive(pOwner, ar); + } + +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + template <> + QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE + QxDataMemberX::QxDataMemberX() : IxDataMemberX(), QxSingleton>("qx::QxDataMemberX_no_base_class_defined") { ; } + + template <> + QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE + QxDataMemberX::QxDataMemberX() : IxDataMemberX(), QxSingleton>("qx::QxDataMemberX_QObject") { ; } + + template <> + QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE long QxDataMemberX::count_WithDaoStrategy_Helper() const { return 0; } + + template <> + QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE bool QxDataMemberX::exist_WithDaoStrategy_Helper(const QString &sKey) const + { + Q_UNUSED(sKey); + return false; + } + + template <> + QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE IxDataMember *QxDataMemberX::get_WithDaoStrategy_Helper(long lIndex) const + { + Q_UNUSED(lIndex); + return NULL; + } + + template <> + QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE IxDataMember *QxDataMemberX::get_WithDaoStrategy_Helper(const QString &sKey) const + { + Q_UNUSED(sKey); + return NULL; + } + + template <> + QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE IxDataMember *QxDataMemberX::getId_WithDaoStrategy_Helper() const { return NULL; } + + template <> + QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE long QxDataMemberX::count_WithDaoStrategy_Helper() const { return 0; } + + template <> + QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE bool QxDataMemberX::exist_WithDaoStrategy_Helper(const QString &sKey) const + { + Q_UNUSED(sKey); + return false; + } + + template <> + QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE IxDataMember *QxDataMemberX::get_WithDaoStrategy_Helper(long lIndex) const + { + Q_UNUSED(lIndex); + return NULL; + } + + template <> + QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE IxDataMember *QxDataMemberX::get_WithDaoStrategy_Helper(const QString &sKey) const + { + Q_UNUSED(sKey); + return NULL; + } + + template <> + QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE IxDataMember *QxDataMemberX::getId_WithDaoStrategy_Helper() const { return NULL; } + +} // namespace qx diff --git a/inl/QxFactory/QxFactory.inl b/inl/QxFactory/QxFactory.inl new file mode 100644 index 0000000..0ae7c67 --- /dev/null +++ b/inl/QxFactory/QxFactory.inl @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) +#pragma warning(disable : 4094) +#endif // _MSC_VER + +#include + +#ifndef QX_REGISTER_FACTORY_HPP +#define QX_REGISTER_FACTORY_COMPLEX_CLASS_NAME_HPP(className, classNameFormatted) \ + extern qx::QxFactory G_QX_REGISTER_FACTORY_##classNameFormatted; +#define QX_REGISTER_FACTORY_HPP(className) \ + QX_REGISTER_FACTORY_COMPLEX_CLASS_NAME_HPP(className, className) +#endif // QX_REGISTER_FACTORY_HPP + +#ifndef QX_REGISTER_FACTORY_CPP +#define QX_REGISTER_FACTORY_COMPLEX_CLASS_NAME_CPP(className, classNameFormatted) \ + qx::QxFactory G_QX_REGISTER_FACTORY_##classNameFormatted(QString(#className)); +#define QX_REGISTER_FACTORY_CPP(className) \ + QX_REGISTER_FACTORY_COMPLEX_CLASS_NAME_CPP(className, className) +#endif // QX_REGISTER_FACTORY_CPP + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER diff --git a/inl/QxRegister/QxClass.inl b/inl/QxRegister/QxClass.inl new file mode 100644 index 0000000..4c85f1c --- /dev/null +++ b/inl/QxRegister/QxClass.inl @@ -0,0 +1,361 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +namespace qx +{ + + template + void QxClass::init() + { + this->setDataMemberX(QxDataMemberX::getSingleton()); + this->getDataMemberX()->setClass(this); + this->setFctMemberX(new IxFunctionX()); + this->setFctStaticX(new IxFunctionX()); + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + this->setVersion(boost::serialization::version::value); +#else // _QX_ENABLE_BOOST_SERIALIZATION + this->setVersion(qx::version::value); +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + this->setKey(qx::trait::get_class_name::get()); + this->setDaoStrategy(QxClass::type_base_class>::getSingleton()->getDaoStrategy()); + this->setName(qx::trait::get_class_name::get_xml_tag()); + this->updateClassX(); + beforeRegisterClass(); + } + + template + IxDataMember *QxClass::data(const QString &sKey, long lVersion) + { + return this->dataMemberX()->add(sKey, lVersion); + } + + template + template + IxDataMember *QxClass::data(V U::*pData, const QString &sKey, long lVersion /* = 0 */, bool bSerialize /* = true */, bool bDao /* = true */) + { + return this->dataMemberX()->add(pData, sKey, lVersion, bSerialize, bDao); + } + + template + IxDataMember *QxClass::id(const QString &sKey, long lVersion) + { + return this->dataMemberX()->id(sKey, lVersion); + } + + template + IxDataMember *QxClass::id(typename QxClass::type_primary_key T::*pDataMemberId, const QString &sKey, long lVersion /* = 0 */) + { + return this->dataMemberX()->id(pDataMemberId, sKey, lVersion); + } + + template + template + IxSqlRelation *QxClass::relationOneToOne(V U::*pData, const QString &sKey, long lVersion /* = 0 */) + { + return this->dataMemberX()->relationOneToOne(pData, sKey, lVersion); + } + + template + template + IxSqlRelation *QxClass::relationManyToOne(V U::*pData, const QString &sKey, long lVersion /* = 0 */) + { + return this->dataMemberX()->relationManyToOne(pData, sKey, lVersion); + } + + template + template + IxSqlRelation *QxClass::relationOneToMany(V U::*pData, const QString &sKey, const QString &sForeignKey, long lVersion /* = 0 */) + { + return this->dataMemberX()->relationOneToMany(pData, sKey, sForeignKey, lVersion); + } + + template + template + IxSqlRelation *QxClass::relationManyToMany(V U::*pData, const QString &sKey, const QString &sExtraTable, const QString &sForeignKeyOwner, const QString &sForeignKeyDataType, long lVersion /* = 0 */) + { + return this->dataMemberX()->relationManyToMany(pData, sKey, sExtraTable, sForeignKeyOwner, sForeignKeyDataType, lVersion); + } + + template + template + IxDataMember *QxClass::pimpl(V U::*pData, const QString &sKey /* = QString("_PIMPL_") */) + { + return this->dataMemberX()->pimpl(pData, sKey); + } + + template + template + IxDataMember *QxClass::id(typename QxClass::type_primary_key U::*pDataMemberId, const QString &sKey, long lVersion, IxDataMember *pImpl) + { + return this->dataMemberX()->id(pDataMemberId, sKey, lVersion, pImpl); + } + + template + template + IxDataMember *QxClass::data(V U::*pData, const QString &sKey, long lVersion, bool bSerialize, bool bDao, IxDataMember *pImpl) + { + return this->dataMemberX()->add(pData, sKey, lVersion, bSerialize, bDao, pImpl); + } + + template + template + IxSqlRelation *QxClass::relationOneToOne(V U::*pData, const QString &sKey, long lVersion, IxDataMember *pImpl) + { + return this->dataMemberX()->relationOneToOne(pData, sKey, lVersion, pImpl); + } + + template + template + IxSqlRelation *QxClass::relationManyToOne(V U::*pData, const QString &sKey, long lVersion, IxDataMember *pImpl) + { + return this->dataMemberX()->relationManyToOne(pData, sKey, lVersion, pImpl); + } + + template + template + IxSqlRelation *QxClass::relationOneToMany(V U::*pData, const QString &sKey, const QString &sForeignKey, long lVersion, IxDataMember *pImpl) + { + return this->dataMemberX()->relationOneToMany(pData, sKey, sForeignKey, lVersion, pImpl); + } + + template + template + IxSqlRelation *QxClass::relationManyToMany(V U::*pData, const QString &sKey, const QString &sExtraTable, const QString &sForeignKeyOwner, const QString &sForeignKeyDataType, long lVersion, IxDataMember *pImpl) + { + return this->dataMemberX()->relationManyToMany(pData, sKey, sExtraTable, sForeignKeyOwner, sForeignKeyDataType, lVersion, pImpl); + } + + template + IxFunction *QxClass::insertFct(IxFunction_ptr pFct, const QString &sKey) + { + if (!this->getFctMemberX() || sKey.isEmpty() || this->getFctMemberX()->exist(sKey)) + { + qAssert(false); + return NULL; + } + bool bInsertOk = this->getFctMemberX()->insert(sKey, pFct); + if (bInsertOk) + { + pFct->setKey(sKey); + } + return (bInsertOk ? pFct.get() : NULL); + } + + template + IxFunction *QxClass::insertFctStatic(IxFunction_ptr pFct, const QString &sKey) + { + if (!this->getFctStaticX() || sKey.isEmpty() || this->getFctStaticX()->exist(sKey)) + { + qAssert(false); + return NULL; + } + bool bInsertOk = this->getFctStaticX()->insert(sKey, pFct); + if (bInsertOk) + { + pFct->setKey(sKey); + } + return (bInsertOk ? pFct.get() : NULL); + } + + template + template + IxFunction *QxClass::fct_0(const typename QxFunction_0::type_fct &fct, const QString &sKey) + { + return this->insertFct(qx::function::bind_member_fct_0(fct), sKey); + } + + template + template + IxFunction *QxClass::fct_1(const typename QxFunction_1::type_fct &fct, const QString &sKey) + { + return this->insertFct(qx::function::bind_member_fct_1(fct), sKey); + } + + template + template + IxFunction *QxClass::fct_2(const typename QxFunction_2::type_fct &fct, const QString &sKey) + { + return this->insertFct(qx::function::bind_member_fct_2(fct), sKey); + } + + template + template + IxFunction *QxClass::fct_3(const typename QxFunction_3::type_fct &fct, const QString &sKey) + { + return this->insertFct(qx::function::bind_member_fct_3(fct), sKey); + } + + template + template + IxFunction *QxClass::fct_4(const typename QxFunction_4::type_fct &fct, const QString &sKey) + { + return this->insertFct(qx::function::bind_member_fct_4(fct), sKey); + } + + template + template + IxFunction *QxClass::fct_5(const typename QxFunction_5::type_fct &fct, const QString &sKey) + { + return this->insertFct(qx::function::bind_member_fct_5(fct), sKey); + } + + template + template + IxFunction *QxClass::fct_6(const typename QxFunction_6::type_fct &fct, const QString &sKey) + { + return this->insertFct(qx::function::bind_member_fct_6(fct), sKey); + } + + template + template + IxFunction *QxClass::fct_7(const typename QxFunction_7::type_fct &fct, const QString &sKey) + { + return this->insertFct(qx::function::bind_member_fct_7(fct), sKey); + } + + template + template + IxFunction *QxClass::fct_8(const typename QxFunction_8::type_fct &fct, const QString &sKey) + { + return this->insertFct(qx::function::bind_member_fct_8(fct), sKey); + } + + template + template + IxFunction *QxClass::fct_9(const typename QxFunction_9::type_fct &fct, const QString &sKey) + { + return this->insertFct(qx::function::bind_member_fct_9(fct), sKey); + } + + template + template + IxFunction *QxClass::fctStatic_0(const typename QxFunction_0::type_fct &fct, const QString &sKey) + { + return this->insertFctStatic(qx::function::bind_fct_0(fct), sKey); + } + + template + template + IxFunction *QxClass::fctStatic_1(const typename QxFunction_1::type_fct &fct, const QString &sKey) + { + return this->insertFctStatic(qx::function::bind_fct_1(fct), sKey); + } + + template + template + IxFunction *QxClass::fctStatic_2(const typename QxFunction_2::type_fct &fct, const QString &sKey) + { + return this->insertFctStatic(qx::function::bind_fct_2(fct), sKey); + } + + template + template + IxFunction *QxClass::fctStatic_3(const typename QxFunction_3::type_fct &fct, const QString &sKey) + { + return this->insertFctStatic(qx::function::bind_fct_3(fct), sKey); + } + + template + template + IxFunction *QxClass::fctStatic_4(const typename QxFunction_4::type_fct &fct, const QString &sKey) + { + return this->insertFctStatic(qx::function::bind_fct_4(fct), sKey); + } + + template + template + IxFunction *QxClass::fctStatic_5(const typename QxFunction_5::type_fct &fct, const QString &sKey) + { + return this->insertFctStatic(qx::function::bind_fct_5(fct), sKey); + } + + template + template + IxFunction *QxClass::fctStatic_6(const typename QxFunction_6::type_fct &fct, const QString &sKey) + { + return this->insertFctStatic(qx::function::bind_fct_6(fct), sKey); + } + + template + template + IxFunction *QxClass::fctStatic_7(const typename QxFunction_7::type_fct &fct, const QString &sKey) + { + return this->insertFctStatic(qx::function::bind_fct_7(fct), sKey); + } + + template + template + IxFunction *QxClass::fctStatic_8(const typename QxFunction_8::type_fct &fct, const QString &sKey) + { + return this->insertFctStatic(qx::function::bind_fct_8(fct), sKey); + } + + template + template + IxFunction *QxClass::fctStatic_9(const typename QxFunction_9::type_fct &fct, const QString &sKey) + { + return this->insertFctStatic(qx::function::bind_fct_9(fct), sKey); + } + + template <> + QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE + QxClass::QxClass() : IxClass(), QxSingleton>("qx::QxClass_no_base_class_defined") + { + setName("qx::trait::no_base_class_defined"); + setFinalClass(true); + } + + template <> + QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE + QxClass::QxClass() : IxClass(), QxSingleton>("qx::QxClass_QObject") + { + setKey("QObject"); + setName("QObject"); + setFinalClass(true); + } + + template <> + QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE void QxClass::registerClass() { ; } + + template <> + QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE void QxClass::registerClass() { ; } + + template <> + QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE void QxClass::beforeRegisterClass() { ; } + + template <> + QX_GCC_WORKAROUND_TEMPLATE_SPEC_INLINE void QxClass::beforeRegisterClass() { ; } + +} // namespace qx diff --git a/inl/QxSerialize/QxArchive.inl b/inl/QxSerialize/QxArchive.inl new file mode 100644 index 0000000..cb5a529 --- /dev/null +++ b/inl/QxSerialize/QxArchive.inl @@ -0,0 +1,416 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +namespace qx +{ + + template + qx_bool QxArchive_ToFile::toFile(const T &obj, const QString &sFileName, unsigned int flags /* = boost::archive::no_header */) + { + typedef typename qx::trait::archive_wide_traits::type_ofstream qx_type_ofstream; + qx_type_ofstream ofile(qPrintable(sFileName), (std::ios_base::binary | std::ios_base::out | std::ios_base::trunc)); + if (!ofile.is_open()) + { + return qx_bool(false, 0, QString("Cannot create or open file '") + sFileName + QString("'")); + } + ArchiveOutput oar(ofile, flags); + qx_bool bSerializeOk = false; + const char *sTag = QxClassName::get_xml_tag(); + QxBoostSerializeRegisterHelperX::helper(oar); + + try + { + oar << boost::serialization::make_nvp(sTag, obj); + bSerializeOk = ofile.good(); + } + catch (const boost::archive::archive_exception &e) + { + bSerializeOk.setDesc(QString(QX_STR_SERIALIZATION_ERROR).replace("%ERR%", e.what())); + } + catch (const std::exception &e) + { + bSerializeOk.setDesc(QString(QX_STR_SERIALIZATION_ERROR).replace("%ERR%", e.what())); + } + catch (...) + { + bSerializeOk.setDesc(QString(QX_STR_SERIALIZATION_ERROR).replace("%ERR%", "unknown error")); + } + + ofile.close(); + if (!bSerializeOk.getDesc().isEmpty()) + { + QString sMsg = QString("qx::QxArchive_ToFile::toFile() -> ") + bSerializeOk.getDesc(); + qDebug("[QxOrm] %s", qPrintable(sMsg)); + } + qAssert(bSerializeOk); + + return bSerializeOk; + } + + template + qx_bool QxArchive_FromFile::fromFile(T &obj, const QString &sFileName, unsigned int flags /* = boost::archive::no_header */) + { + typedef typename qx::trait::archive_wide_traits::type_ifstream qx_type_ifstream; + qx_type_ifstream ifile(qPrintable(sFileName), (std::ios_base::binary | std::ios_base::in)); + if (!ifile.is_open()) + { + return qx_bool(false, 0, QString("Cannot open file '") + sFileName + QString("'")); + } + ArchiveInput iar(ifile, flags); + qx_bool bDeserializeOk = false; + const char *sTag = QxClassName::get_xml_tag(); + QxBoostSerializeRegisterHelperX::helper(iar); + + try + { + iar >> boost::serialization::make_nvp(sTag, obj); + bDeserializeOk = true; + } + catch (const boost::archive::archive_exception &e) + { + bDeserializeOk.setDesc(QString(QX_STR_DESERIALIZATION_ERROR).replace("%ERR%", e.what())); + } + catch (const std::exception &e) + { + bDeserializeOk.setDesc(QString(QX_STR_DESERIALIZATION_ERROR).replace("%ERR%", e.what())); + } + catch (...) + { + bDeserializeOk.setDesc(QString(QX_STR_DESERIALIZATION_ERROR).replace("%ERR%", "unknown error")); + } + + ifile.close(); + if (!bDeserializeOk.getDesc().isEmpty()) + { + QString sMsg = QString("qx::QxArchive_FromFile::fromFile() -> ") + bDeserializeOk.getDesc(); + qDebug("[QxOrm] %s", qPrintable(sMsg)); + } + qAssert(bDeserializeOk); + + return bDeserializeOk; + } + + template + qx_bool QxArchive_ToFileCompressed::toFileCompressed(const T &obj, const QString &sFileName, unsigned int flags /* = boost::archive::no_header */, int iCompressionLevel /* = -1 */) + { + typedef typename qx::trait::archive_wide_traits::type_ostringstream qx_type_ostringstream; + qx_type_ostringstream oss(std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); + ArchiveOutput oar(oss, flags); + qx_bool bSerializeOk = false; + const char *sTag = QxClassName::get_xml_tag(); + QxBoostSerializeRegisterHelperX::helper(oar); + + try + { + oar << boost::serialization::make_nvp(sTag, obj); + bSerializeOk = oss.good(); + } + catch (const boost::archive::archive_exception &e) + { + bSerializeOk.setDesc(QString(QX_STR_SERIALIZATION_ERROR).replace("%ERR%", e.what())); + } + catch (const std::exception &e) + { + bSerializeOk.setDesc(QString(QX_STR_SERIALIZATION_ERROR).replace("%ERR%", e.what())); + } + catch (...) + { + bSerializeOk.setDesc(QString(QX_STR_SERIALIZATION_ERROR).replace("%ERR%", "unknown error")); + } + + qAssert(bSerializeOk); + if (!bSerializeOk.getDesc().isEmpty()) + { + QString sMsg = QString("qx::QxArchive_ToFileCompressed::toFileCompressed() -> ") + bSerializeOk.getDesc(); + qDebug("[QxOrm] %s", qPrintable(sMsg)); + } + if (!bSerializeOk) + { + return false; + } + + oss.seekp(0, std::ios::end); + QByteArray compressed = qCompress((unsigned char *)(oss.str().c_str()), static_cast(oss.tellp()), iCompressionLevel); + if (compressed.isEmpty()) + { + qAssert(false); + return qx_bool(false, 0, QString("Cannot compress archive")); + } + + typedef typename qx::trait::archive_wide_traits::type_ofstream qx_type_ofstream; + qx_type_ofstream ofile(qPrintable(sFileName), (std::ios_base::binary | std::ios_base::out | std::ios_base::trunc)); + if (!ofile.is_open()) + { + return qx_bool(false, 0, QString("Cannot create or open compressed file '") + sFileName + QString("'")); + } + ofile.write(compressed.constData(), compressed.size()); + ofile.close(); + + return true; + } + + template + qx_bool QxArchive_FromFileCompressed::fromFileCompressed(T &obj, const QString &sFileName, unsigned int flags /* = boost::archive::no_header */) + { + typedef typename qx::trait::archive_wide_traits::type_ifstream qx_type_ifstream; + typedef typename qx::trait::archive_wide_traits::type_char qx_type_char; + typedef typename qx::trait::archive_wide_traits::type_string qx_type_string; + typedef typename qx::trait::archive_wide_traits::type_istringstream qx_type_istringstream; + + qx_type_ifstream ifile(qPrintable(sFileName), (std::ios_base::binary | std::ios_base::in)); + if (!ifile.is_open()) + { + return qx_bool(false, 0, QString("Cannot open compressed file '") + sFileName + QString("'")); + } + ifile.seekg(0, std::ios::end); + int iSize = ifile.tellg(); + ifile.seekg(0, std::ios::beg); + qx_type_char *buffer = new qx_type_char[iSize]; + ifile.read(buffer, iSize); + ifile.close(); + + QByteArray uncompressed = qUncompress((unsigned char *)(buffer), iSize); + if (uncompressed.isEmpty()) + { + delete[] buffer; + qAssert(false); + return qx_bool(false, 0, QString("Cannot uncompress archive")); + } + delete[] buffer; + + qx_type_string str(uncompressed.constData(), uncompressed.count()); + qx_type_istringstream iss(str, (std::ios_base::binary | std::ios_base::in)); + ArchiveInput iar(iss, flags); + qx_bool bDeserializeOk = false; + const char *sTag = QxClassName::get_xml_tag(); + QxBoostSerializeRegisterHelperX::helper(iar); + + try + { + iar >> boost::serialization::make_nvp(sTag, obj); + bDeserializeOk = true; + } + catch (const boost::archive::archive_exception &e) + { + bDeserializeOk.setDesc(QString(QX_STR_DESERIALIZATION_ERROR).replace("%ERR%", e.what())); + } + catch (const std::exception &e) + { + bDeserializeOk.setDesc(QString(QX_STR_DESERIALIZATION_ERROR).replace("%ERR%", e.what())); + } + catch (...) + { + bDeserializeOk.setDesc(QString(QX_STR_DESERIALIZATION_ERROR).replace("%ERR%", "unknown error")); + } + + if (!bDeserializeOk.getDesc().isEmpty()) + { + QString sMsg = QString("qx::QxArchive_FromFileCompressed::fromFileCompressed() -> ") + bDeserializeOk.getDesc(); + qDebug("[QxOrm] %s", qPrintable(sMsg)); + } + qAssert(bDeserializeOk); + + return bDeserializeOk; + } + + template + QString QxArchive_ToString::toString(const T &obj, unsigned int flags /* = boost::archive::no_header */) + { + typedef typename qx::trait::archive_wide_traits::type_ostringstream qx_type_ostringstream; + qx_type_ostringstream oss(std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); + ArchiveOutput oar(oss, flags); + qx_bool bSerializeOk = false; + const char *sTag = QxClassName::get_xml_tag(); + QxBoostSerializeRegisterHelperX::helper(oar); + + try + { + oar << boost::serialization::make_nvp(sTag, obj); + bSerializeOk = oss.good(); + } + catch (const boost::archive::archive_exception &e) + { + bSerializeOk.setDesc(QString(QX_STR_SERIALIZATION_ERROR).replace("%ERR%", e.what())); + } + catch (const std::exception &e) + { + bSerializeOk.setDesc(QString(QX_STR_SERIALIZATION_ERROR).replace("%ERR%", e.what())); + } + catch (...) + { + bSerializeOk.setDesc(QString(QX_STR_SERIALIZATION_ERROR).replace("%ERR%", "unknown error")); + } + + if (!bSerializeOk.getDesc().isEmpty()) + { + QString sMsg = QString("qx::QxArchive_ToString::toString() -> ") + bSerializeOk.getDesc(); + qDebug("[QxOrm] %s", qPrintable(sMsg)); + } + qAssert(bSerializeOk); + + return (bSerializeOk ? qx::trait::archive_wide_traits::toQString(oss.str()) : QString()); + } + + template + qx_bool QxArchive_FromString::fromString(T &obj, const QString &sString, unsigned int flags /* = boost::archive::no_header */) + { + typedef typename qx::trait::archive_wide_traits::type_string qx_type_string; + typedef typename qx::trait::archive_wide_traits::type_istringstream qx_type_istringstream; + + qx_type_string str; + if (sString.isEmpty()) + { + return false; + } + qx::trait::archive_wide_traits::fromQString(sString, str); + qx_type_istringstream iss(str, (std::ios_base::binary | std::ios_base::in)); + ArchiveInput iar(iss, flags); + qx_bool bDeserializeOk = false; + const char *sTag = QxClassName::get_xml_tag(); + QxBoostSerializeRegisterHelperX::helper(iar); + + try + { + iar >> boost::serialization::make_nvp(sTag, obj); + bDeserializeOk = true; + } + catch (const boost::archive::archive_exception &e) + { + bDeserializeOk.setDesc(QString(QX_STR_DESERIALIZATION_ERROR).replace("%ERR%", e.what())); + } + catch (const std::exception &e) + { + bDeserializeOk.setDesc(QString(QX_STR_DESERIALIZATION_ERROR).replace("%ERR%", e.what())); + } + catch (...) + { + bDeserializeOk.setDesc(QString(QX_STR_DESERIALIZATION_ERROR).replace("%ERR%", "unknown error")); + } + + if (!bDeserializeOk.getDesc().isEmpty()) + { + QString sMsg = QString("qx::QxArchive_FromString::fromString() -> ") + bDeserializeOk.getDesc(); + qDebug("[QxOrm] %s", qPrintable(sMsg)); + } + qAssert(bDeserializeOk); + + return bDeserializeOk; + } + + template + QByteArray QxArchive_ToByteArray::toByteArray(const T &obj, type_string *owner /* = NULL */, unsigned int flags /* = boost::archive::no_header */) + { + typedef typename qx::trait::archive_wide_traits::type_ostringstream qx_type_ostringstream; + qx_type_ostringstream oss(std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); + ArchiveOutput oar(oss, flags); + qx_bool bSerializeOk = false; + const char *sTag = QxClassName::get_xml_tag(); + QxBoostSerializeRegisterHelperX::helper(oar); + + try + { + oar << boost::serialization::make_nvp(sTag, obj); + bSerializeOk = oss.good(); + } + catch (const boost::archive::archive_exception &e) + { + bSerializeOk.setDesc(QString(QX_STR_SERIALIZATION_ERROR).replace("%ERR%", e.what())); + } + catch (const std::exception &e) + { + bSerializeOk.setDesc(QString(QX_STR_SERIALIZATION_ERROR).replace("%ERR%", e.what())); + } + catch (...) + { + bSerializeOk.setDesc(QString(QX_STR_SERIALIZATION_ERROR).replace("%ERR%", "unknown error")); + } + + if (!bSerializeOk.getDesc().isEmpty()) + { + QString sMsg = QString("qx::QxArchive_ToByteArray::toByteArray() -> ") + bSerializeOk.getDesc(); + qDebug("[QxOrm] %s", qPrintable(sMsg)); + } + qAssert(bSerializeOk); + + return (bSerializeOk ? qx::trait::archive_wide_traits::toQByteArray(oss.str(), owner) : QByteArray()); + } + + template + qx_bool QxArchive_FromByteArray::fromByteArray(T &obj, const QByteArray &data, unsigned int flags /* = boost::archive::no_header */) + { + typedef typename qx::trait::archive_wide_traits::type_string qx_type_string; + typedef typename qx::trait::archive_wide_traits::type_istringstream qx_type_istringstream; + + qx_type_string str; + if (data.size() <= 0) + { + return false; + } + qx::trait::archive_wide_traits::fromQByteArray(data, str); + qx_type_istringstream iss(str, (std::ios_base::binary | std::ios_base::in)); + ArchiveInput iar(iss, flags); + qx_bool bDeserializeOk = false; + const char *sTag = QxClassName::get_xml_tag(); + QxBoostSerializeRegisterHelperX::helper(iar); + + try + { + iar >> boost::serialization::make_nvp(sTag, obj); + bDeserializeOk = true; + } + catch (const boost::archive::archive_exception &e) + { + bDeserializeOk.setDesc(QString(QX_STR_DESERIALIZATION_ERROR).replace("%ERR%", e.what())); + } + catch (const std::exception &e) + { + bDeserializeOk.setDesc(QString(QX_STR_DESERIALIZATION_ERROR).replace("%ERR%", e.what())); + } + catch (...) + { + bDeserializeOk.setDesc(QString(QX_STR_DESERIALIZATION_ERROR).replace("%ERR%", "unknown error")); + } + + if (!bDeserializeOk.getDesc().isEmpty()) + { + QString sMsg = QString("qx::QxArchive_FromByteArray::fromByteArray() -> ") + bDeserializeOk.getDesc(); + qDebug("[QxOrm] %s", qPrintable(sMsg)); + } + qAssert(bDeserializeOk); + + return bDeserializeOk; + } + +} // namespace qx + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/inl/QxSerialize/QxSerializeInvoker.inl b/inl/QxSerialize/QxSerializeInvoker.inl new file mode 100644 index 0000000..ce97208 --- /dev/null +++ b/inl/QxSerialize/QxSerializeInvoker.inl @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +namespace boost +{ + namespace serialization + { + + template + inline void serialize(Archive &ar, qx::trait::no_base_class_defined &t, const unsigned int file_version) + { + Q_UNUSED(ar); + Q_UNUSED(t); + Q_UNUSED(file_version); + } + + } // namespace serialization +} // namespace boost + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/inl/QxSingleton/QxSingleton.inl b/inl/QxSingleton/QxSingleton.inl new file mode 100644 index 0000000..67db5f6 --- /dev/null +++ b/inl/QxSingleton/QxSingleton.inl @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +namespace qx +{ + + template + T *QxSingleton::m_pSingleton = NULL; + template + QMutex QxSingleton::m_oMutexSingleton; + + template + T *QxSingleton::getSingleton() + { + if (m_pSingleton) + return m_pSingleton; + + IxSingleton::initQxSingletonX(); + QMutexLocker locker(QCoreApplication::instance() ? (&m_oMutexSingleton) : NULL); + if (!m_pSingleton) + { + m_pSingleton = new T(); + } + + return m_pSingleton; + } + + template + void QxSingleton::deleteSingleton() + { + QMutexLocker locker(QCoreApplication::instance() ? (&m_oMutexSingleton) : NULL); + if (!m_pSingleton) + { + return; + } + + delete m_pSingleton; + m_pSingleton = NULL; + } + +} // namespace qx diff --git a/inl/QxXml/QxXml.inl b/inl/QxXml/QxXml.inl new file mode 100644 index 0000000..1353d84 --- /dev/null +++ b/inl/QxXml/QxXml.inl @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +template +QString QxXml::toXml(T *pOwner) +{ + if (!pOwner) + { + return ""; + } + + QxXmlWriter xmlWriter; + QxDataMemberX::toXml(pOwner, &xmlWriter); + + return xmlWriter.getXml(); +} + +template +bool QxXml::fromXml(T *pOwner, const QString &sXml) +{ + if (!pOwner || sXml.isEmpty()) + { + return false; + } + + QxXmlReader xmlReader(sXml); + QxDataMemberX::fromXml(pOwner, &xmlReader); + + return true; +} + +template +std::shared_ptr QxXml::toXmlWriter(T *pOwner) +{ + if (!pOwner) + { + return std::shared_ptr(); + } + + std::shared_ptr pXmlWriter; + pXmlWriter.reset(new QxXmlWriter()); + QxDataMemberX::toXml(pOwner, pXmlWriter.get()); + + return pXmlWriter; +} + +template +bool QxXml::fromXmlReader(T *pOwner, QxXmlReader *pXmlReader) +{ + if (!pOwner || !pXmlReader) + { + return false; + } + QxDataMemberX::fromXml(pOwner, pXmlReader); + + return true; +} diff --git a/install-deps-debug.bat b/install-deps-debug.bat new file mode 100644 index 0000000..fb99ae4 --- /dev/null +++ b/install-deps-debug.bat @@ -0,0 +1,13 @@ +@echo off +echo ======================================== +echo Installing Debug dependencies +echo ======================================== +conan install . --build=missing -s build_type=Debug +if %errorlevel% neq 0 ( + echo. + echo [ERROR] Conan install failed! + exit /b %errorlevel% +) +echo. +echo [SUCCESS] Debug dependencies installed! +echo. diff --git a/install-deps-release.bat b/install-deps-release.bat new file mode 100644 index 0000000..30fc345 --- /dev/null +++ b/install-deps-release.bat @@ -0,0 +1,13 @@ +@echo off +echo ======================================== +echo Installing Release dependencies +echo ======================================== +conan install . --build=missing -s build_type=Release +if %errorlevel% neq 0 ( + echo. + echo [ERROR] Conan install failed! + exit /b %errorlevel% +) +echo. +echo [SUCCESS] Release dependencies installed! +echo. diff --git a/qt/moc/.gitignore b/qt/moc/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/qt/moc/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/qt/rcc/src/.gitignore b/qt/rcc/src/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/qt/rcc/src/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/qt/ui/include/.gitignore b/qt/ui/include/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/qt/ui/include/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/qt/ui/src/.gitignore b/qt/ui/src/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/qt/ui/src/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/src/QxCollection/QxCollection.cpp b/src/QxCollection/QxCollection.cpp new file mode 100644 index 0000000..ccb0205 --- /dev/null +++ b/src/QxCollection/QxCollection.cpp @@ -0,0 +1,93 @@ + +#include + +#include +#include +#include + +namespace qx +{ + + IxCollection::~IxCollection() = default; + + namespace unit_test + { + + // Compilation option '_QX_HASH_NO_STD_NAMESPACE' + // Try to avoid compilation error, something like : error: no matching function for call to 'qHash(const std::tuple<...>&)' + // This is due to C++ ADL to resolve specialized functions : qHash(T) should be implemented in the same namespace as T + // For 'std' classes, it should be NOT allowed : the behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std + // More details here : https://www.kdab.com/how-to-declare-a-qhash-overload/ + // And here : https://stackoverflow.com/questions/47460098/using-standard-library-types-as-keys-in-qhash-or-qset + void qx_collection_unit_test() + { + // Test basic functionality with simple types + { + QxCollection coll; + QxCollectionIterator itr(coll); + coll.sortByKey(); + coll.sortByValue(); + itr.toFirst(); + } + + // Test with std::tuple as key + { + using type_std_tuple = std::tuple; + QxCollection> coll; + coll.begin(); + coll.reserve(10); + coll.clear(); + coll.count(); + coll.empty(); + + type_std_tuple item = std::make_tuple(QString(), std::string(), 1); + coll.contains(item); + } + + // Test with std::pair as key + { + using type_std_pair = std::pair; + QxCollection> coll; + coll.begin(); + coll.reserve(10); + coll.clear(); + coll.count(); + coll.empty(); + + type_std_pair item = std::make_pair(QString(), std::string("1")); + coll.contains(item); + } + + // Test with QPair as key + { + using type_qt_pair = QPair; + QxCollection> coll; + coll.begin(); + coll.reserve(10); + coll.clear(); + coll.count(); + coll.empty(); + + type_qt_pair item = qMakePair(QDateTime(), std::string("1")); + coll.contains(item); + } + +#ifdef _QX_ENABLE_BOOST + // Test with boost::tuple as key + { + using type_boost_tuple = boost::tuple; + QxCollection> coll; + coll.begin(); + coll.reserve(10); + coll.clear(); + coll.count(); + coll.empty(); + + type_boost_tuple item = boost::make_tuple(QString(), std::string(), 1); + coll.contains(item); + } +#endif // _QX_ENABLE_BOOST + } + + } // namespace unit_test +} // namespace qx diff --git a/src/QxCommon/QxBool.cpp b/src/QxCommon/QxBool.cpp new file mode 100644 index 0000000..9978815 --- /dev/null +++ b/src/QxCommon/QxBool.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +#include + +QDataStream &operator<<(QDataStream &stream, const qx::QxBool &t) +{ + stream << t.m_bValue; + stream << t.m_lCode; + stream << t.m_sDesc; + return stream; +} + +QDataStream &operator>>(QDataStream &stream, qx::QxBool &t) +{ + stream >> t.m_bValue; + stream >> t.m_lCode; + stream >> t.m_sDesc; + return stream; +} diff --git a/src/QxCommon/QxCache.cpp b/src/QxCommon/QxCache.cpp new file mode 100644 index 0000000..1d29fc2 --- /dev/null +++ b/src/QxCommon/QxCache.cpp @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::cache::detail::QxCache) + +namespace qx +{ + namespace cache + { + namespace detail + { + + QxCache::QxCache() : qx::QxSingleton("qx::cache::detail::QxCache"), m_lMaxCost(999999999), m_lCurrCost(0) { ; } + + QxCache::~QxCache() { ; } + + long QxCache::getCurrCost() const { return m_lCurrCost; } + + long QxCache::getMaxCost() const { return m_lMaxCost; } + + long QxCache::count() const { return m_cache.count(); } + + long QxCache::size() const { return this->count(); } + + bool QxCache::isEmpty() const { return (this->count() == 0); } + + bool QxCache::exist(const QString &sKey) const { return m_cache.exist(sKey); } + + bool QxCache::contains(const QString &sKey) const { return this->exist(sKey); } + + void QxCache::setMaxCost(long l) + { + QMutexLocker locker(&m_oMutexCache); + m_lMaxCost = ((l < 0) ? 0 : l); + updateCost(); + } + + qx::any QxCache::at(const QString &sKey) + { + QMutexLocker locker(&m_oMutexCache); + if (!this->exist(sKey)) + { + return qx::any(); + } + return std::get<2>(m_cache.getByKey(sKey)); + } + + long QxCache::insertionCost(const QString &sKey) + { + QMutexLocker locker(&m_oMutexCache); + if (!this->exist(sKey)) + { + return -1; + } + return std::get<0>(m_cache.getByKey(sKey)); + } + + QDateTime QxCache::insertionDateTime(const QString &sKey) + { + QMutexLocker locker(&m_oMutexCache); + if (!this->exist(sKey)) + { + return QDateTime(); + } + return std::get<1>(m_cache.getByKey(sKey)); + } + + void QxCache::clear() + { + QMutexLocker locker(&m_oMutexCache); + m_cache.clear(); + m_lCurrCost = 0; + } + + bool QxCache::insert(const QString &sKey, const qx::any &anyObj, long lCost /* = 1 */, const QDateTime &dt /* = QDateTime() */) + { + if (sKey.isEmpty()) + { + qAssert(false); + return false; + } + this->remove(sKey); + + QMutexLocker locker(&m_oMutexCache); + lCost = ((lCost < 0) ? 0 : lCost); + QDateTime dtTemp(dt); + if (!dtTemp.isValid()) + { + dtTemp = QDateTime::currentDateTime(); + } + QxCache::type_qx_cache item = std::make_tuple(lCost, dtTemp, anyObj); + bool bInsertOk = m_cache.insert(sKey, item); + if (bInsertOk) + { + m_lCurrCost += lCost; + updateCost(); + } + + return bInsertOk; + } + + bool QxCache::remove(const QString &sKey) + { + QMutexLocker locker(&m_oMutexCache); + if (!this->exist(sKey)) + { + return false; + } + long lCost = std::get<0>(m_cache.getByKey(sKey)); + bool bRemoveOk = m_cache.removeByKey(sKey); + if (bRemoveOk) + { + m_lCurrCost -= lCost; + } + + return bRemoveOk; + } + + void QxCache::updateCost() + { + while ((m_lCurrCost > m_lMaxCost) && (m_cache.count() > 0)) + { + QString sKey = m_cache.getKeyByIndex(0); + long lCost = std::get<0>(m_cache.getByIndex(0)); + m_cache.removeByIndex(0); + m_lCurrCost -= lCost; + QString sMsg = QString("qx::cache : auto remove object in cache '") + sKey + QString("'"); + qDebug("[QxOrm] %s", qPrintable(sMsg)); + } + } + + } // namespace detail + } // namespace cache +} // namespace qx diff --git a/src/QxCommon/QxSimpleCrypt.cpp b/src/QxCommon/QxSimpleCrypt.cpp new file mode 100644 index 0000000..249f45e --- /dev/null +++ b/src/QxCommon/QxSimpleCrypt.cpp @@ -0,0 +1,254 @@ +#include + +#include +#include +#include + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) +#include +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + +#include + +#include + +namespace qx +{ + + QxSimpleCrypt::QxSimpleCrypt() : m_key(0), + m_compressionMode(CompressionAuto), + m_protectionMode(ProtectionChecksum), + m_lastError(ErrorNoError) + { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + qsrand(uint((qint64)(QDateTime::currentDateTime().toTime_t()) & 0xFFFF)); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + } + + QxSimpleCrypt::QxSimpleCrypt(quint64 key) : m_key(key), + m_compressionMode(CompressionAuto), + m_protectionMode(ProtectionChecksum), + m_lastError(ErrorNoError) + { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + qsrand(uint((qint64)(QDateTime::currentDateTime().toTime_t()) & 0xFFFF)); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + splitKey(); + } + + void QxSimpleCrypt::setKey(quint64 key) + { + m_key = key; + splitKey(); + } + + void QxSimpleCrypt::splitKey() + { + m_keyParts.clear(); + m_keyParts.resize(8); + for (int i = 0; i < 8; ++i) + { + quint64 part = m_key; + for (int j = i; j > 0; --j) + part = part >> 8; + part = part & 0xff; + m_keyParts[i] = static_cast(part); + } + } + + QByteArray QxSimpleCrypt::encryptToByteArray(const QString &plaintext) + { + QByteArray plaintextArray = plaintext.toUtf8(); + return encryptToByteArray(plaintextArray); + } + + QByteArray QxSimpleCrypt::encryptToByteArray(QByteArray plaintext) + { + if (m_keyParts.isEmpty()) + { + // qWarning() << "No key set."; + m_lastError = ErrorNoKeySet; + return QByteArray(); + } + + QByteArray ba = plaintext; + + CryptoFlags flags = CryptoFlagNone; + if (m_compressionMode == CompressionAlways) + { + ba = qCompress(ba, 9); // maximum compression + flags |= CryptoFlagCompression; + } + else if (m_compressionMode == CompressionAuto) + { + QByteArray compressed = qCompress(ba, 9); + if (compressed.count() < ba.count()) + { + ba = compressed; + flags |= CryptoFlagCompression; + } + } + + QByteArray integrityProtection; + if (m_protectionMode == ProtectionChecksum) + { + flags |= CryptoFlagChecksum; + QDataStream s(&integrityProtection, QIODevice::WriteOnly); + s << qChecksum(ba.constData(), ba.length()); + } + else if (m_protectionMode == ProtectionHash) + { + flags |= CryptoFlagHash; + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(ba); + integrityProtection += hash.result(); + } + + // prepend a random char to the string +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + QRandomGenerator randomGen = QRandomGenerator::securelySeeded(); + char randomChar = char(randomGen.generate() & 0xFF); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + char randomChar = char(qrand() & 0xFF); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + ba = randomChar + integrityProtection + ba; + + int pos(0); + char lastChar(0); + int cnt = ba.count(); + + while (pos < cnt) + { + ba[pos] = ba.at(pos) ^ m_keyParts.at(pos % 8) ^ lastChar; + lastChar = ba.at(pos); + ++pos; + } + + QByteArray resultArray; + resultArray.append(char(0x03)); // version for future updates to algorithm + resultArray.append(char(flags)); // encryption flags + resultArray.append(ba); + + m_lastError = ErrorNoError; + return resultArray; + } + + QString QxSimpleCrypt::encryptToString(const QString &plaintext) + { + QByteArray plaintextArray = plaintext.toUtf8(); + QByteArray cypher = encryptToByteArray(plaintextArray); + QString cypherString = QString::fromLatin1(cypher.toBase64()); + return cypherString; + } + + QString QxSimpleCrypt::encryptToString(QByteArray plaintext) + { + QByteArray cypher = encryptToByteArray(plaintext); + QString cypherString = QString::fromLatin1(cypher.toBase64()); + return cypherString; + } + + QString QxSimpleCrypt::decryptToString(const QString &cyphertext) + { + QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); + QByteArray plaintextArray = decryptToByteArray(cyphertextArray); + QString plaintext = QString::fromUtf8(plaintextArray, plaintextArray.size()); + return plaintext; + } + + QString QxSimpleCrypt::decryptToString(QByteArray cypher) + { + QByteArray ba = decryptToByteArray(cypher); + QString plaintext = QString::fromUtf8(ba, ba.size()); + return plaintext; + } + + QByteArray QxSimpleCrypt::decryptToByteArray(const QString &cyphertext) + { + QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); + QByteArray ba = decryptToByteArray(cyphertextArray); + return ba; + } + + QByteArray QxSimpleCrypt::decryptToByteArray(QByteArray cypher) + { + if (m_keyParts.isEmpty()) + { + qWarning() << "No key set."; + m_lastError = ErrorNoKeySet; + return QByteArray(); + } + + QByteArray ba = cypher; + char version = ba.at(0); + + if (version != 3) + { // we only work with version 3 + m_lastError = ErrorUnknownVersion; + qWarning() << "Invalid version or not a cyphertext."; + return QByteArray(); + } + + CryptoFlags flags = CryptoFlags(ba.at(1)); + ba = ba.mid(2); + int pos(0); + int cnt(ba.count()); + char lastChar = 0; + + while (pos < cnt) + { + char currentChar = ba[pos]; + ba[pos] = ba.at(pos) ^ lastChar ^ m_keyParts.at(pos % 8); + lastChar = currentChar; + ++pos; + } + + ba = ba.mid(1); // chop off the random number at the start + + bool integrityOk(true); + if (flags.testFlag(CryptoFlagChecksum)) + { + if (ba.length() < 2) + { + m_lastError = ErrorIntegrityFailed; + return QByteArray(); + } + quint16 storedChecksum; + { + QDataStream s(&ba, QIODevice::ReadOnly); + s >> storedChecksum; + } + ba = ba.mid(2); + quint16 checksum = qChecksum(ba.constData(), ba.length()); + integrityOk = (checksum == storedChecksum); + } + else if (flags.testFlag(CryptoFlagHash)) + { + if (ba.length() < 20) + { + m_lastError = ErrorIntegrityFailed; + return QByteArray(); + } + QByteArray storedHash = ba.left(20); + ba = ba.mid(20); + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(ba); + integrityOk = (hash.result() == storedHash); + } + + if (!integrityOk) + { + m_lastError = ErrorIntegrityFailed; + return QByteArray(); + } + + if (flags.testFlag(CryptoFlagCompression)) + ba = qUncompress(ba); + + m_lastError = ErrorNoError; + return ba; + } + +} // namespace qx diff --git a/src/QxConvert/QxConvert_Export.cpp b/src/QxConvert/QxConvert_Export.cpp new file mode 100644 index 0000000..d83c917 --- /dev/null +++ b/src/QxConvert/QxConvert_Export.cpp @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#if _QX_USE_QX_CONVERT_EXPORT + +#include + +#include + +#include + +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, qx::trait::no_type) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QString) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QDate) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QTime) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QDateTime) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QByteArray) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QVariant) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, qx_bool) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, bool) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, char) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, short) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, int) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, long) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, long long) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, float) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, double) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, unsigned short) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, unsigned int) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, unsigned long) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, unsigned long long) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, std::string) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, std::wstring) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QBrush) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QColor) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QFont) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QObject) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QPoint) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QRect) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QRegion) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QSize) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QStringList) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QUrl) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QImage) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QPicture) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QPixmap) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QUuid) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, qx::QxDateNeutral) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, qx::QxTimeNeutral) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, qx::QxDateTimeNeutral) + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QRegExp) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToString, QMatrix) +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, qx::trait::no_type) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QString) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QDate) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QTime) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QDateTime) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QByteArray) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QVariant) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, qx_bool) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, bool) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, char) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, short) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, int) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, long) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, long long) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, float) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, double) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, unsigned short) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, unsigned int) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, unsigned long) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, unsigned long long) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, std::string) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, std::wstring) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QBrush) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QColor) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QFont) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QObject) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QPoint) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QRect) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QRegion) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QSize) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QStringList) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QUrl) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QImage) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QPicture) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QPixmap) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QUuid) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, qx::QxDateNeutral) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, qx::QxTimeNeutral) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, qx::QxDateTimeNeutral) + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QRegExp) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromString, QMatrix) +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, qx::trait::no_type) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QString) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QDate) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QTime) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QDateTime) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QByteArray) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QVariant) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, qx_bool) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, bool) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, char) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, short) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, int) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, long) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, long long) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, float) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, double) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, unsigned short) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, unsigned int) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, unsigned long) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, unsigned long long) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, std::string) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, std::wstring) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QBrush) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QColor) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QFont) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QObject) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QPoint) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QRect) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QRegion) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QSize) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QStringList) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QUrl) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QImage) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QPicture) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QPixmap) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QUuid) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, qx::QxDateNeutral) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, qx::QxTimeNeutral) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, qx::QxDateTimeNeutral) + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QRegExp) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_ToVariant, QMatrix) +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, qx::trait::no_type) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QString) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QDate) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QTime) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QDateTime) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QByteArray) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QVariant) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, qx_bool) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, bool) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, char) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, short) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, int) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, long) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, long long) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, float) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, double) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, unsigned short) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, unsigned int) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, unsigned long) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, unsigned long long) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, std::string) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, std::wstring) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QBrush) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QColor) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QFont) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QObject) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QPoint) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QRect) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QRegion) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QSize) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QStringList) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QUrl) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QImage) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QPicture) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QPixmap) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QUuid) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, qx::QxDateNeutral) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, qx::QxTimeNeutral) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, qx::QxDateTimeNeutral) + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QRegExp) +QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, qx::cvt::detail::QxConvert_FromVariant, QMatrix) +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +#endif // _QX_USE_QX_CONVERT_EXPORT diff --git a/src/QxDao/IxDao_Helper.cpp b/src/QxDao/IxDao_Helper.cpp new file mode 100644 index 0000000..38ac1b1 --- /dev/null +++ b/src/QxDao/IxDao_Helper.cpp @@ -0,0 +1,907 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include +#include + +#include + +#include + +#define QX_DAO_ERR_INTERNAL "[QxOrm] 'qx::dao' internal error" +#define QX_DAO_ERR_UNKNOWN_ERROR "[QxOrm] 'qx::dao' unknown error" +#define QX_DAO_ERR_NO_CONNECTION "[QxOrm] error retrieving valid sql connection to database" +#define QX_DAO_ERR_OPEN_CONNECTION "[QxOrm] unable to open connection to database" +#define QX_DAO_ERR_BUILD_SQL_QUERY "[QxOrm] error building sql query : %s" +#define QX_DAO_ERR_EXECUTE_SQL_QUERY "[QxOrm] execute sql query failed : %s" +#define QX_DAO_ERR_PREPARE_SQL_QUERY "[QxOrm] prepare sql query failed : %s" +#define QX_DAO_ERR_NO_DATA "[QxOrm] sql query returns no data" +#define QX_DAO_ERR_NO_QUERY_BUILDER "[QxOrm] unable to construct sql query builder" +#define QX_DAO_ERR_NO_ELEMENT_IN_CONTAINER "[QxOrm] no element in container" +#define QX_DAO_ERR_INVALID_PRIMARY_KEY "[QxOrm] invalid primary key" +#define QX_DAO_ERR_INVALID_SQL_RELATION "[QxOrm] invalid sql relation" +#define QX_DAO_ERR_INVALID_VALUES_DETECTED "[QxOrm] validator engine : invalid values detected" +#define QX_DAO_ERR_READ_ONLY "[QxOrm] cannot execute INSERT, UPDATE or DELETE query with a read only entity" + +#define QX_CONSTRUCT_IX_DAO_HELPER() \ + m_timeTotal(0), m_timeExec(0), m_timeNext(0), m_timePrepare(0), m_timeBuildHierarchy(0), m_timeBuildCppInstance(0), m_timeReadCppInstance(0), \ + m_timeBuildSql(0), m_timeOpen(0), m_timeTransaction(0), m_nextCount(0), m_lDataCount(0), m_bTransaction(false), m_bQuiet(false), m_bTraceQuery(true), \ + m_bTraceRecord(false), m_bCartesianProduct(false), m_bValidatorThrowable(false), m_bNeedToClearDatabaseByThread(false), \ + m_bMongoDB(false), m_bDisplayTimerDetails(false), m_bUseExecBatch(false), m_pDataMemberX(NULL), m_pDataId(NULL), m_pSqlGenerator(NULL), m_pSession(NULL) + +#if (QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)) +#define QX_DAO_TIMER_ELAPSED(timer) timer.nsecsElapsed() +#else // (QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)) +#define QX_DAO_TIMER_ELAPSED(timer) (timer.elapsed() * 1000000) // convert milli-seconds to nano-seconds +#endif // (QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)) + +namespace qx +{ + namespace dao + { + namespace detail + { + + struct Q_DECL_HIDDEN IxDao_Helper::IxDao_HelperImpl + { + + QElapsedTimer m_timerTotal; //!< Timer to manage total elapsed time : build SQL query + all database operations + fetch C++ classes + QElapsedTimer m_timerExec; //!< Timer to manage database exec() elapsed time + QElapsedTimer m_timerNext; //!< Timer to manage database next() elapsed time + QElapsedTimer m_timerPrepare; //!< Timer to manage database prepare() elapsed time + QElapsedTimer m_timerBuildHierarchy; //!< Timer to manage build relationships hierarchy elapsed time + QElapsedTimer m_timerBuildCppInstance; //!< Timer to manage build C++ instance elapsed time + QElapsedTimer m_timerReadCppInstance; //!< Timer to manage read C++ instance elapsed time + QElapsedTimer m_timerBuildSql; //!< Timer to manage build SQL query elapsed time + QElapsedTimer m_timerOpen; //!< Timer to manage open database elapsed time + QElapsedTimer m_timerTransaction; //!< Timer to manage database transaction (commit/rollback) elapsed time + qint64 m_timeTotal; //!< Time (in nanoseconds) for total elapsed time : build SQL query + all database operations + fetch C++ classes + qint64 m_timeExec; //!< Time (in nanoseconds) to execute SQL query in database + qint64 m_timeNext; //!< Time (in nanoseconds) of all calls of QSqlQuery::next() method + qint64 m_timePrepare; //!< Time (in nanoseconds) of all calls of QSqlQuery::prepare() method + qint64 m_timeBuildHierarchy; //!< Time (in nanoseconds) to build relationships hierarchy + qint64 m_timeBuildCppInstance; //!< Time (in nanoseconds) to build C++ instance + qint64 m_timeReadCppInstance; //!< Time (in nanoseconds) to read C++ instance + qint64 m_timeBuildSql; //!< Time (in nanoseconds) to build SQL query + qint64 m_timeOpen; //!< Time (in nanoseconds) to open database + qint64 m_timeTransaction; //!< Time (in nanoseconds) for database transaction (commit/rollback) + int m_nextCount; //!< Number of calls of QSqlQuery::next() method + QSqlDatabase m_database; //!< Connection to database to execute query + QSqlQuery m_query; //!< Query to execute + QSqlError m_error; //!< Error executing query + QString m_context; //!< Description of context : fetch, insert, update, delete, etc... + long m_lDataCount; //!< Data member collection count + bool m_bTransaction; //!< Transaction in progress : commit if valid, rollback if error + bool m_bQuiet; //!< Display message and assert in debug mode + bool m_bTraceQuery; //!< Trace sql query + bool m_bTraceRecord; //!< Trace sql record + bool m_bCartesianProduct; //!< Recordset can return cartesian product => same id in multiple records + bool m_bValidatorThrowable; //!< An exception of type qx::validator_error is thrown when invalid values are detected inserting or updating an element into database + QStringList m_lstColumns; //!< List of columns to execute sql query (if empty => all columns) + bool m_bNeedToClearDatabaseByThread; //!< Internal purpose only to clear current database context by thread in destructor + bool m_bMongoDB; //!< Current database context is a MongoDB database + QStringList m_lstItemsAsJson; //!< List of items to insert/update/delete as JSON (used for MongoDB database) + bool m_bDisplayTimerDetails; //!< Display in logs all timers details (exec(), next(), prepare(), open(), etc...) + bool m_bUseExecBatch; //!< If true then use the QSqlQuery::execBatch() method to improve performance inserting/updating/deleting a list of instances to database (but doesn't fill the last inserted identifier in the C++ instances) + + qx::IxSqlQueryBuilder_ptr m_pQueryBuilder; //!< Sql query builder + qx::IxDataMemberX *m_pDataMemberX; //!< Collection of data member + qx::IxDataMember *m_pDataId; //!< Data member id + qx::QxSqlQuery m_qxQuery; //!< Query sql with place-holder + IxSqlGenerator *m_pSqlGenerator; //!< SQL generator to build SQL query specific for each database + qx::QxInvalidValueX m_lstInvalidValues; //!< List of invalid values using validator engine + qx::QxSqlRelationLinked_ptr m_pSqlRelationLinked; //!< List of relation linked to build a hierarchy of relationships + qx::QxSession *m_pSession; //!< Current active session + qx::QxCollection m_lstExecBatch; //!< List of data to send to database when QSqlQuery::execBatch() method is used + + IxDao_HelperImpl(qx::IxSqlQueryBuilder *pBuilder, const qx::QxSqlQuery *pQuery) : QX_CONSTRUCT_IX_DAO_HELPER() + { + m_pQueryBuilder.reset(pBuilder); + if (pQuery) + { + m_qxQuery = (*pQuery); + } + } + ~IxDao_HelperImpl() { ; } + + void displaySqlQuery(); + }; + + IxDao_Helper::IxDao_Helper(qx::IxSqlQueryBuilder *pBuilder, const qx::QxSqlQuery *pQuery /* = NULL */) : m_pImpl(new IxDao_HelperImpl(pBuilder, pQuery)) { ; } + + IxDao_Helper::~IxDao_Helper() + { + terminate(); + if (m_pImpl->m_bNeedToClearDatabaseByThread) + { + qx::QxSqlDatabase::getSingleton()->clearCurrentDatabaseByThread(); + } + } + + bool IxDao_Helper::isValid() const { return (!m_pImpl->m_error.isValid() && m_pImpl->m_pQueryBuilder); } + + bool IxDao_Helper::hasFeature(QSqlDriver::DriverFeature ft) const { return (m_pImpl->m_database.driver() ? m_pImpl->m_database.driver()->hasFeature(ft) : false); } + + QSqlDatabase &IxDao_Helper::database() { return m_pImpl->m_database; } + + const QSqlDatabase &IxDao_Helper::database() const { return m_pImpl->m_database; } + + QSqlQuery &IxDao_Helper::query() { return m_pImpl->m_query; } + + const QSqlQuery &IxDao_Helper::query() const { return m_pImpl->m_query; } + + QSqlError &IxDao_Helper::error() { return m_pImpl->m_error; } + + const QSqlError &IxDao_Helper::error() const { return m_pImpl->m_error; } + + qx::QxSqlQuery &IxDao_Helper::qxQuery() { return m_pImpl->m_qxQuery; } + + const qx::QxSqlQuery &IxDao_Helper::qxQuery() const { return m_pImpl->m_qxQuery; } + + qx::IxDataMemberX *IxDao_Helper::getDataMemberX() const { return m_pImpl->m_pDataMemberX; } + + long IxDao_Helper::getDataCount() const { return m_pImpl->m_lDataCount; } + + qx::IxDataMember *IxDao_Helper::getDataId() const { return m_pImpl->m_pDataId; } + + qx::IxDataMember *IxDao_Helper::nextData(long &l) const { return (m_pImpl->m_pQueryBuilder ? m_pImpl->m_pQueryBuilder->nextData(l) : NULL); } + + QString IxDao_Helper::sql() const { return (m_pImpl->m_pQueryBuilder ? m_pImpl->m_pQueryBuilder->getSqlQuery() : ""); } + + qx::QxSqlRelationLinked *IxDao_Helper::getSqlRelationLinked() const { return m_pImpl->m_pSqlRelationLinked.get(); } + + qx::QxSession *IxDao_Helper::getSession() const { return m_pImpl->m_pSession; } + + QString IxDao_Helper::getIgnoreSoftDeleteHash() const { return (m_pImpl->m_pSession ? m_pImpl->m_pSession->getIgnoreSoftDeleteHash() : QString()); } + + bool IxDao_Helper::getCartesianProduct() const { return m_pImpl->m_bCartesianProduct; } + + QStringList IxDao_Helper::getSqlColumns() const { return m_pImpl->m_lstColumns; } + + void IxDao_Helper::setSqlColumns(const QStringList &lst) { m_pImpl->m_lstColumns = lst; } + + bool IxDao_Helper::getUseExecBatch() const { return m_pImpl->m_bUseExecBatch; } + + void IxDao_Helper::setUseExecBatch(bool b) { m_pImpl->m_bUseExecBatch = b; } + + qx::QxCollection &IxDao_Helper::getListExecBatch() { return m_pImpl->m_lstExecBatch; } + + IxSqlGenerator *IxDao_Helper::getSqlGenerator() const { return m_pImpl->m_pSqlGenerator; } + + void IxDao_Helper::quiet() { m_pImpl->m_bQuiet = true; } + + bool IxDao_Helper::isReadOnly() const { return ((m_pImpl->m_pDataMemberX && m_pImpl->m_pDataMemberX->getClass()) ? m_pImpl->m_pDataMemberX->getClass()->isDaoReadOnly() : false); } + + bool IxDao_Helper::isMongoDB() const { return m_pImpl->m_bMongoDB; } + + bool IxDao_Helper::isDistinct() const { return m_pImpl->m_qxQuery.isDistinct(); } + + bool IxDao_Helper::getAddAutoIncrementIdToUpdateQuery() const { return qx::QxSqlDatabase::getSingleton()->getAddAutoIncrementIdToUpdateQuery(); } + + QStringList &IxDao_Helper::itemsAsJson() { return m_pImpl->m_lstItemsAsJson; } + + bool IxDao_Helper::exec(bool bForceEmptyExec /* = false */) + { + bool bExec = false; + IxDao_Timer timer(this, IxDao_Helper::timer_db_exec); + if (m_pImpl->m_bUseExecBatch) + { + bool bQuestionMark = (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark); + for (long l = 0; l < m_pImpl->m_lstExecBatch.size(); ++l) + { + if (bQuestionMark) + { + this->query().addBindValue(m_pImpl->m_lstExecBatch.getByIndex(l)); + } + else + { + this->query().bindValue(m_pImpl->m_lstExecBatch.getKeyByIndex(l), m_pImpl->m_lstExecBatch.getByIndex(l)); + } + } + bExec = this->query().execBatch(); + } + else if ((m_pImpl->m_qxQuery.isEmpty()) && (!bForceEmptyExec)) + { + bExec = this->query().exec(this->builder().getSqlQuery()); + } + else + { + bExec = this->query().exec(); + } + return bExec; + } + + bool IxDao_Helper::prepare(QString &sql) + { + QString sqlTemp = sql; + IxDao_Timer timer(this, IxDao_Helper::timer_db_prepare); + if (m_pImpl->m_pSqlGenerator) + { + m_pImpl->m_pSqlGenerator->onBeforeSqlPrepare(this, sql); + } + if (sqlTemp != sql) + { + qDebug("[QxOrm] SQL query has been changed by SQL generator (onBeforeSqlPrepare) :\n - before : '%s'\n - after : '%s'", qPrintable(sqlTemp), qPrintable(sql)); + } + sqlTemp = sql; + m_pImpl->m_qxQuery.onBeforeSqlPrepare(sql); + if (sqlTemp != sql) + { + qDebug("[QxOrm] SQL query has been changed by qx::QxSqlQuery (onBeforeSqlPrepare) :\n - before : '%s'\n - after : '%s'", qPrintable(sqlTemp), qPrintable(sql)); + } + return this->query().prepare(sql); + } + + void IxDao_Helper::addInvalidValues(const qx::QxInvalidValueX &lst) + { + m_pImpl->m_lstInvalidValues.insert(lst); + if (m_pImpl->m_lstInvalidValues.count() > 0) + { + QString sInvalidValues = QX_DAO_ERR_INVALID_VALUES_DETECTED; + sInvalidValues += QString("\n") + m_pImpl->m_lstInvalidValues.text(); + updateError(sInvalidValues); + if (m_pImpl->m_bValidatorThrowable) + { + QString tmp = m_pImpl->m_lstInvalidValues.text(); + qDebug("[QxOrm] invalid values detected, throw 'qx::validator_error' exception : '%s'", qPrintable(tmp)); + } + if (m_pImpl->m_bValidatorThrowable) + { + throw qx::validator_error(m_pImpl->m_lstInvalidValues); + } + } + } + + qx::IxSqlQueryBuilder &IxDao_Helper::builder() + { + qAssert(m_pImpl->m_pQueryBuilder); + return (*m_pImpl->m_pQueryBuilder); + } + + const qx::IxSqlQueryBuilder &IxDao_Helper::builder() const + { + qAssert(m_pImpl->m_pQueryBuilder); + return (*m_pImpl->m_pQueryBuilder); + } + + QSqlError IxDao_Helper::errFailed(bool bPrepare /* = false */) + { + QString sql = this->sql(); + bool bFormatSql = qx::QxSqlDatabase::getSingleton()->getFormatSqlQueryBeforeLogging(); + if (bFormatSql && m_pImpl->m_pSqlGenerator) + { + m_pImpl->m_pSqlGenerator->formatSqlQuery(this, sql); + } + if (bPrepare) + { + qDebug(QX_DAO_ERR_PREPARE_SQL_QUERY, qPrintable(sql)); + } + else + { + qDebug(QX_DAO_ERR_EXECUTE_SQL_QUERY, qPrintable(sql)); + } + return updateError(m_pImpl->m_query.lastError()); + } + + QSqlError IxDao_Helper::errEmpty() + { + QString sql = this->sql(); + qDebug(QX_DAO_ERR_BUILD_SQL_QUERY, qPrintable(sql)); + return updateError(m_pImpl->m_query.lastError()); + } + + QSqlError IxDao_Helper::errNoData() { return updateError(QX_DAO_ERR_NO_DATA); } + + QSqlError IxDao_Helper::errInvalidId() { return updateError(QX_DAO_ERR_INVALID_PRIMARY_KEY); } + + QSqlError IxDao_Helper::errInvalidRelation() { return updateError(QX_DAO_ERR_INVALID_SQL_RELATION); } + + QSqlError IxDao_Helper::errReadOnly() { return updateError(QX_DAO_ERR_READ_ONLY); } + + bool IxDao_Helper::transaction() + { + if (m_pImpl->m_bMongoDB) + { + return false; + } + if (isValid() && hasFeature(QSqlDriver::Transactions)) + { + IxDao_Timer timer(this, IxDao_Helper::timer_db_transaction); + m_pImpl->m_bTransaction = m_pImpl->m_database.transaction(); + } + return m_pImpl->m_bTransaction; + } + + bool IxDao_Helper::nextRecord() + { + IxDao_Timer timer(this, IxDao_Helper::timer_db_next); + bool bNext = m_pImpl->m_query.next(); + if (bNext && m_pImpl->m_bTraceRecord) + { + dumpRecord(); + } + return bNext; + } + + QVariant IxDao_Helper::getIdFromQuery(int iNameIndex /* = -1 */) const + { + QVariant vId; + bool isDistinct = this->isDistinct(); + qx::IxDataMember *pId = m_pImpl->m_pDataId; + qAssert(pId); + if (!pId) + { + return vId; + } + bool bForceRootId = ((isDistinct && pId && m_pImpl->m_pSqlRelationLinked) ? m_pImpl->m_pSqlRelationLinked->checkRootColumns(pId->getKey()) : false); + if (((!isDistinct) || (bForceRootId)) && (iNameIndex < 0)) + { + QString sId; + for (int i = 0; i < pId->getNameCount(); i++) + { + sId += m_pImpl->m_query.value(i).toString() + "|"; + } + vId = sId; + } + else if (((!isDistinct) || (bForceRootId)) && (iNameIndex >= 0)) + { + qAssert(iNameIndex < pId->getNameCount()); + vId = m_pImpl->m_query.value(iNameIndex); + } + else if (isDistinct) + { + QString sId; + QStringList columns = this->getSqlColumns(); + if ((columns.count() <= 0) || (columns.at(0) == "*")) + { + long l = 0; + qx::IxDataMember *p = NULL; + while ((p = this->builder().nextData(l))) + { + sId += m_pImpl->m_query.value(l - 1).toString() + "|"; + } + } + else + { + for (int i = 0; i < columns.count(); i++) + { + qx::IxDataMember *p = m_pImpl->m_pDataMemberX->get_WithDaoStrategy(columns.at(i)); + if (p && (p != pId)) + { + sId += m_pImpl->m_query.value(i).toString() + "|"; + } + } + } + vId = static_cast(qHash(sId)); + } + return vId; + } + + bool IxDao_Helper::updateSqlRelationX(const QStringList &relation) + { + qx_bool bHierarchyOk(true); + m_pImpl->m_bCartesianProduct = false; + IxDao_Timer timer(this, IxDao_Helper::timer_cpp_build_hierarchy); + m_pImpl->m_pSqlRelationLinked = qx::QxSqlRelationLinked::getHierarchy((m_pImpl->m_pDataMemberX ? m_pImpl->m_pDataMemberX->getClass() : NULL), relation, bHierarchyOk, this); + if (!bHierarchyOk) + { + m_pImpl->m_pSqlRelationLinked.reset(); + } + if (!bHierarchyOk) + { + QString txt = bHierarchyOk.getDesc(); + qDebug("[QxOrm] %s", qPrintable(txt)); + return false; + } + m_pImpl->m_bCartesianProduct = m_pImpl->m_pSqlRelationLinked->getCartesianProduct(); + if (m_pImpl->m_pQueryBuilder) + { + m_pImpl->m_pQueryBuilder->setCartesianProduct(m_pImpl->m_bCartesianProduct); + } + if (m_pImpl->m_pQueryBuilder) + { + m_pImpl->m_pQueryBuilder->setHashRelation(relation.join("|")); + } + if (m_pImpl->m_bCartesianProduct) + { + m_pImpl->m_pQueryBuilder->initIdX(m_pImpl->m_pSqlRelationLinked->getAllRelationCount()); + } + return bHierarchyOk.getValue(); + } + + void IxDao_Helper::dumpRecord() const + { + if (!m_pImpl->m_query.isValid()) + { + return; + } + QString sDump; + QVariant v; + QSqlRecord record = m_pImpl->m_query.record(); + int iCount = record.count(); + if (iCount <= 0) + { + return; + } + for (int i = 0; i < iCount; ++i) + { + v = record.value(i); + sDump += (v.isNull() ? QString("NULL") : v.toString()) + QString("|"); + } + sDump = sDump.left(sDump.length() - 1); // Remove last "|" + qDebug("[QxOrm] dump sql record : %s", qPrintable(sDump)); + } + + void IxDao_Helper::resolveQuery() + { + if (m_pImpl->m_qxQuery.isEmpty()) + { + return; + } + m_pImpl->m_qxQuery.resolve(m_pImpl->m_query, (m_pImpl->m_bUseExecBatch ? (&m_pImpl->m_lstExecBatch) : NULL)); + } + + void IxDao_Helper::timerStart(IxDao_Helper::timer_type timer) + { + switch (timer) + { + case IxDao_Helper::timer_total: + m_pImpl->m_timerTotal.start(); + break; + case IxDao_Helper::timer_db_exec: + m_pImpl->m_timerExec.start(); + break; + case IxDao_Helper::timer_db_next: + if (!m_pImpl->m_bDisplayTimerDetails) + { + break; + } + m_pImpl->m_timerNext.start(); + break; + case IxDao_Helper::timer_db_prepare: + if (!m_pImpl->m_bDisplayTimerDetails) + { + break; + } + m_pImpl->m_timerPrepare.start(); + break; + case IxDao_Helper::timer_cpp_build_hierarchy: + if (!m_pImpl->m_bDisplayTimerDetails) + { + break; + } + m_pImpl->m_timerBuildHierarchy.start(); + break; + case IxDao_Helper::timer_cpp_build_instance: + if (!m_pImpl->m_bDisplayTimerDetails) + { + break; + } + m_pImpl->m_timerBuildCppInstance.start(); + break; + case IxDao_Helper::timer_cpp_read_instance: + if (!m_pImpl->m_bDisplayTimerDetails) + { + break; + } + m_pImpl->m_timerReadCppInstance.start(); + break; + case IxDao_Helper::timer_build_sql: + if (!m_pImpl->m_bDisplayTimerDetails) + { + break; + } + m_pImpl->m_timerBuildSql.start(); + break; + case IxDao_Helper::timer_db_open: + if (!m_pImpl->m_bDisplayTimerDetails) + { + break; + } + m_pImpl->m_timerOpen.start(); + break; + case IxDao_Helper::timer_db_transaction: + if (!m_pImpl->m_bDisplayTimerDetails) + { + break; + } + m_pImpl->m_timerTransaction.start(); + break; + default: + break; + } + } + + qint64 IxDao_Helper::timerElapsed(IxDao_Helper::timer_type timer) + { + qint64 elapsed = 0; + switch (timer) + { + case IxDao_Helper::timer_total: + elapsed = QX_DAO_TIMER_ELAPSED(m_pImpl->m_timerTotal); + m_pImpl->m_timeTotal += elapsed; + break; + case IxDao_Helper::timer_db_exec: + elapsed = QX_DAO_TIMER_ELAPSED(m_pImpl->m_timerExec); + m_pImpl->m_timeExec += elapsed; + break; + case IxDao_Helper::timer_db_next: + if (!m_pImpl->m_bDisplayTimerDetails) + { + break; + } + elapsed = QX_DAO_TIMER_ELAPSED(m_pImpl->m_timerNext); + m_pImpl->m_timeNext += elapsed; + m_pImpl->m_nextCount++; + break; + case IxDao_Helper::timer_db_prepare: + if (!m_pImpl->m_bDisplayTimerDetails) + { + break; + } + elapsed = QX_DAO_TIMER_ELAPSED(m_pImpl->m_timerPrepare); + m_pImpl->m_timePrepare += elapsed; + break; + case IxDao_Helper::timer_cpp_build_hierarchy: + if (!m_pImpl->m_bDisplayTimerDetails) + { + break; + } + elapsed = QX_DAO_TIMER_ELAPSED(m_pImpl->m_timerBuildHierarchy); + m_pImpl->m_timeBuildHierarchy += elapsed; + break; + case IxDao_Helper::timer_cpp_build_instance: + if (!m_pImpl->m_bDisplayTimerDetails) + { + break; + } + elapsed = QX_DAO_TIMER_ELAPSED(m_pImpl->m_timerBuildCppInstance); + m_pImpl->m_timeBuildCppInstance += elapsed; + break; + case IxDao_Helper::timer_cpp_read_instance: + if (!m_pImpl->m_bDisplayTimerDetails) + { + break; + } + elapsed = QX_DAO_TIMER_ELAPSED(m_pImpl->m_timerReadCppInstance); + m_pImpl->m_timeReadCppInstance += elapsed; + break; + case IxDao_Helper::timer_build_sql: + if (!m_pImpl->m_bDisplayTimerDetails) + { + break; + } + elapsed = QX_DAO_TIMER_ELAPSED(m_pImpl->m_timerBuildSql); + m_pImpl->m_timeBuildSql += elapsed; + break; + case IxDao_Helper::timer_db_open: + if (!m_pImpl->m_bDisplayTimerDetails) + { + break; + } + elapsed = QX_DAO_TIMER_ELAPSED(m_pImpl->m_timerOpen); + m_pImpl->m_timeOpen += elapsed; + break; + case IxDao_Helper::timer_db_transaction: + if (!m_pImpl->m_bDisplayTimerDetails) + { + break; + } + elapsed = QX_DAO_TIMER_ELAPSED(m_pImpl->m_timerTransaction); + m_pImpl->m_timeTransaction += elapsed; + break; + default: + break; + } + return elapsed; + } + + void IxDao_Helper::addQuery(bool bResolve) + { + if (m_pImpl->m_qxQuery.isEmpty()) + { + return; + } + IxDao_Timer timer(this, IxDao_Helper::timer_build_sql); + QString sql = this->builder().getSqlQuery(); + QString sqlToAdd = m_pImpl->m_qxQuery.query().trimmed(); + bool bAddSqlCondition = false; + if (sqlToAdd.left(6).contains("WHERE ", Qt::CaseInsensitive)) + { + sqlToAdd = sqlToAdd.right(sqlToAdd.size() - 6); + bAddSqlCondition = true; + } + else if (sqlToAdd.left(4).contains("AND ", Qt::CaseInsensitive)) + { + sqlToAdd = sqlToAdd.right(sqlToAdd.size() - 4); + bAddSqlCondition = true; + } + this->builder().replaceSqlQueryAlias(sqlToAdd); + sql += (bAddSqlCondition ? qx::IxSqlQueryBuilder::addSqlCondition(sql) : QString(" ")) + sqlToAdd; + if (m_pImpl->m_qxQuery.isDistinct() && sql.left(7).contains("SELECT ", Qt::CaseInsensitive)) + { + sql = "SELECT DISTINCT " + sql.right(sql.size() - 7); + } + m_pImpl->m_qxQuery.postProcess(sql); + this->builder().setSqlQuery(sql); + + if (bResolve) + { + if (!this->prepare(sql)) + { + this->errFailed(true); + } + m_pImpl->m_qxQuery.resolve(this->query()); + } + } + + QSqlError IxDao_Helper::updateError(const QString &sError) + { + QString sDriverText = (QX_DAO_ERR_INTERNAL + QString(" <") + m_pImpl->m_context + QString(">")); + sDriverText += (sql().isEmpty() ? QString("") : (QString(" : ") + sql())); + QSqlError err = QSqlError(sDriverText, sError, QSqlError::UnknownError); + return updateError(err); + } + + QSqlError IxDao_Helper::updateError(const QSqlError &error) + { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + QString sDatabaseText = (((error.databaseText() != m_pImpl->m_error.databaseText()) && (!error.databaseText().isEmpty())) ? (m_pImpl->m_error.databaseText() + "\n" + error.databaseText()) : m_pImpl->m_error.databaseText()); + QString sDriverText = (((error.driverText() != m_pImpl->m_error.driverText()) && (!error.driverText().isEmpty())) ? (m_pImpl->m_error.driverText() + "\n" + error.driverText()) : m_pImpl->m_error.driverText()); + QString sNativeErrorCode = (((error.nativeErrorCode() != m_pImpl->m_error.nativeErrorCode()) && (!error.nativeErrorCode().isEmpty())) ? (m_pImpl->m_error.nativeErrorCode() + "\n" + error.nativeErrorCode()) : m_pImpl->m_error.nativeErrorCode()); + int iType = ((((m_pImpl->m_error.type() == QSqlError::NoError) || (m_pImpl->m_error.type() == QSqlError::UnknownError)) && (error.type() != QSqlError::NoError)) ? error.type() : m_pImpl->m_error.type()); + m_pImpl->m_error = QSqlError(sDriverText, sDatabaseText, static_cast(iType), sNativeErrorCode); + return m_pImpl->m_error; +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + if (!m_pImpl->m_error.isValid()) + { + m_pImpl->m_error = error; + return m_pImpl->m_error; + } + + if ((error.databaseText() != m_pImpl->m_error.databaseText()) && (!error.databaseText().isEmpty())) + { + m_pImpl->m_error.setDatabaseText(m_pImpl->m_error.databaseText() + "\n" + error.databaseText()); + } + if ((error.driverText() != m_pImpl->m_error.driverText()) && (!error.driverText().isEmpty())) + { + m_pImpl->m_error.setDriverText(m_pImpl->m_error.driverText() + "\n" + error.driverText()); + } + if (((m_pImpl->m_error.type() == QSqlError::NoError) || (m_pImpl->m_error.type() == QSqlError::UnknownError)) && (error.type() != QSqlError::NoError)) + { + m_pImpl->m_error.setType(error.type()); + } + if (m_pImpl->m_error.number() == -1) + { + m_pImpl->m_error.setNumber(error.number()); + } + + return m_pImpl->m_error; +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + } + + void IxDao_Helper::init(QSqlDatabase *pDatabase, const QString &sContext) + { + timerStart(IxDao_Helper::timer_total); + m_pImpl->m_context = sContext; + m_pImpl->m_bTraceQuery = qx::QxSqlDatabase::getSingleton()->getTraceSqlQuery(); + m_pImpl->m_bTraceRecord = qx::QxSqlDatabase::getSingleton()->getTraceSqlRecord(); + m_pImpl->m_bMongoDB = (qx::QxSqlDatabase::getSingleton()->getDriverName() == "QXMONGODB"); + m_pImpl->m_bDisplayTimerDetails = qx::QxSqlDatabase::getSingleton()->getDisplayTimerDetails(); + qAssert(!m_pImpl->m_context.isEmpty()); + +#ifndef _QX_ENABLE_MONGODB + if (m_pImpl->m_bMongoDB) + { + qAssertMsg(false, "[QxOrm] QXMONGODB driver", "_QX_ENABLE_MONGODB compilation option is required to connect to MongoDB database"); + updateError("[QxOrm] QXMONGODB driver : _QX_ENABLE_MONGODB compilation option is required to connect to MongoDB database"); + return; + } +#endif // _QX_ENABLE_MONGODB + + if (!m_pImpl->m_bMongoDB) + { + QSqlError dbError; + IxDao_Timer timer(this, IxDao_Helper::timer_db_open); + if (pDatabase) + { + m_pImpl->m_bNeedToClearDatabaseByThread = qx::QxSqlDatabase::getSingleton()->setCurrentDatabaseByThread(pDatabase); + } + m_pImpl->m_database = (pDatabase ? (*pDatabase) : qx::QxSqlDatabase::getDatabase(dbError)); + if (dbError.isValid()) + { + updateError(dbError); + return; + } + if (!m_pImpl->m_database.isValid()) + { + updateError(QX_DAO_ERR_NO_CONNECTION); + return; + } + if (!m_pImpl->m_database.isOpen() && !m_pImpl->m_database.open()) + { + updateError(QX_DAO_ERR_OPEN_CONNECTION); + return; + } + if (!m_pImpl->m_pQueryBuilder) + { + updateError(QX_DAO_ERR_NO_QUERY_BUILDER); + return; + } + m_pImpl->m_pSession = qx::QxSession::getActiveSession(&m_pImpl->m_database); + m_pImpl->m_query = QSqlQuery(m_pImpl->m_database); + m_pImpl->m_query.setForwardOnly(true); + } + + m_pImpl->m_pQueryBuilder->init(); + m_pImpl->m_pQueryBuilder->setDaoHelper(this); + m_pImpl->m_pDataMemberX = (m_pImpl->m_pQueryBuilder ? m_pImpl->m_pQueryBuilder->getDataMemberX() : NULL); + m_pImpl->m_lDataCount = (m_pImpl->m_pQueryBuilder ? m_pImpl->m_pQueryBuilder->getDataCount() : 0); + m_pImpl->m_pDataId = (m_pImpl->m_pQueryBuilder ? m_pImpl->m_pQueryBuilder->getDataId() : NULL); + m_pImpl->m_pSqlGenerator = qx::QxSqlDatabase::getSingleton()->getSqlGenerator(); + m_pImpl->m_bValidatorThrowable = qx::QxSqlDatabase::getSingleton()->getValidatorThrowable(); + } + + void IxDao_Helper::terminate() + { + if ((m_pImpl->m_lstInvalidValues.count() > 0) && m_pImpl->m_bValidatorThrowable) + { + if (m_pImpl->m_bTransaction) + { + IxDao_Timer timer(this, IxDao_Helper::timer_db_transaction); + m_pImpl->m_database.rollback(); + } + } + else if (!isValid()) + { + if (m_pImpl->m_bTransaction) + { + IxDao_Timer timer(this, IxDao_Helper::timer_db_transaction); + m_pImpl->m_database.rollback(); + } + if (!m_pImpl->m_bQuiet) + { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + QString serr = m_pImpl->m_error.nativeErrorCode(); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + int ierr = m_pImpl->m_error.number(); + QString serr = QString::number(ierr); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + QString tmp = m_pImpl->m_error.driverText(); + qDebug("Database error number '%s' : %s", qPrintable(serr), qPrintable(tmp)); + tmp = m_pImpl->m_error.databaseText(); + qDebug("%s", qPrintable(tmp)); + if (m_pImpl->m_bMongoDB) + { + tmp = m_pImpl->m_qxQuery.queryAt(0); + qDebug("%s", qPrintable(tmp)); + } + } + } + else if (m_pImpl->m_pQueryBuilder) + { + if (m_pImpl->m_bTransaction) + { + IxDao_Timer timer(this, IxDao_Helper::timer_db_transaction); + m_pImpl->m_database.commit(); + } + if (!m_pImpl->m_bQuiet && m_pImpl->m_bTraceQuery) + { + timerElapsed(IxDao_Helper::timer_total); + m_pImpl->displaySqlQuery(); + } + } + else + { + if (m_pImpl->m_bTransaction) + { + IxDao_Timer timer(this, IxDao_Helper::timer_db_transaction); + m_pImpl->m_database.rollback(); + } + if (!m_pImpl->m_bQuiet) + { + qDebug("%s", QX_DAO_ERR_UNKNOWN_ERROR); + qAssert(false); + } + } + + m_pImpl->m_bTransaction = false; + dumpBoundValues(); + } + + void IxDao_Helper::dumpBoundValues() const + { + if (m_pImpl->m_bMongoDB) + { + return; + } + qx::QxSqlDatabase *pDatabase = qx::QxSqlDatabase::getSingleton(); + if (!pDatabase) + { + return; + } + bool bBoundValues = pDatabase->getTraceSqlBoundValues(); + bool bBoundValuesOnError = pDatabase->getTraceSqlBoundValuesOnError(); + + if ((!isValid() && bBoundValuesOnError) || (bBoundValues)) + { + qx::QxSqlQuery::dumpBoundValues(m_pImpl->m_query); + } + } + + void IxDao_Helper::IxDao_HelperImpl::displaySqlQuery() + { + QString query = (m_bMongoDB ? m_qxQuery.queryAt(0) : QString()); + QString sql = ((query.isEmpty() && m_pQueryBuilder) ? m_pQueryBuilder->getSqlQuery() : query); + bool bFormatSql = qx::QxSqlDatabase::getSingleton()->getFormatSqlQueryBeforeLogging(); + qx::dao::detail::IxSqlGenerator *pSqlGenerator = qx::QxSqlDatabase::getSingleton()->getSqlGenerator(); + qint64 iTraceSqlOnlySlowQueriesDatabase = static_cast(qx::QxSqlDatabase::getSingleton()->getTraceSqlOnlySlowQueriesDatabase()); + qint64 iTraceSqlOnlySlowQueriesTotal = static_cast(qx::QxSqlDatabase::getSingleton()->getTraceSqlOnlySlowQueriesTotal()); + if ((iTraceSqlOnlySlowQueriesDatabase > 0) && (iTraceSqlOnlySlowQueriesTotal < 0)) + { + iTraceSqlOnlySlowQueriesTotal = 999999999; + } + else if ((iTraceSqlOnlySlowQueriesTotal > 0) && (iTraceSqlOnlySlowQueriesDatabase < 0)) + { + iTraceSqlOnlySlowQueriesDatabase = 999999999; + } + if (bFormatSql && pSqlGenerator) + { + pSqlGenerator->formatSqlQuery(NULL, sql); + } + + if ((m_timeTotal >= (iTraceSqlOnlySlowQueriesTotal * 1000000)) || (m_timeExec >= (iTraceSqlOnlySlowQueriesDatabase * 1000000))) // convert milli-seconds to nano-seconds + { + QString log = "sql query (total: " + ((m_timeTotal == 0) ? QString("0") : QString::number((static_cast(m_timeTotal) / 1000000.0), 'g', 3)) + " ms"; + log += ", db_exec: " + ((m_timeExec == 0) ? QString("0") : QString::number((static_cast(m_timeExec) / 1000000.0), 'g', 3)) + " ms"; + if (m_bDisplayTimerDetails) + { + log += ", db_prepare: " + ((m_timePrepare == 0) ? QString("0") : QString::number((static_cast(m_timePrepare) / 1000000.0), 'g', 3)) + " ms"; + log += ", db_next(" + QString::number(m_nextCount) + "): " + ((m_timeNext == 0) ? QString("0") : QString::number((static_cast(m_timeNext) / 1000000.0), 'g', 3)) + " ms"; + log += ", db_open: " + ((m_timeOpen == 0) ? QString("0") : QString::number((static_cast(m_timeOpen) / 1000000.0), 'g', 3)) + " ms"; + log += ", db_transaction: " + ((m_timeTransaction == 0) ? QString("0") : QString::number((static_cast(m_timeTransaction) / 1000000.0), 'g', 3)) + " ms"; + log += ", build_relations: " + ((m_timeBuildHierarchy == 0) ? QString("0") : QString::number((static_cast(m_timeBuildHierarchy) / 1000000.0), 'g', 3)) + " ms"; + log += ", build_sql: " + ((m_timeBuildSql == 0) ? QString("0") : QString::number((static_cast(m_timeBuildSql) / 1000000.0), 'g', 3)) + " ms"; + log += ", build_cpp: " + ((m_timeBuildCppInstance == 0) ? QString("0") : QString::number((static_cast(m_timeBuildCppInstance) / 1000000.0), 'g', 3)) + " ms"; + log += ", read_cpp: " + ((m_timeReadCppInstance == 0) ? QString("0") : QString::number((static_cast(m_timeReadCppInstance) / 1000000.0), 'g', 3)) + " ms"; + } + log += ") : " + sql; + qDebug("[QxOrm] %s", qPrintable(log)); + } + } + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/src/QxDao/IxPersistable.cpp b/src/QxDao/IxPersistable.cpp new file mode 100644 index 0000000..09390c3 --- /dev/null +++ b/src/QxDao/IxPersistable.cpp @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include +#include + +#include + +#include + +#include + +namespace qx +{ + + IxPersistable::IxPersistable() { ; } + + IxPersistable::~IxPersistable() { ; } + + std::shared_ptr IxPersistable::qxFetchAll(const QString &className, const QStringList &columns /* = QStringList() */, const QStringList &relation /* = QStringList() */, QSqlDatabase *pDatabase /* = NULL */, bool bAsList /* = false */) + { + if (!QxClassX::implementIxPersistable(className)) + { + qAssert(false); + return std::shared_ptr(); + } + qx::IxPersistable_ptr ptr = qx::IxPersistable_ptr(static_cast(qx::create_void_ptr(className))); + if (!ptr) + { + throw qx::dao::sql_error(QSqlError("[QxOrm] qx::IxPersistable::qxFetchAll() : 'invalid classname, unable to create a new instance'", "", QSqlError::UnknownError)); + } + std::shared_ptr lst = ptr->qxNewPersistableCollection(bAsList); + if (!lst) + { + throw qx::dao::sql_error(QSqlError("[QxOrm] qx::IxPersistable::qxFetchAll() : 'unable to create a new persistable collection'", "", QSqlError::UnknownError)); + } + QSqlError daoError = lst->qxFetchAll(NULL, columns, relation, pDatabase); + if (daoError.isValid()) + { + throw qx::dao::sql_error(daoError); + } + return lst; + } + + std::shared_ptr IxPersistable::qxFetchByQuery(const QString &className, const qx::QxSqlQuery &query, const QStringList &columns /* = QStringList() */, const QStringList &relation /* = QStringList() */, QSqlDatabase *pDatabase /* = NULL */, bool bAsList /* = false */) + { + if (!QxClassX::implementIxPersistable(className)) + { + qAssert(false); + return std::shared_ptr(); + } + qx::IxPersistable_ptr ptr = qx::IxPersistable_ptr(static_cast(qx::create_void_ptr(className))); + if (!ptr) + { + throw qx::dao::sql_error(QSqlError("[QxOrm] qx::IxPersistable::qxFetchByQuery() : 'invalid classname, unable to create a new instance'", "", QSqlError::UnknownError)); + } + std::shared_ptr lst = ptr->qxNewPersistableCollection(bAsList); + if (!lst) + { + throw qx::dao::sql_error(QSqlError("[QxOrm] qx::IxPersistable::qxFetchByQuery() : 'unable to create a new persistable collection'", "", QSqlError::UnknownError)); + } + QSqlError daoError = lst->qxFetchByQuery(query, NULL, columns, relation, pDatabase); + if (daoError.isValid()) + { + throw qx::dao::sql_error(daoError); + } + return lst; + } + + std::shared_ptr IxPersistable::qxExecuteQuery(const QString &className, qx::QxSqlQuery &query, QSqlDatabase *pDatabase /* = NULL */, bool bAsList /* = false */) + { + if (!QxClassX::implementIxPersistable(className)) + { + qAssert(false); + return std::shared_ptr(); + } + qx::IxPersistable_ptr ptr = qx::IxPersistable_ptr(static_cast(qx::create_void_ptr(className))); + if (!ptr) + { + throw qx::dao::sql_error(QSqlError("[QxOrm] qx::IxPersistable::qxExecuteQuery() : 'invalid classname, unable to create a new instance'", "", QSqlError::UnknownError)); + } + std::shared_ptr lst = ptr->qxNewPersistableCollection(bAsList); + if (!lst) + { + throw qx::dao::sql_error(QSqlError("[QxOrm] qx::IxPersistable::qxExecuteQuery() : 'unable to create a new persistable collection'", "", QSqlError::UnknownError)); + } + QSqlError daoError = lst->qxExecuteQuery(query, NULL, pDatabase); + if (daoError.isValid()) + { + throw qx::dao::sql_error(daoError); + } + return lst; + } + +} // namespace qx diff --git a/src/QxDao/IxPersistableCollection.cpp b/src/QxDao/IxPersistableCollection.cpp new file mode 100644 index 0000000..fe7a901 --- /dev/null +++ b/src/QxDao/IxPersistableCollection.cpp @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +namespace qx +{ + + IxPersistableCollection::IxPersistableCollection() : qx::IxPersistable() { ; } + + IxPersistableCollection::~IxPersistableCollection() { ; } + +} // namespace qx diff --git a/src/QxDao/IxPersistableList.cpp b/src/QxDao/IxPersistableList.cpp new file mode 100644 index 0000000..d4c83b0 --- /dev/null +++ b/src/QxDao/IxPersistableList.cpp @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +namespace qx +{ + +} // namespace qx diff --git a/src/QxDao/IxSqlQueryBuilder.cpp b/src/QxDao/IxSqlQueryBuilder.cpp new file mode 100644 index 0000000..a75c8cf --- /dev/null +++ b/src/QxDao/IxSqlQueryBuilder.cpp @@ -0,0 +1,953 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace qx +{ + + struct Q_DECL_HIDDEN IxSqlQueryBuilder::IxSqlQueryBuilderImpl + { + + typedef QPair type_id; + typedef QHash type_ptr_by_id; + typedef std::shared_ptr type_ptr_by_id_ptr; + typedef QList type_lst_ptr_by_id; + typedef std::shared_ptr type_lst_ptr_by_id_ptr; + typedef QxCollection type_lst_data_member; + typedef std::shared_ptr type_lst_data_member_ptr; + + type_lst_data_member_ptr m_lstDataMemberPtr; //!< Collection of 'IxDataMember' to build sql query + std::shared_ptr m_lstSqlRelationPtr; //!< Collection of 'IxSqlRelation' to build sql query + IxDataMember *m_pDataMemberId; //!< Data member id for sql query + QString m_sSqlQuery; //!< Current sql query + QString m_sTableName; //!< Sql table name of current object + QString m_sHashRelation; //!< Optimization : hash to retrieve sql query with relation + bool m_bCartesianProduct; //!< Recordset can return cartesian product => same id in multiple records + type_lst_ptr_by_id_ptr m_pIdX; //!< Collection of id (and pointer associated) to avoid multiple fetch on same id (cartesian product) + QxSoftDelete m_oSoftDelete; //!< Soft delete (or logical delete) behavior + QxSoftDelete m_oSoftDeleteEmpty; //!< Keep an empty soft delete instance (used with qx::QxSession::ignoreSoftDelete()) + QHash m_lstSqlQueryAlias; //!< List of sql alias to replace into sql query + qx::dao::detail::IxDao_Helper *m_pDaoHelper; //!< Pointer to the dao helper class associated to the builder + IxDataMemberX *m_pDataMemberX; //!< QxDataMemberX singleton reference + bool m_bInitDone; //!< Class initialisation finished + QString m_sCurrentBuildingSql; //!< Current building SQL query (not finished yet) + + static QHash m_lstSqlQuery; //!< Store here all SQL queries generated by child classes + static QHash> m_lstSqlAlias; //!< Store here all SQL aliases generated by child classes + static QMutex m_mutex; //!< Mutex => qx::IxSqlQueryBuilder is thread-safe + + IxSqlQueryBuilderImpl() : m_pDataMemberId(NULL), m_bCartesianProduct(false), m_pDaoHelper(NULL), m_pDataMemberX(NULL), m_bInitDone(false) { ; } + ~IxSqlQueryBuilderImpl() { ; } + + bool checkIgnoreSoftDelete() const + { + if ((!m_pDaoHelper) || (!m_pDataMemberX)) + { + return false; + } + qx::QxSession *pSession = m_pDaoHelper->getSession(); + if (!pSession) + { + return false; + } + qx::IxClass *pClass = m_pDataMemberX->getClass(); + if (!pClass) + { + return false; + } + return pSession->checkIgnoreSoftDelete(pClass->getKey()); + } + }; + + QHash IxSqlQueryBuilder::IxSqlQueryBuilderImpl::m_lstSqlQuery; + QHash> IxSqlQueryBuilder::IxSqlQueryBuilderImpl::m_lstSqlAlias; + QMutex IxSqlQueryBuilder::IxSqlQueryBuilderImpl::m_mutex; + + IxSqlQueryBuilder::IxSqlQueryBuilder() : m_pImpl(new IxSqlQueryBuilderImpl()) { ; } + + IxSqlQueryBuilder::~IxSqlQueryBuilder() { ; } + + IxDataMemberX *IxSqlQueryBuilder::getDataMemberX() const { return m_pImpl->m_pDataMemberX; } + + QxCollection *IxSqlQueryBuilder::getLstDataMember() const { return m_pImpl->m_lstDataMemberPtr.get(); } + + IxSqlRelationX *IxSqlQueryBuilder::getLstRelation() const { return m_pImpl->m_lstSqlRelationPtr.get(); } + + qx::dao::detail::IxDao_Helper *IxSqlQueryBuilder::getDaoHelper() const { return m_pImpl->m_pDaoHelper; } + + void IxSqlQueryBuilder::setDaoHelper(qx::dao::detail::IxDao_Helper *p) { m_pImpl->m_pDaoHelper = p; } + + void IxSqlQueryBuilder::setHashRelation(const QString &s) { m_pImpl->m_sHashRelation = s; } + + void IxSqlQueryBuilder::setCartesianProduct(bool b) { m_pImpl->m_bCartesianProduct = b; } + + QString IxSqlQueryBuilder::getSqlQuery() const { return m_pImpl->m_sSqlQuery; } + + QString IxSqlQueryBuilder::getHashRelation() const { return m_pImpl->m_sHashRelation; } + + QString IxSqlQueryBuilder::table() const { return m_pImpl->m_sTableName; } + + QxSoftDelete IxSqlQueryBuilder::getSoftDelete() const { return (m_pImpl->checkIgnoreSoftDelete() ? m_pImpl->m_oSoftDeleteEmpty : m_pImpl->m_oSoftDelete); } + + bool IxSqlQueryBuilder::getCartesianProduct() const { return m_pImpl->m_bCartesianProduct; } + + long IxSqlQueryBuilder::getDataCount() const { return (m_pImpl->m_lstDataMemberPtr ? m_pImpl->m_lstDataMemberPtr->count() : 0); } + + long IxSqlQueryBuilder::getRelationCount() const { return (m_pImpl->m_lstSqlRelationPtr ? m_pImpl->m_lstSqlRelationPtr->count() : 0); } + + IxDataMember *IxSqlQueryBuilder::getDataId() const { return m_pImpl->m_pDataMemberId; } + + IxDataMember *IxSqlQueryBuilder::nextData(long &l) const + { + if ((!m_pImpl->m_lstDataMemberPtr) || (l < 0) || (l >= m_pImpl->m_lstDataMemberPtr->count())) + { + return NULL; + }; + ++l; + return m_pImpl->m_lstDataMemberPtr->getByIndex(l - 1); + } + + IxSqlRelation *IxSqlQueryBuilder::nextRelation(long &l) const + { + if ((!m_pImpl->m_lstSqlRelationPtr) || (l < 0) || (l >= m_pImpl->m_lstSqlRelationPtr->count())) + { + return NULL; + }; + ++l; + return m_pImpl->m_lstSqlRelationPtr->getByIndex(l - 1); + } + + QString &IxSqlQueryBuilder::getCurrentBuildingSql() const { return m_pImpl->m_sCurrentBuildingSql; } + + bool IxSqlQueryBuilder::isInitDone() const { return m_pImpl->m_bInitDone; } + + QxSoftDelete &IxSqlQueryBuilder::softDelete() { return (m_pImpl->checkIgnoreSoftDelete() ? m_pImpl->m_oSoftDeleteEmpty : m_pImpl->m_oSoftDelete); } + + const QxSoftDelete &IxSqlQueryBuilder::softDelete() const { return (m_pImpl->checkIgnoreSoftDelete() ? m_pImpl->m_oSoftDeleteEmpty : m_pImpl->m_oSoftDelete); } + + void IxSqlQueryBuilder::setSoftDelete(const QxSoftDelete &o) { m_pImpl->m_oSoftDelete = o; } + + void IxSqlQueryBuilder::setDataMemberX(IxDataMemberX *p) { m_pImpl->m_pDataMemberX = p; } + + void IxSqlQueryBuilder::clone(const IxSqlQueryBuilder &other) + { + this->m_pImpl->m_lstDataMemberPtr = other.m_pImpl->m_lstDataMemberPtr; + this->m_pImpl->m_lstSqlRelationPtr = other.m_pImpl->m_lstSqlRelationPtr; + this->m_pImpl->m_pDataMemberId = other.m_pImpl->m_pDataMemberId; + this->m_pImpl->m_sSqlQuery = other.m_pImpl->m_sSqlQuery; + this->m_pImpl->m_sTableName = other.m_pImpl->m_sTableName; + this->m_pImpl->m_sHashRelation = other.m_pImpl->m_sHashRelation; + this->m_pImpl->m_bCartesianProduct = other.m_pImpl->m_bCartesianProduct; + this->m_pImpl->m_pIdX = other.m_pImpl->m_pIdX; + this->m_pImpl->m_oSoftDelete = other.m_pImpl->m_oSoftDelete; + this->m_pImpl->m_lstSqlQueryAlias = other.m_pImpl->m_lstSqlQueryAlias; + this->m_pImpl->m_pDaoHelper = other.m_pImpl->m_pDaoHelper; + this->m_pImpl->m_pDataMemberX = other.m_pImpl->m_pDataMemberX; + } + + void IxSqlQueryBuilder::init() + { + if (!m_pImpl->m_pDataMemberX || !m_pImpl->m_pDataMemberX->getClass() || m_pImpl->m_bInitDone) + { + return; + } + m_pImpl->m_pDataMemberId = m_pImpl->m_pDataMemberX->getId_WithDaoStrategy(); + m_pImpl->m_sTableName = m_pImpl->m_pDataMemberX->getName(); + m_pImpl->m_lstDataMemberPtr = m_pImpl->m_pDataMemberX->getClass()->getSqlDataMemberX(); + m_pImpl->m_lstSqlRelationPtr = m_pImpl->m_pDataMemberX->getClass()->getSqlRelationX(); + m_pImpl->m_bInitDone = true; + } + + void IxSqlQueryBuilder::setSqlQuery(const QString &sql, const QString &key /* = QString() */) + { + m_pImpl->m_sSqlQuery = sql; + if (!key.isEmpty()) + { + QMutexLocker locker(&IxSqlQueryBuilderImpl::m_mutex); + IxSqlQueryBuilderImpl::m_lstSqlQuery.insert(key, sql); + } + } + + bool IxSqlQueryBuilder::findSqlQuery(const QString &key) + { + if (key.isEmpty()) + { + return false; + } + QMutexLocker locker(&IxSqlQueryBuilderImpl::m_mutex); + QString sql = IxSqlQueryBuilderImpl::m_lstSqlQuery.value(key); + if (!sql.isEmpty()) + { + m_pImpl->m_sSqlQuery = sql; + } + return (!sql.isEmpty()); + } + + bool IxSqlQueryBuilder::findSqlAlias(const QString &key) + { + if (key.isEmpty()) + { + return false; + } + QMutexLocker locker(&IxSqlQueryBuilderImpl::m_mutex); + if (!IxSqlQueryBuilderImpl::m_lstSqlAlias.contains(key)) + { + return false; + } + m_pImpl->m_lstSqlQueryAlias = IxSqlQueryBuilderImpl::m_lstSqlAlias.value(key); + return true; + } + + void IxSqlQueryBuilder::insertSqlAlias(const QString &key) + { + if (key.isEmpty()) + { + return; + } + QMutexLocker locker(&IxSqlQueryBuilderImpl::m_mutex); + IxSqlQueryBuilderImpl::m_lstSqlAlias.insert(key, m_pImpl->m_lstSqlQueryAlias); + } + + void IxSqlQueryBuilder::initIdX(long lAllRelationCount) + { + if (!m_pImpl->m_bCartesianProduct) + { + qAssert(false); + return; + } + m_pImpl->m_pIdX = std::make_shared(); + for (long l = 0; l < (lAllRelationCount + 1); ++l) + { + IxSqlQueryBuilderImpl::type_ptr_by_id_ptr pItem = IxSqlQueryBuilderImpl::type_ptr_by_id_ptr(new IxSqlQueryBuilderImpl::type_ptr_by_id()); + m_pImpl->m_pIdX->append(pItem); + } + } + + bool IxSqlQueryBuilder::insertIdX(long lIndex, const QVariant &idOwner, const QVariant &idData, void *ptr) + { + QString sIdOwner = idOwner.toString(); + QString sIdData = idData.toString(); + if (!m_pImpl->m_pIdX || sIdOwner.isEmpty() || sIdData.isEmpty()) + { + qAssert(false); + return false; + } + if ((lIndex < 0) || (lIndex >= m_pImpl->m_pIdX->count())) + { + qAssert(false); + return false; + } + + IxSqlQueryBuilderImpl::type_id idX(sIdOwner, sIdData); + IxSqlQueryBuilderImpl::type_ptr_by_id_ptr pHash = m_pImpl->m_pIdX->at(lIndex); + if (!ptr || !pHash || pHash->contains(idX)) + { + qAssert(false); + return false; + } + pHash->insert(idX, ptr); + + return true; + } + + void *IxSqlQueryBuilder::existIdX(long lIndex, const QVariant &idOwner, const QVariant &idData) + { + QString sIdOwner = idOwner.toString(); + QString sIdData = idData.toString(); + if (!m_pImpl->m_pIdX || sIdOwner.isEmpty() || sIdData.isEmpty()) + { + qAssert(false); + return NULL; + } + if ((lIndex < 0) || (lIndex >= m_pImpl->m_pIdX->count())) + { + qAssert(false); + return NULL; + } + + IxSqlQueryBuilderImpl::type_id idX(sIdOwner, sIdData); + IxSqlQueryBuilderImpl::type_ptr_by_id_ptr pHash = m_pImpl->m_pIdX->at(lIndex); + if (!pHash || !pHash->contains(idX)) + { + return NULL; + } + + return pHash->value(idX); + } + + bool IxSqlQueryBuilder::getAddAutoIncrementIdToUpdateQuery() const + { + return qx::QxSqlDatabase::getSingleton()->getAddAutoIncrementIdToUpdateQuery(); + } + + void IxSqlQueryBuilder::addSqlQueryAlias(const QString &sql, const QString &sqlAlias) + { + m_pImpl->m_lstSqlQueryAlias.insert(sql, sqlAlias); + } + + void IxSqlQueryBuilder::replaceSqlQueryAlias(QString &sql) const + { + if (!qx::QxSqlDatabase::getSingleton()->getAutoReplaceSqlAliasIntoQuery()) + { + return; + } + if (m_pImpl->m_lstSqlQueryAlias.count() <= 0) + { + return; + } + QHashIterator itr(m_pImpl->m_lstSqlQueryAlias); + sql = (" " + sql); + while (itr.hasNext()) + { + itr.next(); + QString sBefore = (" " + itr.key() + "."); + QString sAfter = (" " + itr.value() + "."); + sql.replace(sBefore, sAfter); + } + sql = sql.trimmed(); + } + + bool IxSqlQueryBuilder::verifyColumns(const QStringList &columns) const + { +#ifdef _QX_MODE_DEBUG + if (!m_pImpl->m_pDataMemberX) + { + qAssert(false); + return false; + } + for (int i = 0; i < columns.count(); i++) + { + if (!m_pImpl->m_pDataMemberX->exist_WithDaoStrategy(columns.at(i))) + { + QString sErrorMsg = QString("column '%1' not found in table '%2'").arg(columns.at(i), m_pImpl->m_sTableName); + qDebug("[QxOrm] Error in qx::IxSqlQueryBuilder::verifyColumns() : %s", qPrintable(sErrorMsg)); + qAssertMsg(false, "[QxOrm] qx::IxSqlQueryBuilder::verifyColumns()", qPrintable(sErrorMsg)); + return false; + } + } + return true; +#else // _QX_MODE_DEBUG + Q_UNUSED(columns); + return true; +#endif // _QX_MODE_DEBUG + } + + void IxSqlQueryBuilder::sql_CreateTable(QString &sql, IxSqlQueryBuilder &builder) + { + long l1(0), l2(0); + qx::IxDataMember *p = NULL; + qx::IxDataMember *pId = builder.getDataId(); + qx::IxSqlRelation *pRelation = NULL; + qx::QxSqlRelationParams params(0, 0, (&sql), (&builder), NULL, NULL); + qx::QxSoftDelete oSoftDelete = builder.getSoftDelete(); + QString table = builder.table(); + sql = "CREATE TABLE " + qx::IxDataMember::getSqlTableName(table) + " ("; + int iSqlCountRef = sql.length(); + if (pId) + { + sql += pId->getSqlNameAndTypeAndParams(", ") + ", "; + qAssert(!pId->getSqlType().isEmpty()); + } + while ((p = builder.nextData(l1))) + { + sql += p->getSqlNameAndTypeAndParams(", ") + ", "; + qAssert(!p->getSqlType().isEmpty()); + } + if (!oSoftDelete.isEmpty()) + { + sql += oSoftDelete.buildSqlQueryToCreateTable() + ", "; + } + while ((pRelation = builder.nextRelation(l2))) + { + params.setIndex(l2); + pRelation->createTable(params); + } + bool bAddBracket = (sql.length() != iSqlCountRef); + sql = sql.left(sql.length() - 2); // Remove last ", " + if (bAddBracket) + { + sql += ")"; + } + } + + void IxSqlQueryBuilder::sql_DeleteById(QString &sql, IxSqlQueryBuilder &builder, bool bSoftDelete) + { + qx::IxDataMember *pId = builder.getDataId(); + qAssert(pId); + qx::QxSoftDelete oSoftDelete = builder.getSoftDelete(); + QString table = builder.table(); + if (bSoftDelete && !oSoftDelete.isEmpty()) + { + sql = "UPDATE " + qx::IxDataMember::getSqlTableName(table) + " SET " + oSoftDelete.buildSqlQueryToUpdate(); + } + else + { + sql = "DELETE FROM " + qx::IxDataMember::getSqlFromTable(table); + } + sql += IxSqlQueryBuilder::addSqlCondition(sql); + sql += pId->getSqlNameEqualToPlaceHolder("", " AND ", false, (&builder)); + } + + void IxSqlQueryBuilder::sql_Exist(QString &sql, IxSqlQueryBuilder &builder) + { + qx::IxDataMember *pId = builder.getDataId(); + qAssert(pId); + qx::QxSoftDelete oSoftDelete = builder.getSoftDelete(); + QString table = builder.table(); + sql = "SELECT "; + if (pId) + { + sql += pId->getSqlTablePointNameAsAlias(table, ", ", "", false, "", (&builder)); + } + if (!oSoftDelete.isEmpty()) + { + sql += ", " + oSoftDelete.buildSqlTablePointName(); + } + sql += " FROM " + qx::IxDataMember::getSqlFromTable(table); + sql += " WHERE " + pId->getSqlAliasEqualToPlaceHolder(table, true, "", " AND ", false, (&builder)); + if (!oSoftDelete.isEmpty()) + { + sql += " AND " + oSoftDelete.buildSqlQueryToFetch(); + } + } + + void IxSqlQueryBuilder::sql_FetchAll(QString &sql, IxSqlQueryBuilder &builder) + { + long l1(0), l2(0), l3(0), l4(0), l5(0); + qx::IxDataMember *p = NULL; + qx::IxDataMember *pId = builder.getDataId(); + qx::IxSqlRelation *pRelation = NULL; + qx::QxSqlRelationParams params(0, 0, (&sql), (&builder), NULL, NULL); + qx::QxSoftDelete oSoftDelete = builder.getSoftDelete(); + QString table = builder.table(); + sql = "SELECT "; + bool isDistinct = (builder.getDaoHelper() ? builder.getDaoHelper()->isDistinct() : false); + if (pId && (!isDistinct)) + { + sql += (pId->getSqlTablePointNameAsAlias(table, ", ", "", false, "", (&builder)) + ", "); + } + while ((p = builder.nextData(l1))) + { + sql += (p->getSqlTablePointNameAsAlias(table, ", ", "", false, "", (&builder)) + ", "); + } + if (!oSoftDelete.isEmpty()) + { + l1++; + sql += (oSoftDelete.buildSqlTablePointName() + ", "); + } + while ((pRelation = builder.nextRelation(l2))) + { + params.setIndex(l2); + pRelation->lazySelect(params); + } + sql = sql.left(sql.length() - 2); // Remove last ", " + sql += " FROM " + qx::IxDataMember::getSqlFromTable(table) + ", "; + while ((pRelation = builder.nextRelation(l3))) + { + params.setIndex(l3); + pRelation->lazyFrom(params); + } + sql = sql.left(sql.length() - 2); // Remove last ", " + while ((pRelation = builder.nextRelation(l4))) + { + params.setIndex(l4); + pRelation->lazyJoin(params); + } + if (!oSoftDelete.isEmpty()) + { + sql += " WHERE " + oSoftDelete.buildSqlQueryToFetch(); + } + while ((pRelation = builder.nextRelation(l5))) + { + params.setIndex(l5); + pRelation->lazyWhereSoftDelete(params); + } + } + + void IxSqlQueryBuilder::sql_FetchAll(QString &sql, IxSqlQueryBuilder &builder, const QStringList &columns) + { + qx::IxDataMember *p = NULL; + qx::IxDataMember *pId = builder.getDataId(); + qx::IxDataMemberX *pDataMemberX = builder.getDataMemberX(); + qAssert(pDataMemberX); + qx::QxSoftDelete oSoftDelete = builder.getSoftDelete(); + QString table = builder.table(); + sql = "SELECT "; + bool isDistinct = (builder.getDaoHelper() ? builder.getDaoHelper()->isDistinct() : false); + if (pId && (!isDistinct)) + { + sql += (pId->getSqlTablePointNameAsAlias(table, ", ", "", false, "", (&builder)) + ", "); + } + for (int i = 0; i < columns.count(); i++) + { + p = pDataMemberX->get_WithDaoStrategy(columns.at(i)); + if (p && (p != pId)) + { + sql += (p->getSqlTablePointNameAsAlias(table, ", ", "", false, "", (&builder)) + ", "); + } + } + sql = sql.left(sql.length() - 2); // Remove last ", " + if (!oSoftDelete.isEmpty()) + { + sql += ", " + oSoftDelete.buildSqlTablePointName(); + } + sql += " FROM " + qx::IxDataMember::getSqlFromTable(table); + if (!oSoftDelete.isEmpty()) + { + sql += " WHERE " + oSoftDelete.buildSqlQueryToFetch(); + } + } + + void IxSqlQueryBuilder::sql_FetchAll_WithRelation(qx::QxSqlRelationLinked *pRelationX, QString &sql, IxSqlQueryBuilder &builder) + { + long l(0); + qx::IxDataMember *p = NULL; + qx::IxDataMember *pId = builder.getDataId(); + qx::QxSqlRelationParams params(0, 0, (&sql), (&builder), NULL, NULL); + qx::QxSoftDelete oSoftDelete = builder.getSoftDelete(); + QString table = builder.table(); + sql = "SELECT "; + bool isDistinct = (builder.getDaoHelper() ? builder.getDaoHelper()->isDistinct() : false); + bool bForceRootId = ((isDistinct && pId) ? pRelationX->checkRootColumns(pId->getKey()) : false); + if (pId && ((!isDistinct) || (bForceRootId))) + { + sql += (pId->getSqlTablePointNameAsAlias(table, ", ", "", false, pRelationX->getRootCustomAlias(), (&builder)) + ", "); + } + while ((p = builder.nextData(l))) + { + if (pRelationX->checkRootColumns(p->getKey())) + { + sql += (p->getSqlTablePointNameAsAlias(table, ", ", "", false, pRelationX->getRootCustomAlias(), (&builder)) + ", "); + } + } + if (!oSoftDelete.isEmpty()) + { + l++; + sql += (oSoftDelete.buildSqlTablePointName(pRelationX->getRootCustomAlias()) + ", "); + } + pRelationX->hierarchySelect(params); + sql = sql.left(sql.length() - 2); // Remove last ", " + sql += " FROM " + qx::IxDataMember::getSqlFromTable(table, pRelationX->getRootCustomAlias()) + ", "; + pRelationX->hierarchyFrom(params); + sql = sql.left(sql.length() - 2); // Remove last ", " + pRelationX->hierarchyJoin(params); + if (!oSoftDelete.isEmpty()) + { + sql += " WHERE " + oSoftDelete.buildSqlQueryToFetch(pRelationX->getRootCustomAlias()); + } + pRelationX->hierarchyWhereSoftDelete(params); + } + + void IxSqlQueryBuilder::sql_FetchById(QString &sql, IxSqlQueryBuilder &builder) + { + qx::IxDataMember *pId = builder.getDataId(); + qAssert(pId); + QString table = builder.table(); + sql = builder.buildSql().getSqlQuery(); + sql += IxSqlQueryBuilder::addSqlCondition(sql); + sql += pId->getSqlAliasEqualToPlaceHolder(table, true, "", " AND ", false, (&builder)); + } + + void IxSqlQueryBuilder::sql_FetchById(QString &sql, IxSqlQueryBuilder &builder, const QStringList &columns) + { + qx::IxDataMember *pId = builder.getDataId(); + qAssert(pId); + QString table = builder.table(); + sql = builder.buildSql(columns).getSqlQuery(); + sql += IxSqlQueryBuilder::addSqlCondition(sql); + sql += pId->getSqlAliasEqualToPlaceHolder(table, true, "", " AND ", false, (&builder)); + } + + void IxSqlQueryBuilder::sql_FetchById_WithRelation(qx::QxSqlRelationLinked *pRelationX, QString &sql, IxSqlQueryBuilder &builder) + { + qx::IxDataMember *pId = builder.getDataId(); + qAssert(pId); + QString table = builder.table(); + QStringList columns; + sql = builder.buildSql(columns, pRelationX).getSqlQuery(); + QString tableAlias = (pRelationX ? pRelationX->getRootCustomAlias() : QString()); + if (tableAlias.isEmpty()) + { + tableAlias = table; + } + sql += IxSqlQueryBuilder::addSqlCondition(sql); + sql += pId->getSqlAliasEqualToPlaceHolder(tableAlias, true, "", " AND ", false, (&builder)); + } + + void IxSqlQueryBuilder::sql_Insert(QString &sql, IxSqlQueryBuilder &builder) + { + long l1(0), l2(0); + qx::IxDataMember *p = NULL; + qx::IxDataMember *pId = builder.getDataId(); + qx::IxSqlRelation *pRelation = NULL; + qx::QxSqlRelationParams params(0, 0, (&sql), (&builder), NULL, NULL); + QString table = builder.table(); + QString tmp; + sql = "INSERT INTO " + qx::IxDataMember::getSqlTableName(table) + " ("; + if (pId && !pId->getAutoIncrement()) + { + tmp = pId->getSqlName(", ", "", true, (&builder)); + if (!tmp.isEmpty()) + { + sql += tmp + ", "; + } + } + while ((p = builder.nextData(l1))) + { + sql += p->getSqlName(", ", "", false, (&builder)) + ", "; + } + while ((pRelation = builder.nextRelation(l2))) + { + params.setIndex(l2); + pRelation->lazyInsert(params); + } + sql = sql.left(sql.length() - 2); // Remove last ", " + sql += ") VALUES ("; + l1 = 0; + l2 = 0; + p = NULL; + pRelation = NULL; + if (pId && !pId->getAutoIncrement()) + { + tmp = pId->getSqlPlaceHolder("", -1, ", ", "", true); + if (!tmp.isEmpty()) + { + sql += tmp + ", "; + } + } + while ((p = builder.nextData(l1))) + { + sql += p->getSqlPlaceHolder("", -1, ", ") + ", "; + } + while ((pRelation = builder.nextRelation(l2))) + { + params.setIndex(l2); + pRelation->lazyInsert_Values(params); + } + sql = sql.left(sql.length() - 2); // Remove last ", " + sql += ")"; + } + + void IxSqlQueryBuilder::sql_Update(QString &sql, IxSqlQueryBuilder &builder) + { + long l1(0), l2(0); + qx::IxDataMember *p = NULL; + qx::IxDataMember *pId = builder.getDataId(); + qAssert(pId); + qx::IxSqlRelation *pRelation = NULL; + qx::QxSqlRelationParams params(0, 0, (&sql), (&builder), NULL, NULL); + QString table = builder.table(); + QString tmp; + sql = "UPDATE " + qx::IxDataMember::getSqlTableName(table) + " SET "; + if (!pId->getAutoIncrement() || (pId->getAutoIncrement() && builder.getAddAutoIncrementIdToUpdateQuery())) + { + tmp = pId->getSqlNameEqualToPlaceHolder("", ", ", true, (&builder)); + if (!tmp.isEmpty()) + { + sql += tmp + ", "; + } + } + while ((p = builder.nextData(l1))) + { + sql += p->getSqlNameEqualToPlaceHolder("", ", ", false, (&builder)) + ", "; + } + while ((pRelation = builder.nextRelation(l2))) + { + params.setIndex(l2); + pRelation->lazyUpdate(params); + } + sql = sql.left(sql.length() - 2); // Remove last ", " + sql += " WHERE " + pId->getSqlNameEqualToPlaceHolder("_bis", " AND ", false, (&builder)); + } + + void IxSqlQueryBuilder::sql_Update(QString &sql, IxSqlQueryBuilder &builder, const QStringList &columns) + { + qx::IxDataMember *p = NULL; + qx::IxDataMember *pId = builder.getDataId(); + qAssert(pId); + qx::IxDataMemberX *pDataMemberX = builder.getDataMemberX(); + qAssert(pDataMemberX); + QString table = builder.table(); + sql = "UPDATE " + qx::IxDataMember::getSqlTableName(table) + " SET "; + if (!pId->getAutoIncrement() || (pId->getAutoIncrement() && builder.getAddAutoIncrementIdToUpdateQuery())) + { + sql += pId->getSqlNameEqualToPlaceHolder("", ", ", false, (&builder)) + ", "; + } + for (int i = 0; i < columns.count(); i++) + { + p = pDataMemberX->get_WithDaoStrategy(columns.at(i)); + if (p && (p != pId)) + { + sql += p->getSqlNameEqualToPlaceHolder("", ", ", false, (&builder)) + ", "; + } + } + sql = sql.left(sql.length() - 2); // Remove last ", " + sql += " WHERE " + pId->getSqlNameEqualToPlaceHolder("_bis", " AND ", false, (&builder)); + } + + void IxSqlQueryBuilder::sql_Count_WithRelation(qx::QxSqlRelationLinked *pRelationX, QString &sql, IxSqlQueryBuilder &builder) + { + if (!pRelationX) + { + qAssert(false); + return; + } + qx::QxSqlRelationParams params(0, 0, (&sql), (&builder), NULL, NULL); + qx::QxSoftDelete oSoftDelete = builder.getSoftDelete(); + QString table = builder.table(); + sql = "SELECT COUNT(*) FROM " + qx::IxDataMember::getSqlFromTable(table, pRelationX->getRootCustomAlias()) + ", "; + pRelationX->hierarchyFrom(params); + sql = sql.left(sql.length() - 2); // Remove last ", " + pRelationX->hierarchyJoin(params); + if (!oSoftDelete.isEmpty()) + { + sql += " WHERE " + oSoftDelete.buildSqlQueryToFetch(pRelationX->getRootCustomAlias()); + } + pRelationX->hierarchyWhereSoftDelete(params); + } + + void IxSqlQueryBuilder::resolveOutput_FetchAll(void *t, QSqlQuery &query, IxSqlQueryBuilder &builder) + { + long l1(0), l2(0); + qx::IxDataMember *p = NULL; + qx::IxDataMember *pId = builder.getDataId(); + qx::IxSqlRelation *pRelation = NULL; + qx::QxSoftDelete oSoftDelete = builder.getSoftDelete(); + bool isDistinct = (builder.getDaoHelper() ? builder.getDaoHelper()->isDistinct() : false); + short iOffset = ((pId && (!isDistinct)) ? pId->getNameCount() : 0); + if (pId && (!isDistinct)) + { + for (int i = 0; i < pId->getNameCount(); i++) + { + pId->fromVariant(t, query.value(i), i, qx::cvt::context::e_database); + } + } + while ((p = builder.nextData(l1))) + { + p->fromVariant(t, query.value(l1 + iOffset - 1), -1, qx::cvt::context::e_database); + } + iOffset = (builder.getDataCount() + iOffset + (oSoftDelete.isEmpty() ? 0 : 1)); + qx::QxSqlRelationParams params(0, iOffset, NULL, (&builder), (&query), t); + while ((pRelation = builder.nextRelation(l2))) + { + params.setIndex(l2); + pRelation->lazyFetch_ResolveOutput(params); + } + } + + void IxSqlQueryBuilder::resolveOutput_FetchAll(void *t, QSqlQuery &query, IxSqlQueryBuilder &builder, const QStringList &columns) + { + qx::IxDataMember *p = NULL; + int idx = 0; + qx::IxDataMember *pId = builder.getDataId(); + qx::IxDataMemberX *pDataMemberX = builder.getDataMemberX(); + qAssert(pDataMemberX); + bool isDistinct = (builder.getDaoHelper() ? builder.getDaoHelper()->isDistinct() : false); + short iOffset = ((pId && (!isDistinct)) ? pId->getNameCount() : 0); + if (pId && (!isDistinct)) + { + for (int i = 0; i < pId->getNameCount(); i++) + { + pId->fromVariant(t, query.value(i), i, qx::cvt::context::e_database); + } + } + for (int i = 0; i < columns.count(); i++) + { + p = pDataMemberX->get_WithDaoStrategy(columns.at(i)); + if (p && (p != pId)) + { + p->fromVariant(t, query.value(idx + iOffset), -1, qx::cvt::context::e_database); + idx++; + } + } + } + + void IxSqlQueryBuilder::resolveOutput_FetchAll_WithRelation(qx::QxSqlRelationLinked *pRelationX, void *t, QSqlQuery &query, IxSqlQueryBuilder &builder) + { + long l(0); + long lCurrIndex(0); + QVariant vId; + qx::IxDataMember *p = NULL; + qx::IxDataMember *pId = builder.getDataId(); + qx::QxSoftDelete oSoftDelete = builder.getSoftDelete(); + qx::dao::detail::IxDao_Helper *pDaoHelper = builder.getDaoHelper(); + bool isDistinct = (pDaoHelper ? pDaoHelper->isDistinct() : false); + bool bForceRootId = ((isDistinct && pId) ? pRelationX->checkRootColumns(pId->getKey()) : false); + short iOffsetId = ((pId && ((!isDistinct) || (bForceRootId))) ? pId->getNameCount() : 0); + vId = (pDaoHelper ? pDaoHelper->getIdFromQuery(-1) : QVariant()); + bool bComplex = builder.getCartesianProduct(); + bool bByPass = (bComplex && builder.existIdX(0, vId, vId)); + + if (!bByPass) + { + if (pId && ((!isDistinct) || (bForceRootId))) + { + for (int i = 0; i < pId->getNameCount(); i++) + { + pId->fromVariant(t, query.value(i), i, qx::cvt::context::e_database); + } + } + while ((p = builder.nextData(l))) + { + if (pRelationX->checkRootColumns(p->getKey())) + { + p->fromVariant(t, query.value(lCurrIndex + iOffsetId), -1, qx::cvt::context::e_database); + lCurrIndex++; + } + } + if (bComplex) + { + builder.insertIdX(0, vId, vId, t); + } + } + + short iOffset = (builder.getDataCount() + iOffsetId + (oSoftDelete.isEmpty() ? 0 : 1)); + if ((pRelationX->getRootColumnsCount() > 0) && (pRelationX->getRootColumnsOffset() > 0)) + { + iOffset = (iOffset - pRelationX->getRootColumnsOffset()); + } + else if (pRelationX->getRootColumnsCount() > 0) + { + l = 0; + p = NULL; + long lRootColumnsOffset = 0; + while ((p = builder.nextData(l))) + { + if (!pRelationX->checkRootColumns(p->getKey())) + { + iOffset = (iOffset - 1); + lRootColumnsOffset++; + } + } + pRelationX->setRootColumnsOffset(lRootColumnsOffset); + } + + qx::QxSqlRelationParams params(0, iOffset, NULL, (&builder), (&query), t, vId); + pRelationX->hierarchyResolveOutput(params); + } + + void IxSqlQueryBuilder::resolveInput_Insert(void *t, QSqlQuery &query, IxSqlQueryBuilder &builder) + { + long l1(0), l2(0); + qx::IxDataMember *p = NULL; + qx::IxDataMember *pId = builder.getDataId(); + qx::IxSqlRelation *pRelation = NULL; + bool bUseExecBatch = (builder.getDaoHelper() ? builder.getDaoHelper()->getUseExecBatch() : false); + qx::QxCollection *pLstExecBatch = (bUseExecBatch ? (&builder.getDaoHelper()->getListExecBatch()) : NULL); + qx::QxSqlRelationParams params(0, 0, NULL, (&builder), (&query), t, QVariant(), pLstExecBatch); + if (pId && !pId->getAutoIncrement()) + { + pId->setSqlPlaceHolder(query, t, "", "", true, pLstExecBatch); + } + while ((p = builder.nextData(l1))) + { + p->setSqlPlaceHolder(query, t, "", "", false, pLstExecBatch); + } + while ((pRelation = builder.nextRelation(l2))) + { + params.setIndex(l2); + pRelation->lazyInsert_ResolveInput(params); + } + } + + void IxSqlQueryBuilder::resolveInput_Update(void *t, QSqlQuery &query, IxSqlQueryBuilder &builder) + { + long l1(0), l2(0); + qx::IxDataMember *p = NULL; + qx::IxDataMember *pId = builder.getDataId(); + qAssert(pId); + qx::IxSqlRelation *pRelation = NULL; + bool bUseExecBatch = (builder.getDaoHelper() ? builder.getDaoHelper()->getUseExecBatch() : false); + qx::QxCollection *pLstExecBatch = (bUseExecBatch ? (&builder.getDaoHelper()->getListExecBatch()) : NULL); + qx::QxSqlRelationParams params(0, 0, NULL, (&builder), (&query), t, QVariant(), pLstExecBatch); + if (!pId->getAutoIncrement() || (pId->getAutoIncrement() && builder.getAddAutoIncrementIdToUpdateQuery())) + { + pId->setSqlPlaceHolder(query, t, "", "", true, pLstExecBatch); + } + while ((p = builder.nextData(l1))) + { + p->setSqlPlaceHolder(query, t, "", "", false, pLstExecBatch); + } + while ((pRelation = builder.nextRelation(l2))) + { + params.setIndex(l2); + pRelation->lazyUpdate_ResolveInput(params); + } + pId->setSqlPlaceHolder(query, t, "_bis", "", false, pLstExecBatch); + } + + void IxSqlQueryBuilder::resolveInput_Update(void *t, QSqlQuery &query, IxSqlQueryBuilder &builder, const QStringList &columns) + { + qx::IxDataMember *p = NULL; + qx::IxDataMember *pId = builder.getDataId(); + qAssert(pId); + qx::IxDataMemberX *pDataMemberX = builder.getDataMemberX(); + qAssert(pDataMemberX); + bool bUseExecBatch = (builder.getDaoHelper() ? builder.getDaoHelper()->getUseExecBatch() : false); + qx::QxCollection *pLstExecBatch = (bUseExecBatch ? (&builder.getDaoHelper()->getListExecBatch()) : NULL); + if (!pId->getAutoIncrement() || (pId->getAutoIncrement() && builder.getAddAutoIncrementIdToUpdateQuery())) + { + pId->setSqlPlaceHolder(query, t, "", "", false, pLstExecBatch); + } + for (int i = 0; i < columns.count(); i++) + { + p = pDataMemberX->get_WithDaoStrategy(columns.at(i)); + if (p && (p != pId)) + { + p->setSqlPlaceHolder(query, t, "", "", false, pLstExecBatch); + } + } + pId->setSqlPlaceHolder(query, t, "_bis", "", false, pLstExecBatch); + } + + void IxSqlQueryBuilder::resolveInput_DeleteById(void *t, QSqlQuery &query, IxSqlQueryBuilder &builder) + { + qx::IxDataMember *pId = builder.getDataId(); + qAssert(pId); + bool bUseExecBatch = (builder.getDaoHelper() ? builder.getDaoHelper()->getUseExecBatch() : false); + qx::QxCollection *pLstExecBatch = (bUseExecBatch ? (&builder.getDaoHelper()->getListExecBatch()) : NULL); + pId->setSqlPlaceHolder(query, t, "", "", false, pLstExecBatch); + } + +} // namespace qx diff --git a/src/QxDao/IxSqlRelation.cpp b/src/QxDao/IxSqlRelation.cpp new file mode 100644 index 0000000..3376501 --- /dev/null +++ b/src/QxDao/IxSqlRelation.cpp @@ -0,0 +1,1486 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) +#define QX_CONSTRUCT_IX_RELATION_MUTEX() m_mutex() +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) +#define QX_CONSTRUCT_IX_RELATION_MUTEX() m_mutex(QMutex::Recursive) +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + +#define QX_CONSTRUCT_IX_RELATION() \ + m_pClass(NULL), m_pClassOwner(NULL), m_pDataMember(p), m_pDataMemberX(NULL), \ + m_pDataMemberId(NULL), m_pDataMemberIdOwner(NULL), m_lOffsetRelation(100), \ + m_eJoinType(qx::dao::sql_join::left_outer_join), m_eRelationType(IxSqlRelation::no_relation), \ + m_bInitInEvent(false), m_bInitDone(false), m_iIsSameDataOwner(0), m_pLinkRelationKey(NULL), QX_CONSTRUCT_IX_RELATION_MUTEX() + +namespace qx +{ + + struct Q_DECL_HIDDEN IxSqlRelation::IxSqlRelationImpl + { + + typedef QxCollection type_lst_data_member; + typedef std::shared_ptr type_lst_data_member_ptr; + + IxClass *m_pClass; //!< 'IxClass' associated wth sql relation (relation target) + IxClass *m_pClassOwner; //!< 'IxClass' of the owner (relation source) + IxDataMember *m_pDataMember; //!< 'IxDataMember' associated wth sql relation + IxDataMemberX *m_pDataMemberX; //!< Collection of 'IxDataMember' : parent of 'm_pDataMember' + IxDataMember *m_pDataMemberId; //!< 'IxDataMember' id of 'm_pDataMemberX' + IxDataMember *m_pDataMemberIdOwner; //!< 'IxDataMember' id of the owner + long m_lOffsetRelation; //!< Generic offset for sql relation + qx::dao::sql_join::join_type m_eJoinType; //!< Join type to build sql query + IxSqlRelation::relation_type m_eRelationType; //!< Relation type : one-to-one, one-to-many, etc. + QxSoftDelete m_oSoftDelete; //!< Soft delete (or logical delete) behavior + QxSoftDelete m_oSoftDeleteEmpty; //!< Keep an empty soft delete instance (used with qx::QxSession::ignoreSoftDelete()) + QString m_sForeignKey; //!< SQL query foreign key (1-n) + QString m_sExtraTable; //!< Extra-table that holds the relationship (n-n) + QString m_sForeignKeyOwner; //!< SQL query foreign key for owner (n-n) + QString m_sForeignKeyDataType; //!< SQL query foreign key for data type (n-n) + bool m_bInitInEvent; //!< Class initialization in progress + bool m_bInitDone; //!< Class initialization finished + int m_iIsSameDataOwner; //!< Check if relationship source entity and target entity are equal + IxDataMember *m_pLinkRelationKey; //!< Link relation key to another data member (used by MongoDB to simulate lazy loading) + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QRecursiveMutex m_mutex; //!< Mutex => 'qx::IxSqlRelation' is thread-safe (initialization process) +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QMutex m_mutex; //!< Mutex => 'qx::IxSqlRelation' is thread-safe (initialization process) +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + + type_lst_data_member_ptr m_lstDataMemberPtr; //!< Optimization : handle to collection of 'IxDataMember' + std::shared_ptr m_lstSqlRelationPtr; //!< Optimization : handle to collection of 'IxSqlRelation' + + static bool m_bTraceRelationInit; //!< Can be useful to debug an issue with relationship initialization + + IxSqlRelationImpl(IxDataMember *p) : QX_CONSTRUCT_IX_RELATION() { ; } + ~IxSqlRelationImpl() { ; } + + IxDataMember *isValid_DataMember(long lIndex) const + { + if (!m_pDataMemberX) + { + return NULL; + } + IxDataMember *p = m_pDataMemberX->get_WithDaoStrategy(lIndex); + bool bValid = (p && p->getDao() && !p->hasSqlRelation()); + bValid = (bValid && (p != m_pDataMemberId)); + return (bValid ? p : NULL); + } + + IxDataMember *isValid_SqlRelation(long lIndex) const + { + if (!m_pDataMemberX) + { + return NULL; + } + IxDataMember *p = m_pDataMemberX->get_WithDaoStrategy(lIndex); + bool bIsValid = (p && p->getDao() && p->hasSqlRelation()); + if (bIsValid && (!m_iIsSameDataOwner) && (p != m_pDataMember)) + { + p->getSqlRelation()->init(); + } + return (bIsValid ? p : NULL); + } + + const QxSoftDelete &getSoftDelete(QxSqlRelationParams ¶ms) const + { + qx::IxSqlQueryBuilder &builder = params.builder(); + qx::dao::detail::IxDao_Helper *pDaoHelper = builder.getDaoHelper(); + qx::QxSession *pSession = (pDaoHelper ? pDaoHelper->getSession() : NULL); + if ((!pSession) || (!m_pClass)) + { + return m_oSoftDelete; + } + bool bIgnoreSoftDelete = pSession->checkIgnoreSoftDelete(m_pClass->getKey()); + return (bIgnoreSoftDelete ? m_oSoftDeleteEmpty : m_oSoftDelete); + } + }; + + bool IxSqlRelation::IxSqlRelationImpl::m_bTraceRelationInit = false; + + IxSqlRelation::IxSqlRelation(IxDataMember *p) : qx::QxPropertyBag(), m_pImpl(new IxSqlRelationImpl(p)) { ; } + + IxSqlRelation::~IxSqlRelation() { ; } + + QString IxSqlRelation::getForeignKey() const { return m_pImpl->m_sForeignKey; } + + QString IxSqlRelation::getForeignKeyOwner() const { return m_pImpl->m_sForeignKeyOwner; } + + QString IxSqlRelation::getForeignKeyDataType() const { return m_pImpl->m_sForeignKeyDataType; } + + QString IxSqlRelation::getExtraTable() const { return m_pImpl->m_sExtraTable; } + + QxCollection *IxSqlRelation::getLstDataMember() const { return m_pImpl->m_lstDataMemberPtr.get(); } + + IxSqlRelationX *IxSqlRelation::getLstRelation() const { return m_pImpl->m_lstSqlRelationPtr.get(); } + + void IxSqlRelation::setSqlJoinType(qx::dao::sql_join::join_type e) { m_pImpl->m_eJoinType = e; } + + qx::dao::sql_join::join_type IxSqlRelation::getSqlJoinType() const { return m_pImpl->m_eJoinType; } + + IxSqlRelation::relation_type IxSqlRelation::getRelationType() const { return m_pImpl->m_eRelationType; } + + IxClass *IxSqlRelation::getClass() const { return m_pImpl->m_pClass; } + + IxClass *IxSqlRelation::getClassOwner() const { return m_pImpl->m_pClassOwner; } + + IxDataMember *IxSqlRelation::getDataMember() const { return m_pImpl->m_pDataMember; } + + IxDataMemberX *IxSqlRelation::getDataMemberX() const { return m_pImpl->m_pDataMemberX; } + + IxDataMember *IxSqlRelation::getDataId() const { return m_pImpl->m_pDataMemberId; } + + IxDataMember *IxSqlRelation::getDataIdOwner() const { return m_pImpl->m_pDataMemberIdOwner; } + + void IxSqlRelation::linkRelationKeyTo(IxDataMember *p) { m_pImpl->m_pLinkRelationKey = p; } + + IxDataMember *IxSqlRelation::getLinkRelationKey() const { return m_pImpl->m_pLinkRelationKey; } + + void IxSqlRelation::setIsSameDataOwner(int i) { m_pImpl->m_iIsSameDataOwner = i; } + + bool IxSqlRelation::canInit() const + { + if (m_pImpl->m_bInitDone) + { + return false; + } + { + QMutexLocker locker(&m_pImpl->m_mutex); + return (!m_pImpl->m_bInitInEvent); + } + } + + void IxSqlRelation::setClass(IxClass *pClass, IxClass *pClassOwner) + { + m_pImpl->m_pClass = pClass; + m_pImpl->m_pClassOwner = pClassOwner; + } + + void IxSqlRelation::setRelationType(IxSqlRelation::relation_type e) { m_pImpl->m_eRelationType = e; } + + void IxSqlRelation::setForeignKey(const QString &s) const { m_pImpl->m_sForeignKey = s; } + + void IxSqlRelation::setForeignKeyOwner(const QString &s) const { m_pImpl->m_sForeignKeyOwner = s; } + + void IxSqlRelation::setForeignKeyDataType(const QString &s) const { m_pImpl->m_sForeignKeyDataType = s; } + + void IxSqlRelation::setExtraTable(const QString &s) const { m_pImpl->m_sExtraTable = s; } + + void IxSqlRelation::init() + { + QMutexLocker locker(&m_pImpl->m_mutex); + if (m_pImpl->m_bInitInEvent || m_pImpl->m_bInitDone) + { + return; + } + m_pImpl->m_bInitInEvent = true; + + m_pImpl->m_pDataMemberX = (m_pImpl->m_pClass ? m_pImpl->m_pClass->getDataMemberX() : NULL); + m_pImpl->m_pDataMemberId = (m_pImpl->m_pDataMemberX ? m_pImpl->m_pDataMemberX->getId_WithDaoStrategy() : NULL); + m_pImpl->m_pDataMemberIdOwner = ((m_pImpl->m_pClassOwner && m_pImpl->m_pClassOwner->getDataMemberX()) ? m_pImpl->m_pClassOwner->getDataMemberX()->getId_WithDaoStrategy() : NULL); + if (m_pImpl->m_pClass) + { + m_pImpl->m_oSoftDelete = m_pImpl->m_pClass->getSoftDelete(); + } + +#ifdef _QX_MODE_DEBUG + QString sCheckMsg = "Check relationship '" + this->getKey() + "' from '" + (m_pImpl->m_pClassOwner ? m_pImpl->m_pClassOwner->getKey() : QString()) + "' to '" + (m_pImpl->m_pClass ? m_pImpl->m_pClass->getKey() : QString()) + "'"; + if (!m_pImpl->m_pClass) + { + QString sAssertMsg = sCheckMsg + " : m_pImpl->m_pClass is equal to NULL"; + qAssertMsg(false, "[QxOrm] qx::IxSqlRelation::init()", qPrintable(sAssertMsg)); + } + if (!m_pImpl->m_pClassOwner) + { + QString sAssertMsg = sCheckMsg + " : m_pImpl->m_pClassOwner is equal to NULL"; + qAssertMsg(false, "[QxOrm] qx::IxSqlRelation::init()", qPrintable(sAssertMsg)); + } + if (!m_pImpl->m_pDataMember) + { + QString sAssertMsg = sCheckMsg + " : m_pImpl->m_pDataMember is equal to NULL"; + qAssertMsg(false, "[QxOrm] qx::IxSqlRelation::init()", qPrintable(sAssertMsg)); + } + if (!m_pImpl->m_pDataMemberX) + { + QString sAssertMsg = sCheckMsg + " : m_pImpl->m_pDataMemberX is equal to NULL"; + qAssertMsg(false, "[QxOrm] qx::IxSqlRelation::init()", qPrintable(sAssertMsg)); + } + if (!m_pImpl->m_pDataMemberId) + { + QString sAssertMsg = sCheckMsg + " : m_pImpl->m_pDataMemberId is equal to NULL"; + qAssertMsg(false, "[QxOrm] qx::IxSqlRelation::init()", qPrintable(sAssertMsg)); + } +#endif // _QX_MODE_DEBUG + + if (IxSqlRelation::IxSqlRelationImpl::m_bTraceRelationInit) + { + QString sTraceMsg = "[QxOrm] Init relationship '" + this->getKey() + "' from '" + (m_pImpl->m_pClassOwner ? m_pImpl->m_pClassOwner->getKey() : QString()) + "' to '" + (m_pImpl->m_pClass ? m_pImpl->m_pClass->getKey() : QString()) + "'"; + qDebug() << sTraceMsg; + } + + m_pImpl->m_lstSqlRelationPtr = std::make_shared(); + m_pImpl->m_lstDataMemberPtr = std::make_shared>(); + IxDataMember *p = NULL; + long lCount = m_pImpl->m_pDataMemberX->count_WithDaoStrategy(); + + for (long l = 0; l < lCount; ++l) + { + p = m_pImpl->isValid_DataMember(l); + if (!p) + { + continue; + } +#ifdef _QX_MODE_DEBUG + if (m_pImpl->m_lstDataMemberPtr->exist(p->getKey())) + { + QString sDebugMsg = "[QxOrm] Relationship '" + this->getKey() + "' from '" + (m_pImpl->m_pClassOwner ? m_pImpl->m_pClassOwner->getKey() : QString()) + "' to '" + (m_pImpl->m_pClass ? m_pImpl->m_pClass->getKey() : QString()) + "' : data member '" + p->getKey() + "' already exists in the collection"; + qDebug() << sDebugMsg; + } +#endif // _QX_MODE_DEBUG + m_pImpl->m_lstDataMemberPtr->insert(p->getKey(), p); + } + + for (long l = 0; l < lCount; ++l) + { + p = m_pImpl->isValid_SqlRelation(l); + if (!p) + { + continue; + } +#ifdef _QX_MODE_DEBUG + if (m_pImpl->m_lstSqlRelationPtr->exist(p->getKey())) + { + QString sDebugMsg = "[QxOrm] Relationship '" + this->getKey() + "' from '" + (m_pImpl->m_pClassOwner ? m_pImpl->m_pClassOwner->getKey() : QString()) + "' to '" + (m_pImpl->m_pClass ? m_pImpl->m_pClass->getKey() : QString()) + "' : relation '" + p->getKey() + "' already exists in the collection"; + qDebug() << sDebugMsg; + } +#endif // _QX_MODE_DEBUG + m_pImpl->m_lstSqlRelationPtr->insert(p->getKey(), p->getSqlRelation()); + } + + if (m_pImpl->m_eRelationType == qx::IxSqlRelation::many_to_one) + { + // Check if relationship (foreign key) is also a part of primary key + int iRelationNameCount = (m_pImpl->m_pDataMember ? m_pImpl->m_pDataMember->getNameCount() : 0); + int iIdOwnerNameCount = (m_pImpl->m_pDataMemberIdOwner ? m_pImpl->m_pDataMemberIdOwner->getNameCount() : 0); + for (int i = 0; i < iRelationNameCount; i++) + { + for (int j = 0; j < iIdOwnerNameCount; j++) + { + if (m_pImpl->m_pDataMember->getName(i) == m_pImpl->m_pDataMemberIdOwner->getName(j)) + { + m_pImpl->m_pDataMemberIdOwner->setRelationPartOfPrimaryKey(j, this, i); + m_pImpl->m_pDataMember->setPartOfPrimaryKey(i, m_pImpl->m_pDataMemberIdOwner, j); + } + } + } + } + + m_pImpl->m_bInitDone = true; + m_pImpl->m_bInitInEvent = false; + } + + void IxSqlRelation::setTraceRelationInit(bool bTrace) { IxSqlRelation::IxSqlRelationImpl::m_bTraceRelationInit = bTrace; } + + QString IxSqlRelation::getKey() const + { + return (m_pImpl->m_pDataMember ? m_pImpl->m_pDataMember->getKey() : ""); + } + + long IxSqlRelation::getDataCount() const + { + return (m_pImpl->m_lstDataMemberPtr ? m_pImpl->m_lstDataMemberPtr->count() : 0); + } + + long IxSqlRelation::getRelationCount() const + { + return (m_pImpl->m_lstSqlRelationPtr ? m_pImpl->m_lstSqlRelationPtr->count() : 0); + } + + QString IxSqlRelation::table() const + { + return (m_pImpl->m_pDataMemberX ? m_pImpl->m_pDataMemberX->getName() : ""); + } + + bool IxSqlRelation::traceSqlQuery() const + { + return qx::QxSqlDatabase::getSingleton()->getTraceSqlQuery(); + } + + IxDataMember *IxSqlRelation::getDataByKey(const QString &sKey) const + { + if (m_pImpl->m_lstDataMemberPtr && m_pImpl->m_lstDataMemberPtr->exist(sKey)) + { + return m_pImpl->m_lstDataMemberPtr->getByKey(sKey); + } + else if (m_pImpl->m_lstSqlRelationPtr && m_pImpl->m_lstSqlRelationPtr->exist(sKey)) + { + return m_pImpl->m_lstSqlRelationPtr->getByKey(sKey)->getDataMember(); + } + return NULL; + } + + IxDataMember *IxSqlRelation::nextData(long &lIndex) const + { + if ((!m_pImpl->m_lstDataMemberPtr) || (lIndex < 0) || (lIndex >= m_pImpl->m_lstDataMemberPtr->count())) + { + return NULL; + } + ++lIndex; + return m_pImpl->m_lstDataMemberPtr->getByIndex(lIndex - 1); + } + + IxSqlRelation *IxSqlRelation::nextRelation(long &lIndex) const + { + if ((!m_pImpl->m_lstSqlRelationPtr) || (lIndex < 0) || (lIndex >= m_pImpl->m_lstSqlRelationPtr->count())) + { + return NULL; + } + ++lIndex; + return m_pImpl->m_lstSqlRelationPtr->getByIndex(lIndex - 1); + } + + QString IxSqlRelation::tableAlias(QxSqlRelationParams ¶ms) const + { + if (!params.getCustomAlias().isEmpty()) + { + return params.getCustomAlias(); + } + QString sTableAlias = (m_pImpl->m_pDataMemberX ? (m_pImpl->m_pDataMemberX->getName() + "_" + QString::number(params.index())) : QString("")); + sTableAlias.replace(".", "_"); + return IxDataMember::getSqlTableNameAlias(sTableAlias); + } + + QString IxSqlRelation::tableAliasOwner(QxSqlRelationParams ¶ms) const + { + if (!m_pImpl->m_pClassOwner) + { + qAssert(false); + return ""; + } + if (!params.getCustomAliasOwner().isEmpty()) + { + return params.getCustomAliasOwner(); + } + QString sTableAliasOwner = (m_pImpl->m_pClassOwner->getName() + "_" + QString::number(params.indexOwner())); + if (params.indexOwner() <= 0) + { + sTableAliasOwner = params.builder().table(); + } + if (!params.getTableAlias().isEmpty()) + { + sTableAliasOwner = params.getTableAlias(); + } + sTableAliasOwner.replace(".", "_"); + return sTableAliasOwner; + } + + QString IxSqlRelation::getSqlJoin(qx::dao::sql_join::join_type e /* = qx::dao::sql_join::no_join */) const + { + QString sJoin; + if (e == qx::dao::sql_join::no_join) + { + e = m_pImpl->m_eJoinType; + } + + switch (e) + { + case qx::dao::sql_join::left_outer_join: + sJoin = " LEFT OUTER JOIN "; + break; + case qx::dao::sql_join::inner_join: + sJoin = " INNER JOIN "; + break; + default: + sJoin = " LEFT OUTER JOIN "; + break; + } + + return sJoin; + } + + bool IxSqlRelation::verifyOffset(QxSqlRelationParams ¶ms, bool bId) const + { +#ifdef _QX_MODE_DEBUG + if ((!qx::QxSqlDatabase::getSingleton()->getVerifyOffsetRelation()) || (params.isDistinct())) + { + return true; + } + IxDataMember *p = (bId ? this->getDataId() : this->getDataMember()); + QString table = (bId ? this->tableAlias(params) : this->tableAliasOwner(params)); + if (!p || table.isEmpty()) + { + return true; + } + QString sSuffixAlias = ((!bId && (params.indexOwner() > 0)) ? QString("_" + QString::number(params.indexOwner())) : QString()); + QString sRecordToFind = p->getSqlAlias(table, false, 0, (¶ms.builder())) + sSuffixAlias; + int index = params.query().record().indexOf(sRecordToFind); + qAssert(index == params.offset()); + return (index == params.offset()); +#else // _QX_MODE_DEBUG + Q_UNUSED(params); + Q_UNUSED(bId); + return true; +#endif // _QX_MODE_DEBUG + } + + QVariant IxSqlRelation::getIdFromQuery_ManyToMany(bool bEager, QxSqlRelationParams ¶ms, int iOffset, int iNameIndex) const + { + QVariant vId; + IxDataMember *pId = this->getDataId(); + if (!bEager || !pId) + { + return QVariant(); + } + iOffset = ((iOffset < 0) ? params.offset() : iOffset); + if ((!params.isDistinct()) && (iNameIndex < 0)) + { + QString sId; + for (int i = 0; i < pId->getNameCount(); i++) + { + sId += params.query().value(iOffset + i).toString() + "|"; + } + vId = sId; + } + else if ((!params.isDistinct()) && (iNameIndex >= 0)) + { + qAssert(iNameIndex < pId->getNameCount()); + return params.query().value(iOffset + iNameIndex); + } + else if (params.isDistinct()) + { + QString sId; + IxDataMember *p = NULL; + long lIndex = 0; + long lCurrIndex = 0; + while ((p = this->nextData(lIndex))) + { + if (params.checkColumns(p->getKey())) + { + sId += params.query().value(iOffset + lCurrIndex).toString() + "|"; + lCurrIndex++; + } + } + vId = static_cast(qHash(sId)); + } + return vId; + } + + QVariant IxSqlRelation::getIdFromQuery_ManyToOne(bool bEager, QxSqlRelationParams ¶ms, int iOffset, int iNameIndex) const + { + Q_UNUSED(bEager); + QVariant vId; + IxDataMember *pId = this->getDataId(); + if (!pId) + { + return QVariant(); + } + iOffset = ((iOffset < 0) ? params.offset() : iOffset); + if ((!params.isDistinct()) && (iNameIndex < 0)) + { + QString sId; + for (int i = 0; i < pId->getNameCount(); i++) + { + sId += params.query().value(iOffset + i).toString() + "|"; + } + vId = sId; + } + else if ((!params.isDistinct()) && (iNameIndex >= 0)) + { + qAssert(iNameIndex < pId->getNameCount()); + return params.query().value(iOffset + iNameIndex); + } + else if (params.isDistinct()) + { + QString sId; + IxDataMember *p = NULL; + long lIndex = 0; + long lCurrIndex = 0; + while ((p = this->nextData(lIndex))) + { + if (params.checkColumns(p->getKey())) + { + sId += params.query().value(iOffset + lCurrIndex).toString() + "|"; + lCurrIndex++; + } + } + vId = static_cast(qHash(sId)); + } + return vId; + } + + QVariant IxSqlRelation::getIdFromQuery_OneToMany(bool bEager, QxSqlRelationParams ¶ms, int iOffset, int iNameIndex) const + { + QVariant vId; + IxDataMember *pId = this->getDataId(); + if (!bEager || !pId) + { + return QVariant(); + } + iOffset = ((iOffset < 0) ? params.offset() : iOffset); + if ((!params.isDistinct()) && (iNameIndex < 0)) + { + QString sId; + for (int i = 0; i < pId->getNameCount(); i++) + { + sId += params.query().value(iOffset + i).toString() + "|"; + } + vId = sId; + } + else if ((!params.isDistinct()) && (iNameIndex >= 0)) + { + qAssert(iNameIndex < pId->getNameCount()); + return params.query().value(iOffset + iNameIndex); + } + else if (params.isDistinct()) + { + QString sId; + IxDataMember *p = NULL; + IxDataMember *pForeign = this->getDataByKey(this->getForeignKey()); + long lIndex = 0; + long lCurrIndex = 0; + while ((p = this->nextData(lIndex))) + { + if ((p != pForeign) && (params.checkColumns(p->getKey()))) + { + sId += params.query().value(iOffset + lCurrIndex).toString() + "|"; + lCurrIndex++; + } + } + vId = static_cast(qHash(sId)); + } + return vId; + } + + QVariant IxSqlRelation::getIdFromQuery_OneToOne(bool bEager, QxSqlRelationParams ¶ms, int iOffset, int iNameIndex) const + { + QVariant vId; + IxDataMember *pId = this->getDataId(); + if (!bEager || !pId) + { + return QVariant(); + } + iOffset = ((iOffset < 0) ? params.offset() : iOffset); + if ((!params.isDistinct()) && (iNameIndex < 0)) + { + QString sId; + for (int i = 0; i < pId->getNameCount(); i++) + { + sId += params.query().value(iOffset + i).toString() + "|"; + } + vId = sId; + } + else if ((!params.isDistinct()) && (iNameIndex >= 0)) + { + qAssert(iNameIndex < pId->getNameCount()); + return params.query().value(iOffset + iNameIndex); + } + else if (params.isDistinct()) + { + QString sId; + IxDataMember *p = NULL; + long lIndex = 0; + long lCurrIndex = 0; + while ((p = this->nextData(lIndex))) + { + if (params.checkColumns(p->getKey())) + { + sId += params.query().value(iOffset + lCurrIndex).toString() + "|"; + lCurrIndex++; + } + } + vId = static_cast(qHash(sId)); + } + return vId; + } + + void IxSqlRelation::updateOffset_ManyToMany(bool bEager, QxSqlRelationParams ¶ms) const + { + if (!bEager) + { + return; + } + const QxSoftDelete &oSoftDelete = this->m_pImpl->getSoftDelete(params); + long lOffsetNew = params.offset() + this->getDataCount(); + lOffsetNew += ((this->getDataId() && (!params.isDistinct())) ? this->getDataId()->getNameCount() : 0); + lOffsetNew += (oSoftDelete.isEmpty() ? 0 : 1); + params.setOffset(lOffsetNew); + + if (bEager && (params.getColumnsCount() > 0) && (params.getColumnsOffset() > 0)) + { + lOffsetNew = (lOffsetNew - params.getColumnsOffset()); + params.setOffset(lOffsetNew); + } + else if (bEager && (params.getColumnsCount() > 0)) + { + long l1 = 0; + IxDataMember *p = NULL; + long lColumnsOffset = 0; + while ((p = this->nextData(l1))) + { + if (!params.checkColumns(p->getKey())) + { + lOffsetNew = (lOffsetNew - 1); + lColumnsOffset++; + } + } + params.setOffset(lOffsetNew); + params.setColumnsOffset(lColumnsOffset); + } + + if (!params.relationX()) + { + return; + } + long lRelation = 0; + IxSqlRelation *pRelation = NULL; + long lIndexOwnerOld = params.indexOwner(); + params.setIndexOwner(params.index()); + QString sOldCustomAliasOwner = params.getCustomAliasOwner(); + params.setCustomAliasOwner(params.getCustomAlias()); + while ((pRelation = this->nextRelation(lRelation))) + { + if (this->addLazyRelation(params, pRelation)) + { + pRelation->updateOffset(false, params); + } + } + params.setIndexOwner(lIndexOwnerOld); + params.setCustomAliasOwner(sOldCustomAliasOwner); + } + + void IxSqlRelation::updateOffset_ManyToOne(bool bEager, QxSqlRelationParams ¶ms) const + { + const QxSoftDelete &oSoftDelete = this->m_pImpl->getSoftDelete(params); + long lOffsetDataMember = ((this->getDataMember() && (!params.isDistinct())) ? this->getDataMember()->getNameCount() : 0); + long lOffsetDataId = (bEager ? ((this->getDataId() && (!params.isDistinct())) ? this->getDataId()->getNameCount() : 0) : 0); + long lOffsetDataCount = (bEager ? this->getDataCount() : 0); + long lOffsetSoftDelete = (bEager ? (oSoftDelete.isEmpty() ? 0 : 1) : 0); + long lOffsetNew = (params.offset() + lOffsetDataMember + lOffsetDataId + lOffsetDataCount + lOffsetSoftDelete); + params.setOffset(lOffsetNew); + + if (bEager && (params.getColumnsCount() > 0) && (params.getColumnsOffset() > 0)) + { + lOffsetNew = (lOffsetNew - params.getColumnsOffset()); + params.setOffset(lOffsetNew); + } + else if (bEager && (params.getColumnsCount() > 0)) + { + long l1 = 0; + IxDataMember *p = NULL; + long lColumnsOffset = 0; + while ((p = this->nextData(l1))) + { + if (!params.checkColumns(p->getKey())) + { + lOffsetNew = (lOffsetNew - 1); + lColumnsOffset++; + } + } + params.setOffset(lOffsetNew); + params.setColumnsOffset(lColumnsOffset); + } + + if (!bEager || !params.relationX()) + { + return; + } + long lRelation = 0; + IxSqlRelation *pRelation = NULL; + long lIndexOwnerOld = params.indexOwner(); + params.setIndexOwner(params.index()); + QString sOldCustomAliasOwner = params.getCustomAliasOwner(); + params.setCustomAliasOwner(params.getCustomAlias()); + while ((pRelation = this->nextRelation(lRelation))) + { + if (this->addLazyRelation(params, pRelation)) + { + pRelation->updateOffset(false, params); + } + } + params.setIndexOwner(lIndexOwnerOld); + params.setCustomAliasOwner(sOldCustomAliasOwner); + } + + void IxSqlRelation::updateOffset_OneToMany(bool bEager, QxSqlRelationParams ¶ms) const + { + if (!bEager) + { + return; + } + const QxSoftDelete &oSoftDelete = this->m_pImpl->getSoftDelete(params); + IxDataMember *pForeign = this->getDataByKey(m_pImpl->m_sForeignKey); + bool bAddOffsetForeign = (pForeign && !this->getLstDataMember()->exist(m_pImpl->m_sForeignKey)); + long lOffsetId = ((this->getDataId() && (!params.isDistinct())) ? this->getDataId()->getNameCount() : 0); + long lOffsetNew = (params.offset() + this->getDataCount() + lOffsetId); + lOffsetNew = (lOffsetNew + ((bAddOffsetForeign && (!params.isDistinct())) ? pForeign->getNameCount() : 0)); + lOffsetNew = (lOffsetNew + (oSoftDelete.isEmpty() ? 0 : 1)); + params.setOffset(lOffsetNew); + + if (bEager && (params.getColumnsCount() > 0) && (params.getColumnsOffset() > 0)) + { + lOffsetNew = (lOffsetNew - params.getColumnsOffset()); + params.setOffset(lOffsetNew); + } + else if (bEager && (params.getColumnsCount() > 0)) + { + long l1 = 0; + IxDataMember *p = NULL; + long lColumnsOffset = 0; + while ((p = this->nextData(l1))) + { + if (!params.checkColumns(p->getKey())) + { + lOffsetNew = (lOffsetNew - 1); + lColumnsOffset++; + } + } + params.setOffset(lOffsetNew); + params.setColumnsOffset(lColumnsOffset); + } + + if (!params.relationX()) + { + return; + } + long lRelation = 0; + IxSqlRelation *pRelation = NULL; + long lIndexOwnerOld = params.indexOwner(); + params.setIndexOwner(params.index()); + QString sOldCustomAliasOwner = params.getCustomAliasOwner(); + params.setCustomAliasOwner(params.getCustomAlias()); + while ((pRelation = this->nextRelation(lRelation))) + { + if (this->addLazyRelation(params, pRelation)) + { + pRelation->updateOffset(false, params); + } + } + params.setIndexOwner(lIndexOwnerOld); + params.setCustomAliasOwner(sOldCustomAliasOwner); + } + + void IxSqlRelation::updateOffset_OneToOne(bool bEager, QxSqlRelationParams ¶ms) const + { + if (!bEager) + { + return; + } + const QxSoftDelete &oSoftDelete = this->m_pImpl->getSoftDelete(params); + long lOffsetNew = params.offset() + this->getDataCount(); + lOffsetNew += ((this->getDataId() && (!params.isDistinct())) ? this->getDataId()->getNameCount() : 0); + lOffsetNew += (oSoftDelete.isEmpty() ? 0 : 1); + params.setOffset(lOffsetNew); + + if (bEager && (params.getColumnsCount() > 0) && (params.getColumnsOffset() > 0)) + { + lOffsetNew = (lOffsetNew - params.getColumnsOffset()); + params.setOffset(lOffsetNew); + } + else if (bEager && (params.getColumnsCount() > 0)) + { + long l1 = 0; + IxDataMember *p = NULL; + long lColumnsOffset = 0; + while ((p = this->nextData(l1))) + { + if (!params.checkColumns(p->getKey())) + { + lOffsetNew = (lOffsetNew - 1); + lColumnsOffset++; + } + } + params.setOffset(lOffsetNew); + params.setColumnsOffset(lColumnsOffset); + } + + if (!params.relationX()) + { + return; + } + long lRelation = 0; + IxSqlRelation *pRelation = NULL; + long lIndexOwnerOld = params.indexOwner(); + params.setIndexOwner(params.index()); + QString sOldCustomAliasOwner = params.getCustomAliasOwner(); + params.setCustomAliasOwner(params.getCustomAlias()); + while ((pRelation = this->nextRelation(lRelation))) + { + if (this->addLazyRelation(params, pRelation)) + { + pRelation->updateOffset(false, params); + } + } + params.setIndexOwner(lIndexOwnerOld); + params.setCustomAliasOwner(sOldCustomAliasOwner); + } + + void IxSqlRelation::eagerSelect_ManyToMany(QxSqlRelationParams ¶ms) const + { + long l1(0), l2(0); + QString &sql = params.sql(); + IxSqlRelation *pRelation = NULL; + IxDataMember *p = NULL; + IxDataMember *pId = this->getDataId(); + qAssert(pId); + QString tableAlias = this->tableAlias(params); + if (pId && (!params.isDistinct())) + { + sql += (pId->getSqlTablePointNameAsAlias(tableAlias, ", ", "", false, "", (¶ms.builder())) + ", "); + } + while ((p = this->nextData(l1))) + { + if (params.checkColumns(p->getKey())) + { + sql += (p->getSqlTablePointNameAsAlias(tableAlias, ", ", "", false, "", (¶ms.builder())) + ", "); + } + } + + if (params.relationX()) + { + long lIndexOwnerOld = params.indexOwner(); + params.setIndexOwner(params.index()); + QString sTableAliasOld = params.getTableAlias(); + params.setTableAlias(tableAlias); + QString sOldCustomAliasOwner = params.getCustomAliasOwner(); + params.setCustomAliasOwner(params.getCustomAlias()); + while ((pRelation = this->nextRelation(l2))) + { + if (this->addLazyRelation(params, pRelation)) + { + pRelation->lazySelect(params); + } + } + params.setIndexOwner(lIndexOwnerOld); + params.setTableAlias(sTableAliasOld); + params.setCustomAliasOwner(sOldCustomAliasOwner); + } + + const QxSoftDelete &oSoftDelete = this->m_pImpl->getSoftDelete(params); + if (!oSoftDelete.isEmpty()) + { + sql += (oSoftDelete.buildSqlTablePointName(tableAlias) + ", "); + } + } + + void IxSqlRelation::eagerSelect_ManyToOne(QxSqlRelationParams ¶ms) const + { + long l1(0), l2(0); + QString &sql = params.sql(); + IxSqlRelation *pRelation = NULL; + IxDataMember *pData = this->getDataMember(); + qAssert(pData); + IxDataMember *p = NULL; + IxDataMember *pId = this->getDataId(); + qAssert(pId); + QString table = this->table(); + QString tableAlias = this->tableAlias(params); + QString tableRef = this->tableAliasOwner(params); + QString sSuffixAlias; + if (params.indexOwner() > 0) + { + sSuffixAlias = "_" + QString::number(params.indexOwner()); + } + if (pData && (!params.isDistinct())) + { + sql += (pData->getSqlTablePointNameAsAlias(tableRef, ", ", sSuffixAlias, false, "", (¶ms.builder())) + ", "); + } + if (pId && (!params.isDistinct())) + { + sql += (pId->getSqlTablePointNameAsAlias(tableAlias, ", ", "", false, "", (¶ms.builder())) + ", "); + } + while ((p = this->nextData(l1))) + { + if (params.checkColumns(p->getKey())) + { + sql += (p->getSqlTablePointNameAsAlias(tableAlias, ", ", "", false, "", (¶ms.builder())) + ", "); + } + } + + if (params.relationX()) + { + long lIndexOwnerOld = params.indexOwner(); + params.setIndexOwner(params.index()); + QString sTableAliasOld = params.getTableAlias(); + params.setTableAlias(tableAlias); + QString sOldCustomAliasOwner = params.getCustomAliasOwner(); + params.setCustomAliasOwner(params.getCustomAlias()); + while ((pRelation = this->nextRelation(l2))) + { + if (this->addLazyRelation(params, pRelation)) + { + pRelation->lazySelect(params); + } + } + params.setIndexOwner(lIndexOwnerOld); + params.setTableAlias(sTableAliasOld); + params.setCustomAliasOwner(sOldCustomAliasOwner); + } + + const QxSoftDelete &oSoftDelete = this->m_pImpl->getSoftDelete(params); + if (!oSoftDelete.isEmpty()) + { + sql += (oSoftDelete.buildSqlTablePointName(tableAlias) + ", "); + } + } + + void IxSqlRelation::eagerSelect_OneToMany(QxSqlRelationParams ¶ms) const + { + long l1(0), l2(0); + QString &sql = params.sql(); + IxSqlRelation *pRelation = NULL; + IxDataMember *pForeign = this->getDataByKey(this->m_pImpl->m_sForeignKey); + qAssert(pForeign); + IxDataMember *p = NULL; + IxDataMember *pId = this->getDataId(); + qAssert(pId); + QString tableAlias = this->tableAlias(params); + if (pId && (!params.isDistinct())) + { + sql += (pId->getSqlTablePointNameAsAlias(tableAlias, ", ", "", false, "", (¶ms.builder())) + ", "); + } + if (pForeign && (!params.isDistinct())) + { + sql += (pForeign->getSqlTablePointNameAsAlias(tableAlias, ", ", "", false, "", (¶ms.builder())) + ", "); + } + while ((p = this->nextData(l1))) + { + if ((p != pForeign) && (params.checkColumns(p->getKey()))) + { + sql += (p->getSqlTablePointNameAsAlias(tableAlias, ", ", "", false, "", (¶ms.builder())) + ", "); + } + } + + if (params.relationX()) + { + long lIndexOwnerOld = params.indexOwner(); + params.setIndexOwner(params.index()); + QString sTableAliasOld = params.getTableAlias(); + params.setTableAlias(tableAlias); + QString sOldCustomAliasOwner = params.getCustomAliasOwner(); + params.setCustomAliasOwner(params.getCustomAlias()); + while ((pRelation = this->nextRelation(l2))) + { + if (this->addLazyRelation(params, pRelation)) + { + pRelation->lazySelect(params); + } + } + params.setIndexOwner(lIndexOwnerOld); + params.setTableAlias(sTableAliasOld); + params.setCustomAliasOwner(sOldCustomAliasOwner); + } + + const QxSoftDelete &oSoftDelete = this->m_pImpl->getSoftDelete(params); + if (!oSoftDelete.isEmpty()) + { + sql += (oSoftDelete.buildSqlTablePointName(tableAlias) + ", "); + } + } + + void IxSqlRelation::eagerSelect_OneToOne(QxSqlRelationParams ¶ms) const + { + long l1(0), l2(0); + QString &sql = params.sql(); + IxSqlRelation *pRelation = NULL; + QString tableAlias = this->tableAlias(params); + IxDataMember *p = NULL; + IxDataMember *pId = this->getDataId(); + qAssert(pId); + if (pId && (!params.isDistinct())) + { + sql += (pId->getSqlTablePointNameAsAlias(tableAlias, ", ", "", false, "", (¶ms.builder())) + ", "); + } + while ((p = this->nextData(l1))) + { + if (params.checkColumns(p->getKey())) + { + sql += (p->getSqlTablePointNameAsAlias(tableAlias, ", ", "", false, "", (¶ms.builder())) + ", "); + } + } + + if (params.relationX()) + { + long lIndexOwnerOld = params.indexOwner(); + params.setIndexOwner(params.index()); + QString sTableAliasOld = params.getTableAlias(); + params.setTableAlias(tableAlias); + QString sOldCustomAliasOwner = params.getCustomAliasOwner(); + params.setCustomAliasOwner(params.getCustomAlias()); + while ((pRelation = this->nextRelation(l2))) + { + if (this->addLazyRelation(params, pRelation)) + { + pRelation->lazySelect(params); + } + } + params.setIndexOwner(lIndexOwnerOld); + params.setTableAlias(sTableAliasOld); + params.setCustomAliasOwner(sOldCustomAliasOwner); + } + + const QxSoftDelete &oSoftDelete = this->m_pImpl->getSoftDelete(params); + if (!oSoftDelete.isEmpty()) + { + sql += (oSoftDelete.buildSqlTablePointName(tableAlias) + ", "); + } + } + + void IxSqlRelation::eagerJoin_ManyToMany(QxSqlRelationParams ¶ms) const + { + QString &sql = params.sql(); + IxDataMember *pIdOwner = this->getDataIdOwner(); + qAssert(pIdOwner); + IxDataMember *pIdData = this->getDataId(); + qAssert(pIdData); + QString table = this->table(); + QString tableAlias = this->tableAlias(params); + QString tableOwner = this->tableAliasOwner(params); + if (!pIdOwner || !pIdData) + { + return; + } + QStringList lstForeignKeyOwner = m_pImpl->m_sForeignKeyOwner.split("|"); + QStringList lstForeignKeyDataType = m_pImpl->m_sForeignKeyDataType.split("|"); + qAssert(pIdOwner->getNameCount() == lstForeignKeyOwner.count()); + qAssert(pIdData->getNameCount() == lstForeignKeyDataType.count()); + qx::dao::detail::IxDao_Helper *pDaoHelper = params.builder().getDaoHelper(); + QString sExtraTableAlias = m_pImpl->m_sExtraTable + "_" + QString::number(params.index()); + sql += this->getSqlJoin(params.joinType()) + qx::IxDataMember::getSqlTableName(m_pImpl->m_sExtraTable) + " " + sExtraTableAlias + " ON "; + for (int i = 0; i < pIdOwner->getNameCount(); i++) + { + sql += pIdOwner->getSqlAlias(tableOwner, true, i, (¶ms.builder())) + " = " + sExtraTableAlias + "." + lstForeignKeyOwner.at(i) + " AND "; + } + sql = sql.left(sql.length() - 5); // Remove last " AND " + + QString joinQuery; + if (pDaoHelper) + { + qx_query &query = pDaoHelper->qxQuery(); + joinQuery = query.getJoinQuery(this->getKey(), tableAlias); + } + + const QxSoftDelete &oSoftDelete = this->m_pImpl->getSoftDelete(params); + sql += this->getSqlJoin(params.joinType()) + qx::IxDataMember::getSqlTableName(table) + " " + tableAlias + " ON "; + if (!joinQuery.isEmpty()) + { + sql += "("; + } + params.builder().addSqlQueryAlias(table, tableAlias); + for (int i = 0; i < pIdData->getNameCount(); i++) + { + sql += sExtraTableAlias + "." + lstForeignKeyDataType.at(i) + " = " + pIdData->getSqlAlias(tableAlias, true, i, (¶ms.builder())) + " AND "; + } + if (!oSoftDelete.isEmpty() && oSoftDelete.getSqlFetchInJoin()) + { + sql += oSoftDelete.buildSqlQueryToFetch(tableAlias) + " AND "; + } + sql = sql.left(sql.length() - 5); // Remove last " AND " + if (!joinQuery.isEmpty()) + { + sql += " " + joinQuery + ")"; + } + } + + void IxSqlRelation::eagerJoin_ManyToOne(QxSqlRelationParams ¶ms) const + { + QString &sql = params.sql(); + IxDataMember *pId = this->getDataId(); + qAssert(pId); + IxDataMember *pData = this->getDataMember(); + qAssert(pData); + QString table = this->table(); + QString tableAlias = this->tableAlias(params); + QString tableRef = this->tableAliasOwner(params); + if (!pId || !pData) + { + return; + } + + QString joinQuery; + qx::dao::detail::IxDao_Helper *pDaoHelper = params.builder().getDaoHelper(); + if (pDaoHelper) + { + qx_query &query = pDaoHelper->qxQuery(); + joinQuery = query.getJoinQuery(this->getKey(), tableAlias); + } + + const QxSoftDelete &oSoftDelete = this->m_pImpl->getSoftDelete(params); + sql += this->getSqlJoin(params.joinType()) + qx::IxDataMember::getSqlTableName(table) + " " + tableAlias + " ON "; + if (!joinQuery.isEmpty()) + { + sql += "("; + } + params.builder().addSqlQueryAlias(table, tableAlias); + for (int i = 0; i < pId->getNameCount(); i++) + { + sql += pId->getSqlAlias(tableAlias, true, i, (¶ms.builder())) + " = " + pData->getSqlAlias(tableRef, true, i, (¶ms.builder())) + " AND "; + } + if (!oSoftDelete.isEmpty() && oSoftDelete.getSqlFetchInJoin()) + { + sql += oSoftDelete.buildSqlQueryToFetch(tableAlias) + " AND "; + } + sql = sql.left(sql.length() - 5); // Remove last " AND " + if (!joinQuery.isEmpty()) + { + sql += " " + joinQuery + ")"; + } + } + + void IxSqlRelation::eagerJoin_OneToMany(QxSqlRelationParams ¶ms) const + { + QString &sql = params.sql(); + IxDataMember *pId = this->getDataIdOwner(); + qAssert(pId); + IxDataMember *pForeign = this->getDataByKey(this->m_pImpl->m_sForeignKey); + qAssert(pForeign); + QString table = this->table(); + QString tableAlias = this->tableAlias(params); + QString tableRef = this->tableAliasOwner(params); + if (!pId || !pForeign) + { + return; + } + + QString joinQuery; + qx::dao::detail::IxDao_Helper *pDaoHelper = params.builder().getDaoHelper(); + if (pDaoHelper) + { + qx_query &query = pDaoHelper->qxQuery(); + joinQuery = query.getJoinQuery(this->getKey(), tableAlias); + } + + const QxSoftDelete &oSoftDelete = this->m_pImpl->getSoftDelete(params); + sql += this->getSqlJoin(params.joinType()) + qx::IxDataMember::getSqlTableName(table) + " " + tableAlias + " ON "; + if (!joinQuery.isEmpty()) + { + sql += "("; + } + params.builder().addSqlQueryAlias(table, tableAlias); + for (int i = 0; i < pId->getNameCount(); i++) + { + sql += pForeign->getSqlAlias(tableAlias, true, i, (¶ms.builder())) + " = " + pId->getSqlAlias(tableRef, true, i, (¶ms.builder())) + " AND "; + } + if (!oSoftDelete.isEmpty() && oSoftDelete.getSqlFetchInJoin()) + { + sql += oSoftDelete.buildSqlQueryToFetch(tableAlias) + " AND "; + } + sql = sql.left(sql.length() - 5); // Remove last " AND " + if (!joinQuery.isEmpty()) + { + sql += " " + joinQuery + ")"; + } + } + + void IxSqlRelation::eagerJoin_OneToOne(QxSqlRelationParams ¶ms) const + { + QString &sql = params.sql(); + IxDataMember *pId = this->getDataId(); + qAssert(pId); + IxDataMember *pIdRef = this->getDataIdOwner(); + qAssert(pIdRef); + QString table = this->table(); + QString tableAlias = this->tableAlias(params); + QString tableRef = this->tableAliasOwner(params); + if (!pId || !pIdRef) + { + return; + } + + QString joinQuery; + qx::dao::detail::IxDao_Helper *pDaoHelper = params.builder().getDaoHelper(); + if (pDaoHelper) + { + qx_query &query = pDaoHelper->qxQuery(); + joinQuery = query.getJoinQuery(this->getKey(), tableAlias); + } + + const QxSoftDelete &oSoftDelete = this->m_pImpl->getSoftDelete(params); + sql += this->getSqlJoin(params.joinType()) + qx::IxDataMember::getSqlTableName(table) + " " + tableAlias + " ON "; + if (!joinQuery.isEmpty()) + { + sql += "("; + } + params.builder().addSqlQueryAlias(table, tableAlias); + for (int i = 0; i < pId->getNameCount(); i++) + { + sql += pId->getSqlAlias(tableAlias, true, i, (¶ms.builder())) + " = " + pIdRef->getSqlAlias(tableRef, true, i, (¶ms.builder())) + " AND "; + } + if (!oSoftDelete.isEmpty() && oSoftDelete.getSqlFetchInJoin()) + { + sql += oSoftDelete.buildSqlQueryToFetch(tableAlias) + " AND "; + } + sql = sql.left(sql.length() - 5); // Remove last " AND " + if (!joinQuery.isEmpty()) + { + sql += " " + joinQuery + ")"; + } + } + + void IxSqlRelation::eagerWhereSoftDelete_ManyToMany(QxSqlRelationParams ¶ms) const + { + const QxSoftDelete &oSoftDelete = this->m_pImpl->getSoftDelete(params); + if (oSoftDelete.isEmpty()) + { + return; + } + if (oSoftDelete.getSqlFetchInJoin()) + { + return; + } + QString &sql = params.sql(); + QString tableAlias = this->tableAlias(params); + sql += qx::IxSqlQueryBuilder::addSqlCondition(sql); + sql += oSoftDelete.buildSqlQueryToFetch(tableAlias); + } + + void IxSqlRelation::eagerWhereSoftDelete_ManyToOne(QxSqlRelationParams ¶ms) const + { + const QxSoftDelete &oSoftDelete = this->m_pImpl->getSoftDelete(params); + if (oSoftDelete.isEmpty()) + { + return; + } + if (oSoftDelete.getSqlFetchInJoin()) + { + return; + } + QString &sql = params.sql(); + QString tableAlias = this->tableAlias(params); + sql += qx::IxSqlQueryBuilder::addSqlCondition(sql); + sql += oSoftDelete.buildSqlQueryToFetch(tableAlias); + } + + void IxSqlRelation::eagerWhereSoftDelete_OneToMany(QxSqlRelationParams ¶ms) const + { + const QxSoftDelete &oSoftDelete = this->m_pImpl->getSoftDelete(params); + if (oSoftDelete.isEmpty()) + { + return; + } + if (oSoftDelete.getSqlFetchInJoin()) + { + return; + } + QString &sql = params.sql(); + QString tableAlias = this->tableAlias(params); + sql += qx::IxSqlQueryBuilder::addSqlCondition(sql); + sql += oSoftDelete.buildSqlQueryToFetch(tableAlias); + } + + void IxSqlRelation::eagerWhereSoftDelete_OneToOne(QxSqlRelationParams ¶ms) const + { + const QxSoftDelete &oSoftDelete = this->m_pImpl->getSoftDelete(params); + if (oSoftDelete.isEmpty()) + { + return; + } + if (oSoftDelete.getSqlFetchInJoin()) + { + return; + } + QString &sql = params.sql(); + QString tableAlias = this->tableAlias(params); + sql += qx::IxSqlQueryBuilder::addSqlCondition(sql); + sql += oSoftDelete.buildSqlQueryToFetch(tableAlias); + } + + void IxSqlRelation::lazySelect_ManyToOne(QxSqlRelationParams ¶ms) const + { + QString &sql = params.sql(); + IxDataMember *pData = this->getDataMember(); + qAssert(pData); + QString tableRef = this->tableAliasOwner(params); + QString sSuffixAlias; + if (params.indexOwner() > 0) + { + sSuffixAlias = "_" + QString::number(params.indexOwner()); + } + if (pData) + { + sql += (pData->getSqlTablePointNameAsAlias(tableRef, ", ", sSuffixAlias, false, "", (¶ms.builder())) + ", "); + } + } + + void IxSqlRelation::lazyInsert_ManyToOne(QxSqlRelationParams ¶ms) const + { + QString &sql = params.sql(); + IxDataMember *pData = this->getDataMember(); + qAssert(pData); + if (pData) + { + sql += pData->getSqlName(", ", "", false, (¶ms.builder())) + ", "; + } + } + + void IxSqlRelation::lazyInsert_Values_ManyToOne(QxSqlRelationParams ¶ms) const + { + QString &sql = params.sql(); + IxDataMember *pData = this->getDataMember(); + qAssert(pData); + if (pData) + { + sql += pData->getSqlPlaceHolder("", -1, ", ") + ", "; + } + } + + void IxSqlRelation::lazyUpdate_ManyToOne(QxSqlRelationParams ¶ms) const + { + QString &sql = params.sql(); + IxDataMember *pData = this->getDataMember(); + qAssert(pData); + if (pData) + { + sql += pData->getSqlNameEqualToPlaceHolder("", ", ", false, (¶ms.builder())) + ", "; + } + } + + void IxSqlRelation::createTable_ManyToOne(QxSqlRelationParams ¶ms) const + { + QString &sql = params.sql(); + IxDataMember *pData = this->getDataMember(); + qAssert(pData); + if (pData) + { + sql += pData->getSqlNameAndTypeAndParams(", ") + ", "; + qAssert(!pData->getSqlType().isEmpty()); + } + } + + QString IxSqlRelation::createExtraTable_ManyToMany() const + { + IxDataMember *pIdOwner = this->getDataIdOwner(); + qAssert(pIdOwner); + IxDataMember *pIdData = this->getDataId(); + qAssert(pIdData); + if (!pIdOwner || !pIdData) + { + return ""; + } + + bool bOldPKOwner = pIdOwner->getIsPrimaryKey(); + pIdOwner->setIsPrimaryKey(false); + bool bOldPKData = ((pIdOwner == pIdData) ? bOldPKOwner : pIdData->getIsPrimaryKey()); + pIdData->setIsPrimaryKey(false); + bool bOldAIOwner = pIdOwner->getAutoIncrement(); + pIdOwner->setAutoIncrement(false); + bool bOldAIData = ((pIdOwner == pIdData) ? bOldAIOwner : pIdData->getAutoIncrement()); + pIdData->setAutoIncrement(false); + + QString sql = "CREATE TABLE IF NOT EXISTS " + this->m_pImpl->m_sExtraTable + " ("; + sql += pIdOwner->getSqlNameAndTypeAndParams(", ", this->m_pImpl->m_sForeignKeyOwner) + ", "; + qAssert(!pIdOwner->getSqlType().isEmpty()); + sql += pIdData->getSqlNameAndTypeAndParams(", ", this->m_pImpl->m_sForeignKeyDataType) + ", "; + qAssert(!pIdData->getSqlType().isEmpty()); + sql = sql.left(sql.length() - 2); // Remove last ", " + sql += ")"; + + pIdOwner->setIsPrimaryKey(bOldPKOwner); + pIdData->setIsPrimaryKey(bOldPKData); + pIdOwner->setAutoIncrement(bOldAIOwner); + pIdData->setAutoIncrement(bOldAIData); + if (this->traceSqlQuery()) + { + qDebug("[QxOrm] create extra-table (relation many-to-many) : %s", qPrintable(sql)); + } + return sql; + } + + QSqlError IxSqlRelation::deleteFromExtraTable_ManyToMany(QxSqlRelationParams ¶ms) const + { + IxDataMember *pIdOwner = this->getDataIdOwner(); + qAssert(pIdOwner); + QString sql = "DELETE FROM " + this->m_pImpl->m_sExtraTable + " WHERE "; + QStringList lstForeignKeyOwner = this->m_pImpl->m_sForeignKeyOwner.split("|"); + qAssert(pIdOwner->getNameCount() == lstForeignKeyOwner.count()); + for (int i = 0; i < pIdOwner->getNameCount(); i++) + { + sql += this->m_pImpl->m_sExtraTable + "." + lstForeignKeyOwner.at(i) + " = " + pIdOwner->getSqlPlaceHolder("", i) + " AND "; + } + sql = sql.left(sql.length() - 5); // Remove last " AND " + if (this->traceSqlQuery()) + { + qDebug("[QxOrm] sql query (extra-table) : %s", qPrintable(sql)); + } + + QSqlQuery queryDelete(params.database()); + if (!queryDelete.prepare(sql)) + { + return queryDelete.lastError(); + } + pIdOwner->setSqlPlaceHolder(queryDelete, params.owner()); + if (!queryDelete.exec()) + { + return queryDelete.lastError(); + } + return QSqlError(); + } + + bool IxSqlRelation::addLazyRelation(QxSqlRelationParams ¶ms, IxSqlRelation *pRelation) const + { + if (!params.relationX() || !pRelation || !params.checkColumns(pRelation->getKey())) + { + return false; + } + qx::QxSqlRelationLinked_ptr pRelationLinked = params.relationX()->value(this->getKey()); + return (!pRelationLinked || !pRelationLinked->existRelation(pRelation->getKey())); + } + +} // namespace qx diff --git a/src/QxDao/QxDaoAsync.cpp b/src/QxDao/QxDaoAsync.cpp new file mode 100644 index 0000000..8787e25 --- /dev/null +++ b/src/QxDao/QxDaoAsync.cpp @@ -0,0 +1,543 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include +#include + +#include + +#include + +#include + +#define QX_DAO_ASYNC_QUERY_ALREADY_RUNNING qDebug("[QxOrm] qx::QxDaoAsync : '%s'", "another query is already running"); + +namespace qx +{ + + QxDaoAsync::QxDaoAsync() : QThread() { ; } + + QxDaoAsync::~QxDaoAsync() + { + if (!isRunning()) + { + return; + } + qDebug("[QxOrm] qx::QxDaoAsync thread is running : %s", "quit and wait"); + quit(); + wait(); + } + + void QxDaoAsync::run() + { + qx::dao::detail::QxDaoAsyncRunner daoRunner; + qRegisterMetaType("QSqlError"); + qRegisterMetaType("qx::dao::detail::QxDaoAsyncParams_ptr"); + QObject::connect(this, SIGNAL(queryStarted(qx::dao::detail::QxDaoAsyncParams_ptr)), (&daoRunner), SLOT(onQueryStarted(qx::dao::detail::QxDaoAsyncParams_ptr))); + QObject::connect((&daoRunner), SIGNAL(queryFinished(const QSqlError &, qx::dao::detail::QxDaoAsyncParams_ptr)), this, SLOT(onQueryFinished(const QSqlError &, qx::dao::detail::QxDaoAsyncParams_ptr))); + if (m_pDaoParams && (m_pDaoParams->daoAction != qx::dao::detail::QxDaoAsyncParams::dao_none)) + { + daoRunner.onQueryStarted(m_pDaoParams); + } + exec(); + } + + void QxDaoAsync::startQuery() + { + if (!m_pDaoParams) + { + qAssert(false); + return; + } + if (isRunning()) + { + Q_EMIT queryStarted(m_pDaoParams); + } + else + { + start(); + } + } + + void QxDaoAsync::onQueryFinished(const QSqlError &daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams) + { + m_pDaoParams.reset(); + Q_EMIT queryFinished(daoError, pDaoParams); + } + + bool QxDaoAsync::asyncCount(const QString &className, const qx::QxSqlQuery &query /* = qx::QxSqlQuery() */, QSqlDatabase *pDatabase /* = NULL */) + { + QMutexLocker locker(&m_mutex); + if (m_pDaoParams) + { + QX_DAO_ASYNC_QUERY_ALREADY_RUNNING; + return false; + } + if (!QxClassX::implementIxPersistable(className)) + { + qAssert(false); + return false; + } + m_pDaoParams = std::make_shared(); + m_pDaoParams->daoAction = qx::dao::detail::QxDaoAsyncParams::dao_count; + m_pDaoParams->className = className; + m_pDaoParams->query = query; + m_pDaoParams->pDatabase = pDatabase; + startQuery(); + return true; + } + + bool QxDaoAsync::asyncFetchById(IxPersistable_ptr pToFetch, const QVariant &id /* = QVariant() */, const QStringList &columns /* = QStringList() */, const QStringList &relation /* = QStringList() */, QSqlDatabase *pDatabase /* = NULL */) + { + QMutexLocker locker(&m_mutex); + if (m_pDaoParams) + { + QX_DAO_ASYNC_QUERY_ALREADY_RUNNING; + return false; + } + m_pDaoParams = std::make_shared(); + m_pDaoParams->daoAction = qx::dao::detail::QxDaoAsyncParams::dao_fetch_by_id; + m_pDaoParams->pInstance = pToFetch; + m_pDaoParams->id = id; + m_pDaoParams->listColumns = columns; + m_pDaoParams->listRelations = relation; + m_pDaoParams->pDatabase = pDatabase; + startQuery(); + return true; + } + + bool QxDaoAsync::asyncFetchAll(const QString &className, const QStringList &columns /* = QStringList() */, const QStringList &relation /* = QStringList() */, QSqlDatabase *pDatabase /* = NULL */) + { + QMutexLocker locker(&m_mutex); + if (m_pDaoParams) + { + QX_DAO_ASYNC_QUERY_ALREADY_RUNNING; + return false; + } + if (!QxClassX::implementIxPersistable(className)) + { + qAssert(false); + return false; + } + m_pDaoParams = std::make_shared(); + m_pDaoParams->daoAction = qx::dao::detail::QxDaoAsyncParams::dao_fetch_all; + m_pDaoParams->className = className; + m_pDaoParams->listColumns = columns; + m_pDaoParams->listRelations = relation; + m_pDaoParams->pDatabase = pDatabase; + startQuery(); + return true; + } + + bool QxDaoAsync::asyncFetchByQuery(const QString &className, const qx::QxSqlQuery &query, const QStringList &columns /* = QStringList() */, const QStringList &relation /* = QStringList() */, QSqlDatabase *pDatabase /* = NULL */) + { + QMutexLocker locker(&m_mutex); + if (m_pDaoParams) + { + QX_DAO_ASYNC_QUERY_ALREADY_RUNNING; + return false; + } + if (!QxClassX::implementIxPersistable(className)) + { + qAssert(false); + return false; + } + m_pDaoParams = std::make_shared(); + m_pDaoParams->daoAction = qx::dao::detail::QxDaoAsyncParams::dao_fetch_by_query; + m_pDaoParams->className = className; + m_pDaoParams->query = query; + m_pDaoParams->listColumns = columns; + m_pDaoParams->listRelations = relation; + m_pDaoParams->pDatabase = pDatabase; + startQuery(); + return true; + } + + bool QxDaoAsync::asyncInsert(IxPersistable_ptr pToInsert, const QStringList &relation /* = QStringList() */, QSqlDatabase *pDatabase /* = NULL */) + { + QMutexLocker locker(&m_mutex); + if (m_pDaoParams) + { + QX_DAO_ASYNC_QUERY_ALREADY_RUNNING; + return false; + } + m_pDaoParams = std::make_shared(); + m_pDaoParams->daoAction = qx::dao::detail::QxDaoAsyncParams::dao_insert; + m_pDaoParams->pInstance = pToInsert; + m_pDaoParams->listRelations = relation; + m_pDaoParams->pDatabase = pDatabase; + startQuery(); + return true; + } + + bool QxDaoAsync::asyncUpdate(IxPersistable_ptr pToUpdate, const qx::QxSqlQuery &query /* = qx::QxSqlQuery() */, const QStringList &columns /* = QStringList() */, const QStringList &relation /* = QStringList() */, QSqlDatabase *pDatabase /* = NULL */) + { + QMutexLocker locker(&m_mutex); + if (m_pDaoParams) + { + QX_DAO_ASYNC_QUERY_ALREADY_RUNNING; + return false; + } + m_pDaoParams = std::make_shared(); + m_pDaoParams->daoAction = qx::dao::detail::QxDaoAsyncParams::dao_update; + m_pDaoParams->pInstance = pToUpdate; + m_pDaoParams->query = query; + m_pDaoParams->listColumns = columns; + m_pDaoParams->listRelations = relation; + m_pDaoParams->pDatabase = pDatabase; + startQuery(); + return true; + } + + bool QxDaoAsync::asyncSave(IxPersistable_ptr pToSave, const QStringList &relation /* = QStringList() */, QSqlDatabase *pDatabase /* = NULL */) + { + QMutexLocker locker(&m_mutex); + if (m_pDaoParams) + { + QX_DAO_ASYNC_QUERY_ALREADY_RUNNING; + return false; + } + m_pDaoParams = std::make_shared(); + m_pDaoParams->daoAction = qx::dao::detail::QxDaoAsyncParams::dao_save; + m_pDaoParams->pInstance = pToSave; + m_pDaoParams->listRelations = relation; + m_pDaoParams->pDatabase = pDatabase; + startQuery(); + return true; + } + + bool QxDaoAsync::asyncDeleteById(IxPersistable_ptr pToDelete, const QVariant &id /* = QVariant() */, QSqlDatabase *pDatabase /* = NULL */) + { + QMutexLocker locker(&m_mutex); + if (m_pDaoParams) + { + QX_DAO_ASYNC_QUERY_ALREADY_RUNNING; + return false; + } + m_pDaoParams = std::make_shared(); + m_pDaoParams->daoAction = qx::dao::detail::QxDaoAsyncParams::dao_delete_by_id; + m_pDaoParams->pInstance = pToDelete; + m_pDaoParams->id = id; + m_pDaoParams->pDatabase = pDatabase; + startQuery(); + return true; + } + + bool QxDaoAsync::asyncDeleteAll(const QString &className, QSqlDatabase *pDatabase /* = NULL */) + { + QMutexLocker locker(&m_mutex); + if (m_pDaoParams) + { + QX_DAO_ASYNC_QUERY_ALREADY_RUNNING; + return false; + } + if (!QxClassX::implementIxPersistable(className)) + { + qAssert(false); + return false; + } + m_pDaoParams = std::make_shared(); + m_pDaoParams->daoAction = qx::dao::detail::QxDaoAsyncParams::dao_delete_all; + m_pDaoParams->className = className; + m_pDaoParams->pDatabase = pDatabase; + startQuery(); + return true; + } + + bool QxDaoAsync::asyncDeleteByQuery(const QString &className, const qx::QxSqlQuery &query, QSqlDatabase *pDatabase /* = NULL */) + { + QMutexLocker locker(&m_mutex); + if (m_pDaoParams) + { + QX_DAO_ASYNC_QUERY_ALREADY_RUNNING; + return false; + } + if (!QxClassX::implementIxPersistable(className)) + { + qAssert(false); + return false; + } + m_pDaoParams = std::make_shared(); + m_pDaoParams->daoAction = qx::dao::detail::QxDaoAsyncParams::dao_delete_by_query; + m_pDaoParams->className = className; + m_pDaoParams->query = query; + m_pDaoParams->pDatabase = pDatabase; + startQuery(); + return true; + } + + bool QxDaoAsync::asyncDestroyById(IxPersistable_ptr pToDestroy, const QVariant &id /* = QVariant() */, QSqlDatabase *pDatabase /* = NULL */) + { + QMutexLocker locker(&m_mutex); + if (m_pDaoParams) + { + QX_DAO_ASYNC_QUERY_ALREADY_RUNNING; + return false; + } + m_pDaoParams = std::make_shared(); + m_pDaoParams->daoAction = qx::dao::detail::QxDaoAsyncParams::dao_destroy_by_id; + m_pDaoParams->pInstance = pToDestroy; + m_pDaoParams->id = id; + m_pDaoParams->pDatabase = pDatabase; + startQuery(); + return true; + } + + bool QxDaoAsync::asyncDestroyAll(const QString &className, QSqlDatabase *pDatabase /* = NULL */) + { + QMutexLocker locker(&m_mutex); + if (m_pDaoParams) + { + QX_DAO_ASYNC_QUERY_ALREADY_RUNNING; + return false; + } + if (!QxClassX::implementIxPersistable(className)) + { + qAssert(false); + return false; + } + m_pDaoParams = std::make_shared(); + m_pDaoParams->daoAction = qx::dao::detail::QxDaoAsyncParams::dao_destroy_all; + m_pDaoParams->className = className; + m_pDaoParams->pDatabase = pDatabase; + startQuery(); + return true; + } + + bool QxDaoAsync::asyncDestroyByQuery(const QString &className, const qx::QxSqlQuery &query, QSqlDatabase *pDatabase /* = NULL */) + { + QMutexLocker locker(&m_mutex); + if (m_pDaoParams) + { + QX_DAO_ASYNC_QUERY_ALREADY_RUNNING; + return false; + } + if (!QxClassX::implementIxPersistable(className)) + { + qAssert(false); + return false; + } + m_pDaoParams = std::make_shared(); + m_pDaoParams->daoAction = qx::dao::detail::QxDaoAsyncParams::dao_destroy_by_query; + m_pDaoParams->className = className; + m_pDaoParams->query = query; + m_pDaoParams->pDatabase = pDatabase; + startQuery(); + return true; + } + + bool QxDaoAsync::asyncExecuteQuery(const QString &className, qx::QxSqlQuery &query, QSqlDatabase *pDatabase /* = NULL */) + { + QMutexLocker locker(&m_mutex); + if (m_pDaoParams) + { + QX_DAO_ASYNC_QUERY_ALREADY_RUNNING; + return false; + } + if (!QxClassX::implementIxPersistable(className)) + { + qAssert(false); + return false; + } + m_pDaoParams = std::make_shared(); + m_pDaoParams->daoAction = qx::dao::detail::QxDaoAsyncParams::dao_execute_query; + m_pDaoParams->className = className; + m_pDaoParams->query = query; + m_pDaoParams->pDatabase = pDatabase; + startQuery(); + return true; + } + + bool QxDaoAsync::asyncCallQuery(qx::QxSqlQuery &query, QSqlDatabase *pDatabase /* = NULL */) + { + QMutexLocker locker(&m_mutex); + if (m_pDaoParams) + { + QX_DAO_ASYNC_QUERY_ALREADY_RUNNING; + return false; + } + m_pDaoParams = std::make_shared(); + m_pDaoParams->daoAction = qx::dao::detail::QxDaoAsyncParams::dao_call_query; + m_pDaoParams->query = query; + m_pDaoParams->pDatabase = pDatabase; + startQuery(); + return true; + } + + namespace dao + { + namespace detail + { + + QxDaoAsyncRunner::QxDaoAsyncRunner() : QObject() { ; } + + QxDaoAsyncRunner::~QxDaoAsyncRunner() { ; } + + void QxDaoAsyncRunner::onQueryStarted(qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams) + { + QSqlError daoError = this->runQuery(pDaoParams); + Q_EMIT queryFinished(daoError, pDaoParams); + } + + QSqlError QxDaoAsyncRunner::runQuery(qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams) + { + if (!pDaoParams) + { + qAssert(false); + return QSqlError("[QxOrm] qx::QxDaoAsync : 'null dao parameters'", "", QSqlError::UnknownError); + } + + QSqlError daoError; + QSqlDatabase *pDatabase(NULL); + QSqlDatabase db; + QString dbKey; + + try + { + if (!pDaoParams->className.isEmpty() && !pDaoParams->pInstance) + { + pDaoParams->pInstance.reset(static_cast(qx::create_void_ptr(pDaoParams->className))); + } + if (!pDaoParams->pInstance && (pDaoParams->daoAction != qx::dao::detail::QxDaoAsyncParams::dao_call_query)) + { + qAssert(false); + return QSqlError("[QxOrm] qx::QxDaoAsync : 'null instance implementing qx::IxPersistable interface'", "", QSqlError::UnknownError); + } + qx::IxPersistable_ptr pInstance = pDaoParams->pInstance; + + if ((pDaoParams->daoAction == qx::dao::detail::QxDaoAsyncParams::dao_fetch_all) || + (pDaoParams->daoAction == qx::dao::detail::QxDaoAsyncParams::dao_fetch_by_query) || + (pDaoParams->daoAction == qx::dao::detail::QxDaoAsyncParams::dao_execute_query)) + { + pDaoParams->pListOfInstances = pInstance->qxNewPersistableCollection(); + } + + if (pDaoParams->pDatabase != NULL) + { + dbKey = QUuid::createUuid().toString(); + db = QSqlDatabase::cloneDatabase((*pDaoParams->pDatabase), dbKey); + db.open(); + } + pDatabase = (pDaoParams->pDatabase ? (&db) : NULL); + + switch (pDaoParams->daoAction) + { + case qx::dao::detail::QxDaoAsyncParams::dao_count: + daoError = pInstance->qxCount(pDaoParams->daoCount, pDaoParams->query, pDatabase, pDaoParams->listRelations); + break; + case qx::dao::detail::QxDaoAsyncParams::dao_fetch_by_id: + daoError = pInstance->qxFetchById(pDaoParams->id, pDaoParams->listColumns, pDaoParams->listRelations, pDatabase); + break; + case qx::dao::detail::QxDaoAsyncParams::dao_fetch_all: + daoError = pInstance->qxFetchAll(pDaoParams->pListOfInstances.get(), pDaoParams->listColumns, pDaoParams->listRelations, pDatabase); + break; + case qx::dao::detail::QxDaoAsyncParams::dao_fetch_by_query: + daoError = pInstance->qxFetchByQuery(pDaoParams->query, pDaoParams->pListOfInstances.get(), pDaoParams->listColumns, pDaoParams->listRelations, pDatabase); + break; + case qx::dao::detail::QxDaoAsyncParams::dao_insert: + daoError = pInstance->qxInsert(pDaoParams->listRelations, pDatabase, pDaoParams->useExecBatch); + break; + case qx::dao::detail::QxDaoAsyncParams::dao_update: + daoError = pInstance->qxUpdate(pDaoParams->query, pDaoParams->listColumns, pDaoParams->listRelations, pDatabase, pDaoParams->useExecBatch); + break; + case qx::dao::detail::QxDaoAsyncParams::dao_save: + daoError = pInstance->qxSave(pDaoParams->listRelations, pDatabase); + break; + case qx::dao::detail::QxDaoAsyncParams::dao_delete_by_id: + daoError = pInstance->qxDeleteById(pDaoParams->id, pDatabase, pDaoParams->useExecBatch); + break; + case qx::dao::detail::QxDaoAsyncParams::dao_delete_all: + daoError = pInstance->qxDeleteAll(pDatabase); + break; + case qx::dao::detail::QxDaoAsyncParams::dao_delete_by_query: + daoError = pInstance->qxDeleteByQuery(pDaoParams->query, pDatabase); + break; + case qx::dao::detail::QxDaoAsyncParams::dao_destroy_by_id: + daoError = pInstance->qxDestroyById(pDaoParams->id, pDatabase, pDaoParams->useExecBatch); + break; + case qx::dao::detail::QxDaoAsyncParams::dao_destroy_all: + daoError = pInstance->qxDestroyAll(pDatabase); + break; + case qx::dao::detail::QxDaoAsyncParams::dao_destroy_by_query: + daoError = pInstance->qxDestroyByQuery(pDaoParams->query, pDatabase); + break; + case qx::dao::detail::QxDaoAsyncParams::dao_execute_query: + daoError = pInstance->qxExecuteQuery(pDaoParams->query, pDaoParams->pListOfInstances.get(), pDatabase); + break; + case qx::dao::detail::QxDaoAsyncParams::dao_call_query: + daoError = qx::dao::call_query(pDaoParams->query, pDatabase); + break; + default: + qAssert(false); + break; + } + } + catch (const qx::dao::sql_error &sqlErr) + { + daoError = sqlErr.get(); + } + catch (const std::exception &err) + { + daoError = QSqlError(err.what(), "", QSqlError::UnknownError); + } + catch (...) + { + daoError = QSqlError("[QxOrm] qx::QxDaoAsync : 'unknown error executing query'", "", QSqlError::UnknownError); + } + + if (pDatabase) + { + pDatabase->close(); + } + if (pDatabase && !dbKey.isEmpty()) + { + QSqlDatabase::removeDatabase(dbKey); + } + return daoError; + } + + } // namespace detail + } // namespace dao +} // namespace qx + +// Not really implemented, this is just to avoid a compilation error with Qt6 and _QX_UNITY_BUILD compilation option (?) +QDataStream &operator<<(QDataStream &stream, const qx::dao::detail::QxDaoAsyncParams &t) +{ + Q_UNUSED(t); + return stream; +} +QDataStream &operator>>(QDataStream &stream, qx::dao::detail::QxDaoAsyncParams &t) +{ + Q_UNUSED(t); + return stream; +} diff --git a/src/QxDao/QxDateNeutral.cpp b/src/QxDao/QxDateNeutral.cpp new file mode 100644 index 0000000..153542a --- /dev/null +++ b/src/QxDao/QxDateNeutral.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +QDataStream &operator<<(QDataStream &stream, const qx::QxDateNeutral &t) +{ + stream << t.m_date; + stream << t.m_neutral; + return stream; +} + +QDataStream &operator>>(QDataStream &stream, qx::QxDateNeutral &t) +{ + stream >> t.m_date; + stream >> t.m_neutral; + return stream; +} diff --git a/src/QxDao/QxDateTimeNeutral.cpp b/src/QxDao/QxDateTimeNeutral.cpp new file mode 100644 index 0000000..8607d09 --- /dev/null +++ b/src/QxDao/QxDateTimeNeutral.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +QDataStream &operator<<(QDataStream &stream, const qx::QxDateTimeNeutral &t) +{ + stream << t.m_dt; + stream << t.m_neutral; + return stream; +} + +QDataStream &operator>>(QDataStream &stream, qx::QxDateTimeNeutral &t) +{ + stream >> t.m_dt; + stream >> t.m_neutral; + return stream; +} diff --git a/src/QxDao/QxMongoDB/QxMongoDB_Helper.cpp b/src/QxDao/QxMongoDB/QxMongoDB_Helper.cpp new file mode 100644 index 0000000..072ca5a --- /dev/null +++ b/src/QxDao/QxMongoDB/QxMongoDB_Helper.cpp @@ -0,0 +1,1851 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_MONGODB + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" +#endif + +#include +#include + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) +#include +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) +#include +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::dao::mongodb::QxMongoDB_Helper) + +namespace qx +{ + namespace dao + { + namespace mongodb + { + + struct qx_engine + { + ; + }; + struct qx_reply + { + ; + }; + struct qx_opts + { + ; + }; + + template + struct qx_scoped_wrapper + { + ; + }; + + template <> + struct qx_scoped_wrapper + { + qx_scoped_wrapper() { mongoc_init(); } + ~qx_scoped_wrapper() { mongoc_cleanup(); } + }; + + template <> + struct qx_scoped_wrapper + { + mongoc_uri_t *m_uri; + qx_scoped_wrapper(const std::string &uri) : m_uri(NULL) { m_uri = mongoc_uri_new(uri.c_str()); } + ~qx_scoped_wrapper() + { + if (m_uri) + { + mongoc_uri_destroy(m_uri); + } + } + mongoc_uri_t *get() { return m_uri; } + }; + + template <> + struct qx_scoped_wrapper + { + mongoc_client_pool_t *m_pool; + qx_scoped_wrapper(mongoc_uri_t *uri) : m_pool(NULL) + { + if (uri) + { + m_pool = mongoc_client_pool_new(uri); + }; + } + ~qx_scoped_wrapper() + { + if (m_pool) + { + mongoc_client_pool_destroy(m_pool); + } + } + mongoc_client_pool_t *get() { return m_pool; } + }; + + template <> + struct qx_scoped_wrapper + { + mongoc_client_t *m_client; + mongoc_client_pool_t *m_pool; + qx_scoped_wrapper(mongoc_client_pool_t *pool) : m_client(NULL), m_pool(pool) + { + if (m_pool) + { + m_client = mongoc_client_pool_pop(m_pool); + }; + } + ~qx_scoped_wrapper() + { + if (m_pool && m_client) + { + mongoc_client_pool_push(m_pool, m_client); + } + } + mongoc_client_t *get() { return m_client; } + }; + + template <> + struct qx_scoped_wrapper + { + mongoc_database_t *m_db; + qx_scoped_wrapper(mongoc_client_t *client, const std::string &name) : m_db(NULL) + { + if (client) + { + m_db = mongoc_client_get_database(client, name.c_str()); + }; + } + ~qx_scoped_wrapper() + { + if (m_db) + { + mongoc_database_destroy(m_db); + } + } + mongoc_database_t *get() { return m_db; } + }; + + template <> + struct qx_scoped_wrapper + { + mongoc_collection_t *m_collection; + qx_scoped_wrapper(mongoc_client_t *client, const std::string &db, const std::string &name) : m_collection(NULL) + { + if (client) + { + m_collection = mongoc_client_get_collection(client, db.c_str(), name.c_str()); + }; + } + ~qx_scoped_wrapper() + { + if (m_collection) + { + mongoc_collection_destroy(m_collection); + } + } + mongoc_collection_t *get() { return m_collection; } + }; + + template <> + struct qx_scoped_wrapper + { + mongoc_cursor_t *m_cursor; + qx_scoped_wrapper(mongoc_cursor_t *cursor) : m_cursor(cursor) { ; } + ~qx_scoped_wrapper() + { + if (m_cursor) + { + mongoc_cursor_destroy(m_cursor); + } + } + mongoc_cursor_t *get() { return m_cursor; } + }; + + template <> + struct qx_scoped_wrapper + { + bson_t *m_doc; + bson_error_t m_err; + qx_scoped_wrapper(bson_t *doc) : m_doc(doc) { ; } + qx_scoped_wrapper(const std::string &json) : m_doc(NULL) + { + if (json.empty()) + { + m_doc = bson_new(); + } + else + { + m_doc = bson_new_from_json((const uint8_t *)(json.c_str()), -1, (&m_err)); + } + } + ~qx_scoped_wrapper() + { + if (m_doc) + { + bson_destroy(m_doc); + } + } + bson_error_t *err() { return (&m_err); } + bson_t *get() { return m_doc; } + }; + + template <> + struct qx_scoped_wrapper> + { + bson_error_t m_err; + std::vector m_docs; + qx_scoped_wrapper>(int size) { m_docs.reserve(size); } + ~qx_scoped_wrapper>() + { + for (bson_t *doc : m_docs) + { + if (doc) + { + bson_destroy(doc); + } + } + } + bool append(const std::string &json) + { + if (!json.empty()) + { + bson_t *doc = bson_new_from_json((const uint8_t *)(json.c_str()), -1, (&m_err)); + if (doc) + { + m_docs.push_back(doc); + return true; + } + }; + return false; + } + std::vector *get() { return (&m_docs); } + bson_error_t *err() { return (&m_err); } + }; + + template <> + struct qx_scoped_wrapper + { + mongoc_bulk_operation_t *m_bulk; + qx_scoped_wrapper(mongoc_collection_t *coll, const bson_t *opts) : m_bulk(NULL) + { + if (coll) + { + m_bulk = mongoc_collection_create_bulk_operation_with_opts(coll, opts); + } + } + ~qx_scoped_wrapper() + { + if (m_bulk) + { + mongoc_bulk_operation_destroy(m_bulk); + } + } + mongoc_bulk_operation_t *get() { return m_bulk; } + }; + + template <> + struct qx_scoped_wrapper + { + bson_t *m_opts; + bson_error_t m_err; + qx_scoped_wrapper(const std::string &json) : m_opts(NULL) + { + if (!json.empty()) + { + m_opts = bson_new_from_json((const uint8_t *)(json.c_str()), -1, (&m_err)); + } + } + ~qx_scoped_wrapper() + { + if (m_opts) + { + bson_destroy(m_opts); + } + } + const bson_t *get() const { return const_cast(m_opts); } + bson_error_t *err() { return (&m_err); } + }; + typedef std::unique_ptr> qx_opts_ptr; + + template <> + struct qx_scoped_wrapper + { + bson_t m_reply; + bool m_destroy; + QString m_json; + QVariantMap m_values; + qx_scoped_wrapper() : m_destroy(true) { ; } + ~qx_scoped_wrapper() + { + if (m_destroy) + { + bson_destroy(&m_reply); + } + } + QString toString() + { + if (!m_json.isEmpty()) + { + return m_json; + }; + char *s = bson_as_relaxed_extended_json((&m_reply), NULL); + m_json = QString(s); + bson_free(s); + return m_json; + } + QVariantMap &values() + { + if (m_values.isEmpty()) + { + QString txt = toString(); + qx::serialization::json::from_string(m_values, txt); + }; + return m_values; + } + QVariant value(const QString &key) { return (values().contains(key) ? values().value(key) : QVariant()); } + void log() + { + QString txt = toString(); + qDebug("[QxOrm] MongoDB database reply : %s", qPrintable(txt)); + } + bson_t *get() { return (&m_reply); } + }; + typedef std::unique_ptr> qx_reply_ptr; + + struct qx_db_collection + { + std::shared_ptr> client; + std::shared_ptr> db; + std::shared_ptr> collection; + }; + + struct Q_DECL_HIDDEN QxMongoDB_Helper::QxMongoDB_HelperImpl + { + + QMutex m_oDbMutex; //!< Mutex => 'QxMongoDB_Helper' is thread-safe + std::unique_ptr> m_pInstance; //!< MongoDB database driver instance + std::unique_ptr> m_pUri; //!< MongoDB database uri (connection string) + std::unique_ptr> m_pPool; //!< MongoDB database pool to get client connections + QHash m_lstOpts; //!< MongoDB database options per action (based on QxMongoDB_Helper::opts enum) + bool m_bLogDatabaseReply; //!< Log MongoDB database replies + bool m_bLogDatabaseInfo; //!< Log MongoDB database info (ping, server name, etc...) + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) + QRegularExpression m_regExpCheckNumber; //!< Regular expression to check if a string contains only numbers +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) + QxMongoDB_HelperImpl() : m_bLogDatabaseReply(false), m_bLogDatabaseInfo(true) + { + m_pInstance.reset(new qx_scoped_wrapper()); + m_regExpCheckNumber = QRegularExpression(QRegularExpression::anchoredPattern("\\d*")); + } +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) + QxMongoDB_HelperImpl() : m_bLogDatabaseReply(false), m_bLogDatabaseInfo(true) { m_pInstance.reset(new qx_scoped_wrapper()); } +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) + ~QxMongoDB_HelperImpl() + { + m_pPool.reset(); + m_pUri.reset(); + m_pInstance.reset(); + } + + QSqlError insertOne_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QString &json, QString &insertedId); + QSqlError insertMany_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QStringList &json, QStringList &insertedId); + QSqlError updateOne_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QString &json, const qx::QxSqlQuery *query); + QSqlError updateMany_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QStringList &json, const qx::QxSqlQuery *query); + QSqlError deleteOne_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QString &json, const qx::QxSqlQuery *query); + QSqlError deleteMany_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QStringList &json, const qx::QxSqlQuery *query); + QSqlError findOne_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, QString &json, const qx::QxSqlQuery *query); + QSqlError findMany_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, QStringList &json, const qx::QxSqlQuery *query, QxMongoDB_Fetcher *pFetcher); + QSqlError aggregate_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, QStringList &json, const qx::QxSqlQuery *query, const QString &lookup, QxMongoDB_Fetcher *pFetcher, bool bModeDelete = false); + QSqlError count_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, long &cnt, const qx::QxSqlQuery *query); + QSqlError executeCommand_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, qx::QxSqlQuery *query); + + QSqlError initPool() + { + { + if (m_pPool) + { + return QSqlError(); + } + QMutexLocker locker(&m_oDbMutex); + if (m_pPool) + { + return QSqlError(); + } + + QString uri; + qx::QxSqlDatabase *db = qx::QxSqlDatabase::getSingleton(); + int port = db->getPort(); + if (port == -1) + { + port = 27017; + } + if (!db->getUserName().isEmpty()) + { + uri += db->getUserName(); + } + if (!db->getUserName().isEmpty() && !db->getPassword().isEmpty()) + { + uri += ":" + db->getPassword() + "@"; + } + if (!db->getHostName().isEmpty()) + { + uri += db->getHostName() + ":" + QString::number(port); + } + if (!db->getConnectOptions().isEmpty()) + { + uri += "/?" + db->getConnectOptions(); + } + if (!uri.isEmpty()) + { + uri = "mongodb://" + uri; + } + if (uri.isEmpty()) + { + return QSqlError("[QxOrm] Please define a connection string to a MongoDB database through 'qx::QxSqlDatabase::getSingleton()' settings", "", QSqlError::UnknownError); + } + if (m_bLogDatabaseInfo) + { + qDebug("[QxOrm] Connect to MongoDB database : %s", qPrintable(uri)); + } + + std::string uri_ = toStdString(uri); + m_pUri.reset(new qx_scoped_wrapper(uri_)); + if (!m_pUri->get()) + { + m_pUri.reset(); + return QSqlError("[QxOrm] Unable to create a 'mongoc_uri_t' instance to connect to MongoDB database (" + uri + ")", "", QSqlError::UnknownError); + } + m_pPool.reset(new qx_scoped_wrapper(m_pUri->get())); + if (!m_pPool->get()) + { + m_pPool.reset(); + m_pUri.reset(); + return QSqlError("[QxOrm] Unable to create a 'mongoc_client_pool_t' instance to connect to MongoDB database (" + uri + ")", "", QSqlError::UnknownError); + } + } + +#ifdef Q_COMPILER_INITIALIZER_LISTS + qx_query ping{{"ping", 1}}; +#else // Q_COMPILER_INITIALIZER_LISTS + qx_query ping("{ \"ping\": 1 }"); +#endif // Q_COMPILER_INITIALIZER_LISTS + QSqlError err = executeCommand_(NULL, NULL, (&ping)); + if (err.isValid()) + { + m_pPool.reset(); + m_pUri.reset(); + return err; + } + else + { + if (m_bLogDatabaseInfo) + { + qDebug("[QxOrm] Ping to MongoDB database server : %s", "OK"); + } + } + +#ifdef Q_COMPILER_INITIALIZER_LISTS + qx_query buildInfo{{"buildInfo", 1}}; +#else // Q_COMPILER_INITIALIZER_LISTS + qx_query buildInfo("{ \"buildInfo\": 1 }"); +#endif // Q_COMPILER_INITIALIZER_LISTS + err = executeCommand_(NULL, NULL, (&buildInfo)); + if (err.isValid()) + { + m_pPool.reset(); + m_pUri.reset(); + return err; + } + else + { + if (m_bLogDatabaseInfo) + { + QString resp = buildInfo.response().toString(); + qDebug("[QxOrm] Fetch build info from MongoDB database server : \n%s", qPrintable(resp)); + } + } + + return QSqlError(); + } + + QSqlError initCollection(qx::IxClass *pClass, qx_db_collection &coll) + { + if (!pClass) + { + return QSqlError("[QxOrm] Parameter 'qx::IxClass' is required to get MongoDB database collection name", "", QSqlError::UnknownError); + } + QSqlError err = initDatabase(coll); + if (err.isValid()) + { + return err; + } + std::string dbName = toStdString(qx::QxSqlDatabase::getSingleton()->getDatabaseName()); + coll.collection = std::make_shared>(coll.client->get(), dbName, toStdString(pClass->getName())); + if (!coll.collection->get()) + { + return QSqlError("[QxOrm] Unable to create a 'mongoc_collection_t' instance from MongoDB database (" + qx::QxSqlDatabase::getSingleton()->getDatabaseName() + " - " + pClass->getName() + ")", "", QSqlError::UnknownError); + } + return QSqlError(); + } + + QSqlError initDatabase(qx_db_collection &coll) + { + QSqlError err = initPool(); + if (err.isValid()) + { + return err; + } + coll.client = std::make_shared>(m_pPool->get()); + if (!coll.client->get()) + { + return QSqlError("[QxOrm] Unable to get a MongoDB client connection from pool of type 'mongoc_client_t'", "", QSqlError::UnknownError); + } + std::string dbName = toStdString(qx::QxSqlDatabase::getSingleton()->getDatabaseName()); + if (dbName.empty()) + { + return QSqlError("[QxOrm] Unable to connect to MongoDB database : please define a database name using qx::QxSqlDatabase::getSingleton()->setDatabaseName()", "", QSqlError::UnknownError); + } + coll.db = std::make_shared>(coll.client->get(), dbName); + if (!coll.db->get()) + { + return QSqlError("[QxOrm] Unable to create a 'mongoc_database_t' instance from MongoDB database (" + qx::QxSqlDatabase::getSingleton()->getDatabaseName() + ")", "", QSqlError::UnknownError); + } + return QSqlError(); + } + + std::string toStdString(const QString &s) const + { +#ifndef QT_NO_STL + return s.toStdString(); +#else // QT_NO_STL + return s.toLatin1().constData(); +#endif // QT_NO_STL + } + + QString asJson(const QString &s) const + { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) + QString json = s; + bool isNumber = m_regExpCheckNumber.match(s).hasMatch(); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) + QString json = s; + QRegExp regexp("\\d*"); + bool isNumber = regexp.exactMatch(s); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) + if (s.startsWith("qx_oid:")) + { + return "{ \"$oid\": \"" + s.right(s.size() - 7) + "\" }"; + } + return ((isNumber ? QString() : QString("\"")) + json.replace("\"", "\\\"") + (isNumber ? QString() : QString("\""))); + } + + QSqlError checkInsertOID(bson_t *doc, qx::IxClass *pClass, QString &insertedId) + { + bson_iter_t itr; + if (!bson_iter_init_find((&itr), doc, "_id")) + { + bson_oid_t oid; + bson_oid_init((&oid), NULL); + if (!bson_append_oid(doc, "_id", -1, (&oid))) + { + return QSqlError("[QxOrm] Unable to append 'bson_oid_t' to document (" + pClass->getName() + ")", "", QSqlError::UnknownError); + } + char str[25]; + bson_oid_to_string((&oid), str); + insertedId = "qx_oid:" + QString(str); + } + return QSqlError(); + } + + bool setOptions_(QxMongoDB_Helper::opts e, const QString &optsAsJson) + { + int i = static_cast(e); + if (optsAsJson.isEmpty()) + { + m_lstOpts.remove(i); + return true; + } + m_lstOpts.insert(i, optsAsJson); + return true; + } + + QSqlError getOptions_(QxMongoDB_Helper::opts e, qx_opts_ptr &opts, const QString &customOpts = QString()) + { + opts.reset(); + int i = static_cast(e); + QString json = (customOpts.isEmpty() ? m_lstOpts.value(i) : customOpts); + if (json.isEmpty()) + { + return QSqlError(); + } + opts.reset(new qx_scoped_wrapper(toStdString(json))); + if (!opts->get()) + { + QString msg(opts->err()->message); + opts.reset(); + return QSqlError("[QxOrm] Unable to create a 'bson_t' options instance (" + QString::number(i) + " - " + json + ") from JSON : " + msg, "", QSqlError::UnknownError); + } + return QSqlError(); + } + + QString buildLookup(qx::dao::detail::IxDao_Helper *pDaoHelper, bool idAsArray, qx::QxSqlRelationLinked *pSubHierarchy = NULL, const QString &sTab = QString("\t"), QStringList *pSubFieldsAs = NULL) + { + qx::QxSqlRelationLinked *pHierarchy = (pSubHierarchy ? pSubHierarchy : (pDaoHelper ? pDaoHelper->getSqlRelationLinked() : NULL)); + if (!pHierarchy) + { + return QString(); + } + QString templFrom = "\n\t" + sTab + "\"from\": \"%COLL_TARGET%\""; + QString templLet = "\n\t" + sTab + "\"let\": { \"%COLL_LET_KEY%\": \"$%COLL_LET_VAL%\" }"; + QString templPipeline = "\n\t" + sTab + "\"pipeline\": [ { \"$match\": { \"$expr\": { \"$" + (idAsArray ? QString("in") : QString("eq")) + "\": [ \"$%COLL_EQ%\", \"$$%COLL_LET_KEY%\" ] } } } %NEXT_LEVEL_LOOKUP% %PROJECTION% ]"; + QString templAs = "\n\t" + sTab + "\"as\": \"%COLL_AS%\""; + QString templOneToMany = "\n" + sTab + "{ \"$lookup\": { " + templFrom + ", " + templLet + ", " + templPipeline + ", " + templAs + " } }"; + QString templManyToOne = templOneToMany + ",\n" + sTab + "{ \"$addFields\": { \"%COLL_AS%\": { \"$arrayElemAt\": [ \"$%COLL_AS%\", 0 ] } } }"; + QString templOneToOne = templManyToOne; + qx::QxSqlRelationLinked::type_lst_relation_linked pRelationsLinked = pHierarchy->getRelationLinkedX(); + qx::QxSqlRelationLinked::type_lst_relation pRelations = pHierarchy->getRelationX(); + QString result; + QStringList projectionFields; + QStringList subProjectionFields; + + for (auto itr = pRelations.begin(); itr != pRelations.end(); ++itr) + { + const QxSqlRelationLinked::type_relation &item = itr->second; + qx::IxSqlRelation *pRelation = std::get<1>(item); + if (!pRelation) + { + continue; + } + qx::IxSqlRelation::relation_type type = pRelation->getRelationType(); + if ((type != qx::IxSqlRelation::many_to_one) && (type != qx::IxSqlRelation::one_to_many) && (type != qx::IxSqlRelation::one_to_one)) + { + continue; + } + qx::IxDataMember *pDataMember = pRelation->getDataMember(); + if (!pDataMember) + { + continue; + } + qx::IxDataMember *pFromId = pRelation->getDataIdOwner(); + if (!pFromId) + { + continue; + } + qx::IxDataMember *pTargetId = pRelation->getDataId(); + if (!pTargetId) + { + continue; + } + qx::IxClass *pFrom = pRelation->getClassOwner(); + if (!pFrom) + { + continue; + } + qx::IxClass *pTarget = pRelation->getClass(); + if (!pTarget) + { + continue; + } + qx::QxSqlRelationLinked::type_ptr pRelationLinked = pRelationsLinked.value(pRelation->getKey()); + QString subLookup = (pRelationLinked ? buildLookup(pDaoHelper, false, pRelationLinked.get(), (sTab + "\t"), (&subProjectionFields)) : QString()); + if (!subLookup.isEmpty()) + { + subLookup = ", " + subLookup; + } + + QString lookup = ((type == qx::IxSqlRelation::many_to_one) ? templManyToOne : ((type == qx::IxSqlRelation::one_to_many) ? templOneToMany : templOneToOne)); + lookup.replace("%COLL_TARGET%", pTarget->getName()); + lookup.replace("%NEXT_LEVEL_LOOKUP%", subLookup); + lookup.replace("%COLL_AS%", pDataMember->getKey()); + if (pSubFieldsAs) + { + (*pSubFieldsAs) << pDataMember->getKey(); + } + else + { + projectionFields << pDataMember->getKey(); + } + + if (type == qx::IxSqlRelation::many_to_one) + { + qx::IxDataMember *pLinkRelationKey = pRelation->getLinkRelationKey(); + lookup.replace("%COLL_LET_KEY%", pDataMember->getKey()); + lookup.replace("%COLL_LET_VAL%", (pLinkRelationKey ? pLinkRelationKey->getKey() : pDataMember->getKey())); + lookup.replace("%COLL_EQ%", "_id"); + } + else if (type == qx::IxSqlRelation::one_to_many) + { + lookup.replace("%COLL_LET_KEY%", pRelation->getForeignKey()); + lookup.replace("%COLL_LET_VAL%", "_id"); + lookup.replace("%COLL_EQ%", pRelation->getForeignKey()); + } + else if (type == qx::IxSqlRelation::one_to_one) + { + qx::IxDataMember *pLinkRelationKey = pRelation->getLinkRelationKey(); + lookup.replace("%COLL_LET_KEY%", pDataMember->getKey()); + lookup.replace("%COLL_LET_VAL%", (pLinkRelationKey ? pLinkRelationKey->getKey() : pDataMember->getKey())); + lookup.replace("%COLL_EQ%", "_id"); + } + + QString projection; + QSet columns = std::get<2>(item).first; + if (columns.count() > 0) + { + long l = 0; + qx::IxDataMember *p = NULL; + while ((p = pRelation->nextData(l))) + { + if (p && columns.contains(p->getKey())) + { + projection += ((projection.isEmpty() ? QString("\"") : QString(", \"")) + p->getKey() + "\": 1"); + } + } + if (!projection.isEmpty()) + { + Q_FOREACH (QString s, subProjectionFields) + { + projection += ((projection.isEmpty() ? QString("\"") : QString(", \"")) + s + "\": 1"); + } + } + if (!projection.isEmpty()) + { + projection = ",\n\t" + sTab + "{ \"$project\": { " + projection + " } }"; + } + } + lookup.replace("%PROJECTION%", projection); + + result += ((result.isEmpty() ? QString() : QString(", ")) + lookup); + } + + QString project; + if (pDaoHelper && pHierarchy && !pSubHierarchy && !result.isEmpty()) + { + long l = 0; + qx::IxDataMember *p = NULL; + bool bIncludeAllFields = true; + while ((p = pDaoHelper->builder().nextData(l))) + { + if (p && pHierarchy->checkRootColumns(p->getKey())) + { + project += ((project.isEmpty() ? QString("\"") : QString(", \"")) + p->getKey() + "\": 1"); + } + else + { + bIncludeAllFields = false; + } + } + if (!project.isEmpty()) + { + Q_FOREACH (QString s, projectionFields) + { + project += ((project.isEmpty() ? QString("\"") : QString(", \"")) + s + "\": 1"); + } + } + if (!project.isEmpty()) + { + project = ",\n" + sTab + "{ \"$project\": { " + project + " } }"; + } + if (bIncludeAllFields) + { + project = ""; + } + } + + return (result + project); + } + }; + + QxMongoDB_Fetcher::QxMongoDB_Fetcher() { ; } + + QxMongoDB_Fetcher::~QxMongoDB_Fetcher() { ; } + + QxMongoDB_Helper::QxMongoDB_Helper() : QxSingleton("qx::dao::mongodb::QxMongoDB_Helper"), m_pImpl(new QxMongoDB_HelperImpl()) { ; } + + QxMongoDB_Helper::~QxMongoDB_Helper() { ; } + + QSqlError QxMongoDB_Helper::insertOne(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QString &json, QString &insertedId) + { + qx::dao::detail::IxDao_Timer timer(pDaoHelper, qx::dao::detail::IxDao_Helper::timer_db_exec); + QxMongoDB_Helper *pSingleton = QxMongoDB_Helper::getSingleton(); + qAssert(pSingleton != NULL); + QSqlError err = pSingleton->m_pImpl->insertOne_(pDaoHelper, pClass, json, insertedId); + return (pDaoHelper ? pDaoHelper->updateError(err) : err); + } + + QSqlError QxMongoDB_Helper::QxMongoDB_HelperImpl::insertOne_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QString &json, QString &insertedId) + { + if (json.count() <= 0) + { + return QSqlError(); + } + qx_db_collection coll; + QSqlError err = initCollection(pClass, coll); + if (err.isValid()) + { + return err; + } + if (pDaoHelper) + { + pDaoHelper->qxQuery().queryAt(0, "MongoDB insert one '" + (pClass ? pClass->getName() : QString()) + "' :\n" + json); + } + + qx_scoped_wrapper doc(toStdString(json)); + if (!doc.get()) + { + return QSqlError("[QxOrm] Unable to create a 'bson_t' document instance (" + pClass->getName() + ") from JSON : " + QString(doc.err()->message), "", QSqlError::UnknownError); + } + err = checkInsertOID(doc.get(), pClass, insertedId); + if (err.isValid()) + { + return err; + } + + bson_error_t bsonError; + qx_opts_ptr opts; + qx_reply_ptr reply; + if (m_bLogDatabaseReply) + { + reply.reset(new qx_scoped_wrapper()); + } + err = getOptions_(QxMongoDB_Helper::opts_collection_insert_one, opts); + if (err.isValid()) + { + return err; + } + bool ok = mongoc_collection_insert_one(coll.collection->get(), doc.get(), (opts ? opts->get() : NULL), (reply ? reply->get() : NULL), (&bsonError)); + if (!ok) + { + return QSqlError("[QxOrm] Unable to insert one 'bson_t' document (" + pClass->getName() + ") to MongoDB database : " + QString(bsonError.message), "", QSqlError::UnknownError); + } + if (reply) + { + reply->log(); + } + return QSqlError(); + } + + QSqlError QxMongoDB_Helper::insertMany(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QStringList &json, QStringList &insertedId) + { + qx::dao::detail::IxDao_Timer timer(pDaoHelper, qx::dao::detail::IxDao_Helper::timer_db_exec); + QxMongoDB_Helper *pSingleton = QxMongoDB_Helper::getSingleton(); + qAssert(pSingleton != NULL); + QSqlError err = pSingleton->m_pImpl->insertMany_(pDaoHelper, pClass, json, insertedId); + return (pDaoHelper ? pDaoHelper->updateError(err) : err); + } + + QSqlError QxMongoDB_Helper::QxMongoDB_HelperImpl::insertMany_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QStringList &json, QStringList &insertedId) + { + if (json.count() <= 0) + { + return QSqlError(); + } + qx_db_collection coll; + QSqlError err = initCollection(pClass, coll); + if (err.isValid()) + { + return err; + } + if (pDaoHelper) + { + pDaoHelper->qxQuery().queryAt(0, "MongoDB insert many '" + (pClass ? pClass->getName() : QString()) + "' :\n" + json.join(", \n")); + } + insertedId.clear(); + insertedId.reserve(json.count()); + + qx_scoped_wrapper> docs(json.count()); + Q_FOREACH (QString s, json) + { + if (s.isEmpty()) + { + continue; + } + if (!docs.append(toStdString(s))) + { + return QSqlError("[QxOrm] Unable to create a 'bson_t' document instance (" + pClass->getName() + ") from JSON : " + QString(docs.err()->message), "", QSqlError::UnknownError); + } + QString oid; + err = checkInsertOID(docs.m_docs.back(), pClass, oid); + if (err.isValid()) + { + return err; + }; + insertedId.append(oid); + } + + bson_error_t bsonError; + qx_opts_ptr opts; + qx_reply_ptr reply; + const bson_t **docs_ = const_cast(docs.get()->data()); + if (m_bLogDatabaseReply) + { + reply.reset(new qx_scoped_wrapper()); + } + err = getOptions_(QxMongoDB_Helper::opts_collection_insert_many, opts); + if (err.isValid()) + { + return err; + } + bool ok = mongoc_collection_insert_many(coll.collection->get(), docs_, docs.get()->size(), (opts ? opts->get() : NULL), (reply ? reply->get() : NULL), (&bsonError)); + if (!ok) + { + return QSqlError("[QxOrm] Unable to insert many 'bson_t' documents (" + pClass->getName() + ") to MongoDB database : " + QString(bsonError.message), "", QSqlError::UnknownError); + } + if (reply) + { + reply->log(); + } + return QSqlError(); + } + + QSqlError QxMongoDB_Helper::updateOne(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QString &json, const qx::QxSqlQuery *query /* = NULL */) + { + qx::dao::detail::IxDao_Timer timer(pDaoHelper, qx::dao::detail::IxDao_Helper::timer_db_exec); + QxMongoDB_Helper *pSingleton = QxMongoDB_Helper::getSingleton(); + qAssert(pSingleton != NULL); + QSqlError err = pSingleton->m_pImpl->updateOne_(pDaoHelper, pClass, json, query); + return (pDaoHelper ? pDaoHelper->updateError(err) : err); + } + + QSqlError QxMongoDB_Helper::QxMongoDB_HelperImpl::updateOne_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QString &json, const qx::QxSqlQuery *query) + { + if (json.count() <= 0) + { + return QSqlError(); + } + qx_db_collection coll; + QSqlError err = initCollection(pClass, coll); + if (err.isValid()) + { + return err; + } + if (pDaoHelper) + { + pDaoHelper->qxQuery().queryAt(0, "MongoDB update one '" + (pClass ? pClass->getName() : QString()) + "' :\n" + json + "\n" + (query ? query->queryAt(0) : QString())); + } + + qx_scoped_wrapper doc(toStdString(json)); + if (!doc.get()) + { + return QSqlError("[QxOrm] Unable to create a 'bson_t' document instance (" + pClass->getName() + ") from JSON : " + QString(doc.err()->message), "", QSqlError::UnknownError); + } + + qx_scoped_wrapper query_(toStdString((query ? query->queryAt(0) : QString()))); + if (!query_.get()) + { + return QSqlError("[QxOrm] Unable to create a 'bson_t' document/query instance (" + pClass->getName() + ") from JSON : " + QString(query_.err()->message), "", QSqlError::UnknownError); + } + if (!query || query->queryAt(0).isEmpty()) + { + bson_iter_t itr; + const bson_value_t *oid = (bson_iter_init_find((&itr), doc.get(), "_id") ? bson_iter_value(&itr) : NULL); + if (!oid) + { + return QSqlError("[QxOrm] Unable to find/fetch document oid '_id' (" + pClass->getName() + ")", "", QSqlError::UnknownError); + } + if (!bson_append_value(query_.get(), "_id", -1, oid)) + { + return QSqlError("[QxOrm] Unable to append 'bson_value_t' oid value to document (" + pClass->getName() + ")", "", QSqlError::UnknownError); + } + } + + qx_scoped_wrapper update_(""); + const bson_t *doc_ = const_cast(doc.get()); + if (!bson_append_document(update_.get(), "$set", -1, doc_)) + { + return QSqlError("[QxOrm] Unable to append 'bson_t' document for update query (" + pClass->getName() + ")", "", QSqlError::UnknownError); + } + + bson_error_t bsonError; + qx_opts_ptr opts; + qx_reply_ptr reply; + reply.reset(new qx_scoped_wrapper()); + err = getOptions_(QxMongoDB_Helper::opts_collection_update_one, opts, (query ? query->queryAt(1) : QString())); + if (err.isValid()) + { + return err; + } + bool ok = mongoc_collection_update_one(coll.collection->get(), query_.get(), update_.get(), (opts ? opts->get() : NULL), (reply ? reply->get() : NULL), (&bsonError)); + if (!ok) + { + return QSqlError("[QxOrm] Unable to update one 'bson_t' document (" + pClass->getName() + ") to MongoDB database : " + QString(bsonError.message), "", QSqlError::UnknownError); + } + if (reply && (reply->value("matchedCount").toInt() < 1)) + { + return QSqlError("[QxOrm] Nothing has been updated (" + pClass->getName() + ") in MongoDB database", "", QSqlError::UnknownError); + } + if (reply && m_bLogDatabaseReply) + { + reply->log(); + } + return QSqlError(); + } + + QSqlError QxMongoDB_Helper::updateMany(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QStringList &json, const qx::QxSqlQuery *query /* = NULL */) + { + qx::dao::detail::IxDao_Timer timer(pDaoHelper, qx::dao::detail::IxDao_Helper::timer_db_exec); + QxMongoDB_Helper *pSingleton = QxMongoDB_Helper::getSingleton(); + qAssert(pSingleton != NULL); + QSqlError err = pSingleton->m_pImpl->updateMany_(pDaoHelper, pClass, json, query); + return (pDaoHelper ? pDaoHelper->updateError(err) : err); + } + + QSqlError QxMongoDB_Helper::QxMongoDB_HelperImpl::updateMany_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QStringList &json, const qx::QxSqlQuery *query) + { + if (json.count() <= 0) + { + return QSqlError(); + } + qx_db_collection coll; + QSqlError err = initCollection(pClass, coll); + if (err.isValid()) + { + return err; + } + if (pDaoHelper) + { + pDaoHelper->qxQuery().queryAt(0, "MongoDB update many '" + (pClass ? pClass->getName() : QString()) + "' :\n" + json.join(", \n") + "\n" + (query ? query->queryAt(0) : QString())); + } + + qx_opts_ptr optsBulk; + err = getOptions_(QxMongoDB_Helper::opts_collection_create_bulk_operation, optsBulk); + if (err.isValid()) + { + return err; + } + qx_scoped_wrapper bulk(coll.collection->get(), (optsBulk ? optsBulk->get() : NULL)); + if (!bulk.get()) + { + return QSqlError("[QxOrm] Unable to create 'mongoc_bulk_operation_t' bulk operation instance (" + pClass->getName() + ")", "", QSqlError::UnknownError); + } + qx_opts_ptr opts; + err = getOptions_(QxMongoDB_Helper::opts_bulk_operation_update_one, opts, (query ? query->queryAt(1) : QString())); + if (err.isValid()) + { + return err; + } + int iUpdateCount = 0; + + Q_FOREACH (QString s, json) + { + if (s.isEmpty()) + { + continue; + } + qx_scoped_wrapper doc(toStdString(s)); + if (!doc.get()) + { + return QSqlError("[QxOrm] Unable to create a 'bson_t' document instance (" + pClass->getName() + ") from JSON : " + QString(doc.err()->message), "", QSqlError::UnknownError); + } + + qx_scoped_wrapper query_(toStdString((query ? query->queryAt(0) : QString()))); + if (!query_.get()) + { + return QSqlError("[QxOrm] Unable to create a 'bson_t' document/query instance (" + pClass->getName() + ") from JSON : " + QString(query_.err()->message), "", QSqlError::UnknownError); + } + if (!query || query->queryAt(0).isEmpty()) + { + bson_iter_t itr; + const bson_value_t *oid = (bson_iter_init_find((&itr), doc.get(), "_id") ? bson_iter_value(&itr) : NULL); + if (!oid) + { + return QSqlError("[QxOrm] Unable to find/fetch document oid '_id' (" + pClass->getName() + ")", "", QSqlError::UnknownError); + } + if (!bson_append_value(query_.get(), "_id", -1, oid)) + { + return QSqlError("[QxOrm] Unable to append 'bson_value_t' oid value to document (" + pClass->getName() + ")", "", QSqlError::UnknownError); + } + } + + qx_scoped_wrapper update_(""); + const bson_t *doc_ = const_cast(doc.get()); + if (!bson_append_document(update_.get(), "$set", -1, doc_)) + { + return QSqlError("[QxOrm] Unable to append 'bson_t' document for update query (" + pClass->getName() + ")", "", QSqlError::UnknownError); + } + + bson_error_t bsonErr; + if (!mongoc_bulk_operation_update_one_with_opts(bulk.get(), query_.get(), update_.get(), (opts ? opts->get() : NULL), (&bsonErr))) + { + return QSqlError("[QxOrm] Unable to append update bulk operation (" + pClass->getName() + ") : " + QString(bsonErr.message), "", QSqlError::UnknownError); + } + iUpdateCount++; + } + + bson_error_t bsonError; + qx_reply_ptr reply; + reply.reset(new qx_scoped_wrapper()); + bool ok = mongoc_bulk_operation_execute(bulk.get(), reply->get(), (&bsonError)); + if (!ok) + { + return QSqlError("[QxOrm] Unable to update many 'bson_t' documents (" + pClass->getName() + ") to MongoDB database (bulk operation) : " + QString(bsonError.message), "", QSqlError::UnknownError); + } + if (reply && (reply->value("nMatched").toInt() != iUpdateCount)) + { + return QSqlError("[QxOrm] Not everything has been updated (" + pClass->getName() + ") in MongoDB database (result:'" + QString::number(reply->value("nMatched").toInt()) + "', expected:'" + QString::number(iUpdateCount) + "')", "", QSqlError::UnknownError); + } + if (m_bLogDatabaseReply && reply) + { + reply->log(); + } + return QSqlError(); + } + + QSqlError QxMongoDB_Helper::deleteOne(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QString &json, const qx::QxSqlQuery *query /* = NULL */) + { + qx::dao::detail::IxDao_Timer timer(pDaoHelper, qx::dao::detail::IxDao_Helper::timer_db_exec); + QxMongoDB_Helper *pSingleton = QxMongoDB_Helper::getSingleton(); + qAssert(pSingleton != NULL); + QSqlError err = pSingleton->m_pImpl->deleteOne_(pDaoHelper, pClass, json, query); + return (pDaoHelper ? pDaoHelper->updateError(err) : err); + } + + QSqlError QxMongoDB_Helper::QxMongoDB_HelperImpl::deleteOne_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QString &json, const qx::QxSqlQuery *query) + { + if (json.isEmpty() && !query) + { + return QSqlError(); + } + qx_db_collection coll; + QSqlError err = initCollection(pClass, coll); + if (err.isValid()) + { + return err; + } + if (pDaoHelper) + { + pDaoHelper->qxQuery().queryAt(0, "MongoDB delete one '" + (pClass ? pClass->getName() : QString()) + "' :\n" + json + "\n" + (query ? query->queryAt(0) : QString())); + } + + qx_scoped_wrapper query_(toStdString((query ? query->queryAt(0) : json))); + if (!query_.get()) + { + return QSqlError("[QxOrm] Unable to create a 'bson_t' document/query instance (" + pClass->getName() + ") from JSON : " + QString(query_.err()->message), "", QSqlError::UnknownError); + } + + bson_error_t bsonError; + qx_opts_ptr opts; + qx_reply_ptr reply; + reply.reset(new qx_scoped_wrapper()); + err = getOptions_(QxMongoDB_Helper::opts_collection_delete_one, opts, (query ? query->queryAt(1) : QString())); + if (err.isValid()) + { + return err; + } + bool ok = mongoc_collection_delete_one(coll.collection->get(), query_.get(), (opts ? opts->get() : NULL), (reply ? reply->get() : NULL), (&bsonError)); + if (!ok) + { + return QSqlError("[QxOrm] Unable to delete one 'bson_t' document (" + pClass->getName() + ") in MongoDB database : " + QString(bsonError.message), "", QSqlError::UnknownError); + } + if (reply && (reply->value("deletedCount").toInt() < 1)) + { + return QSqlError("[QxOrm] Nothing has been deleted (" + pClass->getName() + ") in MongoDB database", "", QSqlError::UnknownError); + } + if (reply && m_bLogDatabaseReply) + { + reply->log(); + } + return QSqlError(); + } + + QSqlError QxMongoDB_Helper::deleteMany(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QStringList &json, const qx::QxSqlQuery *query /* = NULL */) + { + qx::dao::detail::IxDao_Timer timer(pDaoHelper, qx::dao::detail::IxDao_Helper::timer_db_exec); + QxMongoDB_Helper *pSingleton = QxMongoDB_Helper::getSingleton(); + qAssert(pSingleton != NULL); + QSqlError err = pSingleton->m_pImpl->deleteMany_(pDaoHelper, pClass, json, query); + return (pDaoHelper ? pDaoHelper->updateError(err) : err); + } + + QSqlError QxMongoDB_Helper::QxMongoDB_HelperImpl::deleteMany_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, const QStringList &json, const qx::QxSqlQuery *query) + { + if ((json.count() <= 0) && (!query)) + { + return QSqlError(); + } + if (query && (query->type() == "aggregate")) + { + QStringList empty; + return aggregate_(pDaoHelper, pClass, empty, query, "", NULL, true); + } + QString listId = (query ? QString() : ((json.count() > 0) ? QString("{ \"_id\": { \"$in\": [ %ID% ] } }") : QString())); + if (!listId.isEmpty()) + { + QString ids; + Q_FOREACH (QString s, json) + { + ids += asJson(s) + ", "; + }; + ids = ids.left(ids.count() - 2); + listId.replace("%ID%", ids); + } + if (pDaoHelper) + { + pDaoHelper->qxQuery().queryAt(0, "MongoDB delete many '" + (pClass ? pClass->getName() : QString()) + "' :\n" + json.join(", \n") + "\n" + (query ? query->queryAt(0) : QString())); + } + qx_db_collection coll; + QSqlError err = initCollection(pClass, coll); + if (err.isValid()) + { + return err; + } + + qx_scoped_wrapper query_(toStdString((query ? query->queryAt(0) : listId))); + if (!query_.get()) + { + return QSqlError("[QxOrm] Unable to create a 'bson_t' document/query instance (" + pClass->getName() + ") from JSON : " + QString(query_.err()->message), "", QSqlError::UnknownError); + } + + bson_error_t bsonError; + qx_opts_ptr opts; + qx_reply_ptr reply; + if (m_bLogDatabaseReply) + { + reply.reset(new qx_scoped_wrapper()); + } + err = getOptions_(QxMongoDB_Helper::opts_collection_delete_many, opts, (query ? query->queryAt(1) : QString())); + if (err.isValid()) + { + return err; + } + bool ok = mongoc_collection_delete_many(coll.collection->get(), query_.get(), (opts ? opts->get() : NULL), (reply ? reply->get() : NULL), (&bsonError)); + if (!ok) + { + return QSqlError("[QxOrm] Unable to delete many 'bson_t' documents (" + pClass->getName() + ") in MongoDB database : " + QString(bsonError.message), "", QSqlError::UnknownError); + } + if (reply) + { + reply->log(); + } + return QSqlError(); + } + + QSqlError QxMongoDB_Helper::findOne(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, QString &json, const qx::QxSqlQuery *query /* = NULL */) + { + qx::dao::detail::IxDao_Timer timer(pDaoHelper, qx::dao::detail::IxDao_Helper::timer_db_exec); + QxMongoDB_Helper *pSingleton = QxMongoDB_Helper::getSingleton(); + qAssert(pSingleton != NULL); + QSqlError err = pSingleton->m_pImpl->findOne_(pDaoHelper, pClass, json, query); + return (pDaoHelper ? pDaoHelper->updateError(err) : err); + } + + QSqlError QxMongoDB_Helper::QxMongoDB_HelperImpl::findOne_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, QString &json, const qx::QxSqlQuery *query) + { + QString lookup = buildLookup(pDaoHelper, false); + QSqlError err; + if ((!lookup.isEmpty()) || (query && (query->type() == "aggregate"))) + { + QStringList result; + if ((!query) && (!json.isEmpty())) + { + qx_query query_tmp(json); + err = aggregate_(pDaoHelper, pClass, result, (&query_tmp), lookup, NULL); + } + else + { + err = aggregate_(pDaoHelper, pClass, result, query, lookup, NULL); + } + json = ((result.count() > 0) ? result.at(0) : QString()); + return err; + } + + QString id = (query ? QString() : json); + json = QString(); + qx_db_collection coll; + err = initCollection(pClass, coll); + if (err.isValid()) + { + return err; + } + + QString findQuery = (query ? query->queryAt(0) : id); + qx_scoped_wrapper query_(toStdString(findQuery)); + if (pDaoHelper) + { + pDaoHelper->qxQuery().queryAt(0, "MongoDB find one '" + (pClass ? pClass->getName() : QString()) + "' :\n" + findQuery); + } + if (!query_.get()) + { + return QSqlError("[QxOrm] Unable to create a 'bson_t' document/query instance (" + pClass->getName() + ") from JSON : " + QString(query_.err()->message), "", QSqlError::UnknownError); + } + + QStringList columns = (pDaoHelper ? pDaoHelper->getSqlColumns() : QStringList()); + QString projection; + Q_FOREACH (QString col, columns) + { + projection += (col.isEmpty() ? QString() : ((projection.isEmpty() ? QString("\"") : QString(", \"")) + col + "\": 1")); + } + projection = (projection.isEmpty() ? QString() : QString("{ \"projection\": { " + projection + " } }")); + + QString customOpts = ((query && !query->queryAt(1).isEmpty()) ? query->queryAt(1) : projection); + if (pDaoHelper) + { + pDaoHelper->qxQuery().queryAt(0, "MongoDB find one '" + (pClass ? pClass->getName() : QString()) + "' :\n" + findQuery + "\n" + customOpts); + } + qx_opts_ptr opts; + err = getOptions_(QxMongoDB_Helper::opts_collection_find, opts, customOpts); + if (err.isValid()) + { + return err; + } + mongoc_cursor_t *cursor_ = mongoc_collection_find_with_opts(coll.collection->get(), query_.get(), (opts ? opts->get() : NULL), NULL); + + qx_scoped_wrapper cursor(cursor_); + if (!cursor.get()) + { + return QSqlError("[QxOrm] Unable to create a 'mongoc_cursor_t' cursor instance (" + pClass->getName() + ")", "", QSqlError::UnknownError); + } + + const bson_t *doc = NULL; + if (mongoc_cursor_next(cursor.get(), (&doc))) + { + char *val = bson_as_relaxed_extended_json(doc, NULL); + json = QString(val); + bson_free(val); + } + + bson_error_t bsonError; + if (mongoc_cursor_error(cursor.get(), (&bsonError))) + { + return QSqlError("[QxOrm] MongoDB cursor error trying to find one document (" + pClass->getName() + ") : " + QString(bsonError.message), "", QSqlError::UnknownError); + } + return QSqlError(); + } + + QSqlError QxMongoDB_Helper::findMany(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, QStringList &json, const qx::QxSqlQuery *query /* = NULL */, QxMongoDB_Fetcher *pFetcher /* = NULL */) + { + qx::dao::detail::IxDao_Timer timer(pDaoHelper, qx::dao::detail::IxDao_Helper::timer_db_exec); + QxMongoDB_Helper *pSingleton = QxMongoDB_Helper::getSingleton(); + qAssert(pSingleton != NULL); + QSqlError err = pSingleton->m_pImpl->findMany_(pDaoHelper, pClass, json, query, pFetcher); + return (pDaoHelper ? pDaoHelper->updateError(err) : err); + } + + QSqlError QxMongoDB_Helper::QxMongoDB_HelperImpl::findMany_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, QStringList &json, const qx::QxSqlQuery *query, QxMongoDB_Fetcher *pFetcher) + { + QString lookup = buildLookup(pDaoHelper, false); + if ((!lookup.isEmpty()) || (query && (query->type() == "aggregate"))) + { + return aggregate_(pDaoHelper, pClass, json, query, lookup, pFetcher); + } + + QString listId = (query ? QString() : ((json.count() > 0) ? QString("{ \"_id\": { \"$in\": [ %ID% ] } }") : QString())); + if (!listId.isEmpty()) + { + QString ids; + Q_FOREACH (QString s, json) + { + ids += asJson(s) + ", "; + }; + ids = ids.left(ids.count() - 2); + listId.replace("%ID%", ids); + } + json.clear(); + qx_db_collection coll; + QSqlError err = initCollection(pClass, coll); + if (err.isValid()) + { + return err; + } + + QString findQuery = (query ? query->queryAt(0) : listId); + qx_scoped_wrapper query_(toStdString((findQuery))); + if (pDaoHelper) + { + pDaoHelper->qxQuery().queryAt(0, "MongoDB find many '" + (pClass ? pClass->getName() : QString()) + "' :\n" + findQuery); + } + if (!query_.get()) + { + return QSqlError("[QxOrm] Unable to create a 'bson_t' document/query instance (" + pClass->getName() + ") from JSON : " + QString(query_.err()->message), "", QSqlError::UnknownError); + } + + QStringList columns = (pDaoHelper ? pDaoHelper->getSqlColumns() : QStringList()); + QString projection; + Q_FOREACH (QString col, columns) + { + projection += (col.isEmpty() ? QString() : ((projection.isEmpty() ? QString("\"") : QString(", \"")) + col + "\": 1")); + } + projection = (projection.isEmpty() ? QString() : QString("{ \"projection\": { " + projection + " } }")); + + QString customOpts = ((query && !query->queryAt(1).isEmpty()) ? query->queryAt(1) : projection); + if (pDaoHelper) + { + pDaoHelper->qxQuery().queryAt(0, "MongoDB find many '" + (pClass ? pClass->getName() : QString()) + "' :\n" + findQuery + "\n" + customOpts); + } + qx_opts_ptr opts; + err = getOptions_(QxMongoDB_Helper::opts_collection_find, opts, customOpts); + if (err.isValid()) + { + return err; + } + mongoc_cursor_t *cursor_ = mongoc_collection_find_with_opts(coll.collection->get(), query_.get(), (opts ? opts->get() : NULL), NULL); + + qx_scoped_wrapper cursor(cursor_); + if (!cursor.get()) + { + return QSqlError("[QxOrm] Unable to create a 'mongoc_cursor_t' cursor instance (" + pClass->getName() + ")", "", QSqlError::UnknownError); + } + + const bson_t *doc = NULL; + while (mongoc_cursor_next(cursor.get(), (&doc))) + { + char *val = bson_as_relaxed_extended_json(doc, NULL); + if (pFetcher) + { + QString tmp(val); + pFetcher->fetch(tmp); + if (pDaoHelper && (!pDaoHelper->isValid())) + { + bson_free(val); + return pDaoHelper->error(); + } + } + else + { + json << QString(val); + } + bson_free(val); + } + + bson_error_t bsonError; + if (mongoc_cursor_error(cursor.get(), (&bsonError))) + { + return QSqlError("[QxOrm] MongoDB cursor error trying to find many documents (" + pClass->getName() + ") : " + QString(bsonError.message), "", QSqlError::UnknownError); + } + return QSqlError(); + } + + QSqlError QxMongoDB_Helper::aggregate(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, QStringList &json, const qx::QxSqlQuery *query /* = NULL */, const QString &lookup /* = QString() */, QxMongoDB_Fetcher *pFetcher /* = NULL */) + { + qx::dao::detail::IxDao_Timer timer(pDaoHelper, qx::dao::detail::IxDao_Helper::timer_db_exec); + QxMongoDB_Helper *pSingleton = QxMongoDB_Helper::getSingleton(); + qAssert(pSingleton != NULL); + QSqlError err = pSingleton->m_pImpl->aggregate_(pDaoHelper, pClass, json, query, lookup, pFetcher); + return (pDaoHelper ? pDaoHelper->updateError(err) : err); + } + + QSqlError QxMongoDB_Helper::QxMongoDB_HelperImpl::aggregate_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, QStringList &json, const qx::QxSqlQuery *query, const QString &lookup, QxMongoDB_Fetcher *pFetcher, bool bModeDelete /* = false */) + { + QString listId = (query ? QString() : ((json.count() > 0) ? QString("{ \"_id\": { \"$in\": [ %ID% ] } }") : QString())); + if (!listId.isEmpty()) + { + QString ids; + Q_FOREACH (QString s, json) + { + ids += asJson(s) + ", "; + }; + ids = ids.left(ids.count() - 2); + listId.replace("%ID%", ids); + } + json.clear(); + qx_db_collection coll; + QSqlError err = initCollection(pClass, coll); + if (err.isValid()) + { + return err; + } + + QString findQuery = (query ? query->queryAt(0) : listId); + if (!lookup.isEmpty() && !findQuery.isEmpty()) + { + findQuery = "[ { \"$match\": " + findQuery + " }, " + lookup + " ]"; + } + else if (!lookup.isEmpty() && findQuery.isEmpty()) + { + findQuery = "[ " + lookup + " ]"; + } + + qx_scoped_wrapper query_(toStdString((findQuery))); + if (pDaoHelper) + { + pDaoHelper->qxQuery().queryAt(0, "MongoDB aggregate '" + (pClass ? pClass->getName() : QString()) + "' :\n" + findQuery); + } + if (!query_.get()) + { + return QSqlError("[QxOrm] Unable to create a 'bson_t' document/query instance (" + pClass->getName() + ") from JSON : " + QString(query_.err()->message), "", QSqlError::UnknownError); + } + + qx_opts_ptr opts; + err = getOptions_(QxMongoDB_Helper::opts_collection_aggregate, opts, (query ? query->queryAt(1) : QString())); + if (err.isValid()) + { + return err; + } + mongoc_cursor_t *cursor_ = mongoc_collection_aggregate(coll.collection->get(), MONGOC_QUERY_NONE, query_.get(), (opts ? opts->get() : NULL), NULL); + + qx_scoped_wrapper cursor(cursor_); + if (!cursor.get()) + { + return QSqlError("[QxOrm] Unable to create a 'mongoc_cursor_t' cursor instance (" + pClass->getName() + ")", "", QSqlError::UnknownError); + } + + const bson_t *doc = NULL; + QStringList listIdsToDelete; + while (mongoc_cursor_next(cursor.get(), (&doc))) + { + char *val = bson_as_relaxed_extended_json(doc, NULL); + if (bModeDelete) + { + QJsonValue obj; + QString tmp(val); + qx::cvt::from_string(tmp, obj); + if (obj.isObject()) + { + QJsonValue id = obj.toObject().value("_id"); + qx::cvt::from_json(id, tmp, "mongodb"); + if (!tmp.isEmpty()) + { + listIdsToDelete.append(tmp); + } + } + } + else if (pFetcher) + { + QString tmp(val); + pFetcher->fetch(tmp); + if (pDaoHelper && (!pDaoHelper->isValid())) + { + bson_free(val); + return pDaoHelper->error(); + } + } + else + { + json << QString(val); + } + bson_free(val); + } + + bson_error_t bsonError; + if (mongoc_cursor_error(cursor.get(), (&bsonError))) + { + return QSqlError("[QxOrm] MongoDB cursor error trying to find many documents (" + pClass->getName() + ") : " + QString(bsonError.message), "", QSqlError::UnknownError); + } + if (bModeDelete && (listIdsToDelete.count() > 0)) + { + return deleteMany_(pDaoHelper, pClass, listIdsToDelete, NULL); + } + return QSqlError(); + } + + QSqlError QxMongoDB_Helper::executeCommand(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, qx::QxSqlQuery *query) + { + qx::dao::detail::IxDao_Timer timer(pDaoHelper, qx::dao::detail::IxDao_Helper::timer_db_exec); + QxMongoDB_Helper *pSingleton = QxMongoDB_Helper::getSingleton(); + qAssert(pSingleton != NULL); + QSqlError err = pSingleton->m_pImpl->executeCommand_(pDaoHelper, pClass, query); + return (pDaoHelper ? pDaoHelper->updateError(err) : err); + } + + QSqlError QxMongoDB_Helper::QxMongoDB_HelperImpl::executeCommand_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, qx::QxSqlQuery *query) + { + if (!query) + { + return QSqlError("[QxOrm] Unable to execute command in MongoDB database : 'query' parameter is required", "", QSqlError::UnknownError); + } + query->setResponse(QVariant()); + qx_db_collection coll; + QSqlError err = (pClass ? initCollection(pClass, coll) : initDatabase(coll)); + if (err.isValid()) + { + return err; + } + QString collName = (pClass ? pClass->getName() : QString()); + + qx_scoped_wrapper cmd_(toStdString((query ? query->queryAt(0) : QString()))); + if (pDaoHelper) + { + pDaoHelper->qxQuery().queryAt(0, "MongoDB command '" + (pClass ? pClass->getName() : QString()) + "' :\n" + (query ? query->queryAt(0) : QString())); + } + if (!cmd_.get()) + { + return QSqlError("[QxOrm] Unable to create a 'bson_t' document/command instance (" + collName + ") from JSON : " + QString(cmd_.err()->message), "", QSqlError::UnknownError); + } + + bson_error_t bsonError; + qx_opts_ptr opts; + qx_reply_ptr reply; + reply.reset(new qx_scoped_wrapper()); + err = getOptions_(QxMongoDB_Helper::opts_collection_command, opts, (query ? query->queryAt(1) : QString())); + if (err.isValid()) + { + return err; + } + + if (!pClass) + { + if (!mongoc_database_command_with_opts(coll.db->get(), cmd_.get(), NULL, (opts ? opts->get() : NULL), reply->get(), (&bsonError))) + { + return QSqlError("[QxOrm] Unable to execute command in MongoDB database : " + QString(bsonError.message), "", QSqlError::UnknownError); + } + } + else + { + if (!mongoc_collection_command_with_opts(coll.collection->get(), cmd_.get(), NULL, (opts ? opts->get() : NULL), reply->get(), (&bsonError))) + { + return QSqlError("[QxOrm] Unable to execute command (" + collName + ") in MongoDB database : " + QString(bsonError.message), "", QSqlError::UnknownError); + } + } + + if (query && (query->type() == "cursor")) + { + reply->m_destroy = false; + qx_scoped_wrapper cursor(mongoc_cursor_new_from_command_reply(coll.client->get(), reply->get(), 0)); + if (!cursor.get()) + { + return QSqlError("[QxOrm] Unable to create a 'mongoc_cursor_t' cursor instance from reply (" + pClass->getName() + ")", "", QSqlError::UnknownError); + } + + const bson_t *doc = NULL; + QString json; + while (mongoc_cursor_next(cursor.get(), (&doc))) + { + char *val = bson_as_relaxed_extended_json(doc, NULL); + json += ((json.isEmpty() ? QString() : QString(",\n")) + QString(val)); + bson_free(val); + } + if (!json.isEmpty()) + { + json = "[ " + json + " ]"; + } + query->setResponse(QVariant(json)); + + bson_error_t bsonError; + if (mongoc_cursor_error(cursor.get(), (&bsonError))) + { + return QSqlError("[QxOrm] MongoDB cursor error trying to find many documents from reply (" + pClass->getName() + ") : " + QString(bsonError.message), "", QSqlError::UnknownError); + } + } + else + { + const bson_t *reply_ = const_cast(reply->get()); + char *val = bson_as_relaxed_extended_json(reply_, NULL); + query->setResponse(QVariant(QString(val))); + bson_free(val); + } + + return QSqlError(); + } + + QSqlError QxMongoDB_Helper::count(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, long &cnt, const qx::QxSqlQuery *query /* = NULL */) + { + qx::dao::detail::IxDao_Timer timer(pDaoHelper, qx::dao::detail::IxDao_Helper::timer_db_exec); + QxMongoDB_Helper *pSingleton = QxMongoDB_Helper::getSingleton(); + qAssert(pSingleton != NULL); + QSqlError err = pSingleton->m_pImpl->count_(pDaoHelper, pClass, cnt, query); + return (pDaoHelper ? pDaoHelper->updateError(err) : err); + } + + QSqlError QxMongoDB_Helper::QxMongoDB_HelperImpl::count_(qx::dao::detail::IxDao_Helper *pDaoHelper, qx::IxClass *pClass, long &cnt, const qx::QxSqlQuery *query) + { + cnt = 0; + qx_db_collection coll; + QSqlError err = initCollection(pClass, coll); + if (err.isValid()) + { + return err; + } + + qx_scoped_wrapper query_(toStdString((query ? query->queryAt(0) : QString()))); + if (pDaoHelper) + { + pDaoHelper->qxQuery().queryAt(0, "MongoDB count '" + (pClass ? pClass->getName() : QString()) + "' :\n" + (query ? query->queryAt(0) : QString())); + } + if (!query_.get()) + { + return QSqlError("[QxOrm] Unable to create a 'bson_t' document/query instance (" + pClass->getName() + ") from JSON : " + QString(query_.err()->message), "", QSqlError::UnknownError); + } + + bson_error_t bsonError; + qx_opts_ptr opts; + err = getOptions_(QxMongoDB_Helper::opts_collection_count, opts, (query ? query->queryAt(1) : QString())); + if (err.isValid()) + { + return err; + } + cnt = static_cast(mongoc_collection_count_with_opts(coll.collection->get(), MONGOC_QUERY_NONE, query_.get(), 0, 0, (opts ? opts->get() : NULL), NULL, (&bsonError))); + if (cnt < 0) + { + cnt = 0; + return QSqlError("[QxOrm] Unable to count 'bson_t' documents (" + pClass->getName() + ") in MongoDB database : " + QString(bsonError.message), "", QSqlError::UnknownError); + } + return QSqlError(); + } + + QSqlError QxMongoDB_Helper::autoCreateIndexes(bool log /* = true */) + { + qx::QxClassX::registerAllClasses(); + qx::QxCollection *pAllClasses = qx::QxClassX::getAllClasses(); + if (!pAllClasses) + { + return QSqlError("[QxOrm] Unable to get all classes registered in QxOrm context", "", QSqlError::UnknownError); + } + + for (auto itr = pAllClasses->begin(); itr != pAllClasses->end(); ++itr) + { + qx::IxClass *pClass = itr->second; + if (!pClass || pClass->isAbstract()) + { + continue; + } + QString collectionName = pClass->getName(); + + do + { + qx::IxDataMemberX *pDataMemberX = pClass->getDataMemberX(); + long cnt = (pDataMemberX ? pDataMemberX->count() : 0); + for (long l = 0; l < cnt; l++) + { + qx::IxDataMember *pDataMember = pDataMemberX->get(l); + if (!pDataMember || !pDataMember->getDao() || pDataMember->getIsPrimaryKey()) + { + continue; + } + qx::IxSqlRelation *pRelation = pDataMember->getSqlRelation(); + bool isIndex = pDataMember->getIsIndex(); + if (!isIndex && !pRelation) + { + continue; + } + qx::IxSqlRelation::relation_type eRelationType = (pRelation ? pRelation->getRelationType() : qx::IxSqlRelation::no_relation); + bool isUnique = pDataMember->getIsUnique(); + + QString idxName; + if (eRelationType == qx::IxSqlRelation::many_to_one) + { + idxName = "idx_" + collectionName + "_" + pDataMember->getKey() + "_many_to_one"; + } + else if (eRelationType == qx::IxSqlRelation::one_to_one) + { + idxName = "idx_" + collectionName + "_" + pDataMember->getKey() + "_one_to_one"; + } + else if (isIndex) + { + idxName = "idx_" + collectionName + "_" + pDataMember->getKey(); + } + if (idxName.isEmpty()) + { + continue; + } + + QString query_ = "{ \"createIndexes\": \"" + collectionName + "\", \"indexes\": "; + query_ += "[ { \"key\": { \"" + pDataMember->getKey() + "\": 1 }, \"name\": \"" + idxName + "\"" + (isUnique ? QString(", \"unique\": true") : QString()) + " } ] }"; + qx_query query(query_); + + if (log) + { + QString msg = "MongoDB create index for '" + collectionName + "::" + pDataMember->getKey() + "', request : " + query_; + qDebug("[QxOrm] %s", qPrintable(msg)); + } + QSqlError err = executeCommand(NULL, NULL, (&query)); + if (err.isValid()) + { + return err; + } + if (log) + { + QString response = query.response().toString(); + QString msg = "MongoDB create index for '" + collectionName + "::" + pDataMember->getKey() + "', response : " + response; + qDebug("[QxOrm] %s", qPrintable(msg)); + } + } + pClass = pClass->getBaseClass(); + } while (pClass != NULL); + } + + return QSqlError(); + } + + bool QxMongoDB_Helper::setOptions(QxMongoDB_Helper::opts e, const QString &optsAsJson) + { + QxMongoDB_Helper *pSingleton = QxMongoDB_Helper::getSingleton(); + qAssert(pSingleton != NULL); + return pSingleton->m_pImpl->setOptions_(e, optsAsJson); + } + + void QxMongoDB_Helper::setLogDatabaseReply(bool b) + { + QxMongoDB_Helper *pSingleton = QxMongoDB_Helper::getSingleton(); + qAssert(pSingleton != NULL); + pSingleton->m_pImpl->m_bLogDatabaseReply = b; + } + + void QxMongoDB_Helper::setLogDatabaseInfo(bool b) + { + QxMongoDB_Helper *pSingleton = QxMongoDB_Helper::getSingleton(); + qAssert(pSingleton != NULL); + pSingleton->m_pImpl->m_bLogDatabaseInfo = b; + } + + void QxMongoDB_Helper::clearPoolConnection() + { + QxMongoDB_Helper *pSingleton = QxMongoDB_Helper::getSingleton(); + qAssert(pSingleton != NULL); + QMutexLocker locker(&pSingleton->m_pImpl->m_oDbMutex); + pSingleton->m_pImpl->m_pPool.reset(); + pSingleton->m_pImpl->m_pUri.reset(); + } + + } // namespace mongodb + } // namespace dao +} // namespace qx + +#endif // _QX_ENABLE_MONGODB diff --git a/src/QxDao/QxRepository/IxRepository.cpp b/src/QxDao/QxRepository/IxRepository.cpp new file mode 100644 index 0000000..b4f482a --- /dev/null +++ b/src/QxDao/QxRepository/IxRepository.cpp @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include +#include + +#include + +#include + +namespace qx +{ + + IxRepository::IxRepository(bool bRegister, const QString &sKey) : m_bRegister(bRegister), m_sKeyRepository(sKey), m_pSession(NULL) + { + if (m_bRegister && !m_sKeyRepository.isEmpty()) + { + QxRepositoryX::getSingleton()->registerRepository(m_sKeyRepository, this); + } + } + + IxRepository::IxRepository(bool bRegister, const QString &sKey, const QSqlDatabase &database) : m_bRegister(bRegister), m_sKeyRepository(sKey), m_database(database), m_pSession(NULL) + { + if (m_bRegister && !m_sKeyRepository.isEmpty()) + { + QxRepositoryX::getSingleton()->registerRepository(m_sKeyRepository, this); + } + } + + IxRepository::IxRepository(bool bRegister, const QString &sKey, QxSession *pSession) : m_bRegister(bRegister), m_sKeyRepository(sKey), m_pSession(pSession) + { + if (m_bRegister && !m_sKeyRepository.isEmpty()) + { + QxRepositoryX::getSingleton()->registerRepository(m_sKeyRepository, this); + } + } + + IxRepository::~IxRepository() + { + if (m_bRegister && !m_sKeyRepository.isEmpty() && !QxRepositoryX::isSingletonNull()) + { + QxRepositoryX::getSingleton()->unregisterRepository(m_sKeyRepository); + } + } + + QSqlDatabase *IxRepository::database() + { + if (m_pSession) + { + return (m_pSession->database()->isValid() ? m_pSession->database() : NULL); + } + return (m_database.isValid() ? (&m_database) : NULL); + } + + QxSession *IxRepository::session() const + { + return m_pSession; + } + + qx::IxCollection_ptr IxRepository::_fetchAll(const QString &repositoryKey, const QStringList &columns /* = QStringList() */, const QStringList &relation /* = QStringList() */) + { + IxRepository *pRepository = QxRepositoryX::get(repositoryKey); + if (!pRepository) + { + throw qx::dao::sql_error(QSqlError("[QxOrm] qx::IxRepository::_fetchAll() : 'invalid repository key, unable to get repository pointer'", "", QSqlError::UnknownError)); + } + qx::IxCollection_ptr lst = pRepository->_newCollection(); + if (!lst) + { + throw qx::dao::sql_error(QSqlError("[QxOrm] qx::IxRepository::_fetchAll() : 'unable to create a new collection from repository'", "", QSqlError::UnknownError)); + } + QSqlError daoError = pRepository->_fetchAll(lst.get(), columns, relation); + if (daoError.isValid()) + { + throw qx::dao::sql_error(daoError); + } + return lst; + } + + qx::IxCollection_ptr IxRepository::_fetchByQuery(const QString &repositoryKey, const qx::QxSqlQuery &query, const QStringList &columns /* = QStringList() */, const QStringList &relation /* = QStringList() */) + { + IxRepository *pRepository = QxRepositoryX::get(repositoryKey); + if (!pRepository) + { + throw qx::dao::sql_error(QSqlError("[QxOrm] qx::IxRepository::_fetchByQuery() : 'invalid repository key, unable to get repository pointer'", "", QSqlError::UnknownError)); + } + qx::IxCollection_ptr lst = pRepository->_newCollection(); + if (!lst) + { + throw qx::dao::sql_error(QSqlError("[QxOrm] qx::IxRepository::_fetchByQuery() : 'unable to create a new collection from repository'", "", QSqlError::UnknownError)); + } + QSqlError daoError = pRepository->_fetchByQuery(query, lst.get(), columns, relation); + if (daoError.isValid()) + { + throw qx::dao::sql_error(daoError); + } + return lst; + } + +} // namespace qx diff --git a/src/QxDao/QxRepository/QxRepositoryX.cpp b/src/QxDao/QxRepository/QxRepositoryX.cpp new file mode 100644 index 0000000..1792e5b --- /dev/null +++ b/src/QxDao/QxRepository/QxRepositoryX.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::QxRepositoryX) + +namespace qx +{ + + void QxRepositoryX::registerRepository(const QString &sKey, IxRepository *pRepository) + { + QMutexLocker locker(&m_oMutexRepositoryX); + qAssert(!sKey.isEmpty() && !m_mapRepositoryX.contains(sKey)); + if (!pRepository || sKey.isEmpty() || m_mapRepositoryX.contains(sKey)) + { + return; + } + m_mapRepositoryX.insert(sKey, pRepository); + } + + void QxRepositoryX::unregisterRepository(const QString &sKey) + { + if (m_bUnregisterAllRepository) + { + return; + } + QMutexLocker locker(&m_oMutexRepositoryX); + IxRepository *pRepository = (m_mapRepositoryX.contains(sKey) ? m_mapRepositoryX.value(sKey) : NULL); + if (pRepository) + { + delete pRepository; + pRepository = NULL; + } + m_mapRepositoryX.remove(sKey); + } + + void QxRepositoryX::unregisterAllRepository() + { + m_bUnregisterAllRepository = true; + QMutexLocker locker(&m_oMutexRepositoryX); + QHashIterator itr(m_mapRepositoryX); + while (itr.hasNext()) + { + itr.next(); + delete itr.value(); + } + m_mapRepositoryX.clear(); + m_bUnregisterAllRepository = false; + } + + IxRepository *QxRepositoryX::get(const QString &sKey) + { + if (!QxRepositoryX::getSingleton()->m_mapRepositoryX.contains(sKey)) + { + return NULL; + } + return QxRepositoryX::getSingleton()->m_mapRepositoryX.value(sKey); + } + +} // namespace qx diff --git a/src/QxDao/QxSession.cpp b/src/QxDao/QxSession.cpp new file mode 100644 index 0000000..a8ab055 --- /dev/null +++ b/src/QxDao/QxSession.cpp @@ -0,0 +1,353 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include +#include + +#include +#include + +#include + +namespace qx +{ + + struct Q_DECL_HIDDEN QxSession::QxSessionImpl + { + + QSqlDatabase m_database; //!< Database connexion of current session + QList m_lstSqlError; //!< List of SQL errors + bool m_bTransaction; //!< Flag to know if a transaction is opened or not + bool m_bThrowable; //!< When a SQL error is appended, an exception of type qx::dao::sql_error is thrown + bool m_bThrowInEvent; //!< An exception of type qx::dao::sql_error is throwing + bool m_bAutoOpenClose; //!< Open and close automatically connection to database + bool m_bIgnoreSoftDelete; //!< Ignore soft delete behavior even if it has been defined for registered classes + QStringList m_lstIgnoreSoftDelete; //!< List of registered classes to ignore soft delete behavior (if empty and m_bIgnoreSoftDelete is true, then all classes are ignored) + bool m_bAutoRollbackWhenDestroyed; //!< If true, then database rollback is called when this session instance destructor (or close() method) is invoked + QPair m_sessionId; //!< Session identifier based on connection name and current thread + + static QMutex m_mutex; //!< Mutex => qx::QxSession is thread-safe + static QHash, qx::QxSession *> m_lstActiveSessions; //!< List of active sessions by database connection name + + QxSessionImpl() : m_bTransaction(false), m_bThrowable(false), m_bThrowInEvent(false), m_bAutoOpenClose(false), m_bIgnoreSoftDelete(false), m_bAutoRollbackWhenDestroyed(false) { ; } + QxSessionImpl(const QSqlDatabase &database) : m_database(database), m_bTransaction(false), m_bThrowable(false), m_bThrowInEvent(false), m_bAutoOpenClose(false), m_bIgnoreSoftDelete(false), m_bAutoRollbackWhenDestroyed(false) { ; } + QxSessionImpl(const QSqlDatabase &database, bool bThrowable, bool bAutoRollbackWhenDestroyed) : m_database(database), m_bTransaction(false), m_bThrowable(bThrowable), m_bThrowInEvent(false), m_bAutoOpenClose(false), m_bIgnoreSoftDelete(false), m_bAutoRollbackWhenDestroyed(bAutoRollbackWhenDestroyed) { ; } + ~QxSessionImpl() { ; } + + void appendSqlError(const QSqlError &err) + { + if (!err.isValid()) + { + return; + } + m_lstSqlError.append(err); + if (m_bThrowInEvent) + { + return; + } + if (m_bThrowable) + { + QString msg = err.text(); + qDebug("[QxOrm] qx::QxSession throw 'qx::dao::sql_error' exception : '%s'", qPrintable(msg)); + } + if (m_bThrowable) + { + m_bThrowInEvent = true; + throw qx::dao::sql_error(err); + } + m_bThrowInEvent = false; + } + + void clear() + { + m_lstSqlError.clear(); + m_bTransaction = false; + } + + static QPair getSessionIdentifier(QSqlDatabase &db, qx::QxSession *pSessionToFill, qx::QxSession *pSessionToFetch) + { + if (pSessionToFetch && pSessionToFetch->m_pImpl) + { + return pSessionToFetch->m_pImpl->m_sessionId; + } + QString connectionName = db.connectionName(); + Qt::HANDLE hCurrThreadId = QThread::currentThreadId(); + QPair sessionId = qMakePair(hCurrThreadId, connectionName); + if (pSessionToFill && pSessionToFill->m_pImpl) + { + pSessionToFill->m_pImpl->m_sessionId = sessionId; + } + return sessionId; + } + }; + + QMutex qx::QxSession::QxSessionImpl::m_mutex; + QHash, qx::QxSession *> qx::QxSession::QxSessionImpl::m_lstActiveSessions; + + QxSession::QxSession() : m_pImpl(new QxSessionImpl()) + { + m_pImpl->m_database = qx::QxSqlDatabase::getDatabaseCloned(); + m_pImpl->m_bThrowable = qx::QxSqlDatabase::getSingleton()->getSessionThrowable(); + if (qx::QxSqlDatabase::getSingleton()->getSessionAutoTransaction()) + { + open(); + } + QMutexLocker locker(&QxSessionImpl::m_mutex); + QPair sessionId = QxSessionImpl::getSessionIdentifier(m_pImpl->m_database, this, NULL); + if (!sessionId.second.isEmpty()) + { + QxSessionImpl::m_lstActiveSessions.insert(sessionId, this); + } + } + + QxSession::QxSession(const QSqlDatabase &database) : m_pImpl(new QxSessionImpl(database)) + { + m_pImpl->m_bThrowable = qx::QxSqlDatabase::getSingleton()->getSessionThrowable(); + if (qx::QxSqlDatabase::getSingleton()->getSessionAutoTransaction()) + { + open(); + } + QMutexLocker locker(&QxSessionImpl::m_mutex); + QPair sessionId = QxSessionImpl::getSessionIdentifier(m_pImpl->m_database, this, NULL); + if (!sessionId.second.isEmpty()) + { + QxSessionImpl::m_lstActiveSessions.insert(sessionId, this); + } + } + + QxSession::QxSession(const QSqlDatabase &database, bool bOpenTransaction) : m_pImpl(new QxSessionImpl(database)) + { + m_pImpl->m_bThrowable = qx::QxSqlDatabase::getSingleton()->getSessionThrowable(); + if (bOpenTransaction) + { + open(); + } + QMutexLocker locker(&QxSessionImpl::m_mutex); + QPair sessionId = QxSessionImpl::getSessionIdentifier(m_pImpl->m_database, this, NULL); + if (!sessionId.second.isEmpty()) + { + QxSessionImpl::m_lstActiveSessions.insert(sessionId, this); + } + } + + QxSession::QxSession(const QSqlDatabase &database, bool bOpenTransaction, bool bThrowable, bool bAutoRollbackWhenDestroyed /* = false */) : m_pImpl(new QxSessionImpl(database, bThrowable, bAutoRollbackWhenDestroyed)) + { + if (bOpenTransaction) + { + open(); + } + QMutexLocker locker(&QxSessionImpl::m_mutex); + QPair sessionId = QxSessionImpl::getSessionIdentifier(m_pImpl->m_database, this, NULL); + if (!sessionId.second.isEmpty()) + { + QxSessionImpl::m_lstActiveSessions.insert(sessionId, this); + } + } + + QxSession::~QxSession() + { + close(); + QMutexLocker locker(&QxSessionImpl::m_mutex); + QPair sessionId = QxSessionImpl::getSessionIdentifier(m_pImpl->m_database, NULL, this); + if (!sessionId.second.isEmpty()) + { + QxSessionImpl::m_lstActiveSessions.remove(sessionId); + } + } + + QxSession *QxSession::getActiveSession(QSqlDatabase *db) + { + if (!db) + { + return NULL; + } + QMutexLocker locker(&QxSessionImpl::m_mutex); + QPair sessionId = QxSessionImpl::getSessionIdentifier((*db), NULL, NULL); + if (sessionId.second.isEmpty()) + { + return NULL; + } + return (QxSessionImpl::m_lstActiveSessions.contains(sessionId) ? QxSessionImpl::m_lstActiveSessions.value(sessionId) : NULL); + } + + bool QxSession::isThrowable() const { return m_pImpl->m_bThrowable; } + + bool QxSession::isOpened() const { return m_pImpl->m_bTransaction; } + + bool QxSession::isValid() const { return (m_pImpl->m_lstSqlError.count() <= 0); } + + bool QxSession::isAutoRollbackWhenDestroyed() const { return m_pImpl->m_bAutoRollbackWhenDestroyed; } + + void QxSession::setAutoRollbackWhenDestroyed(bool b) { m_pImpl->m_bAutoRollbackWhenDestroyed = b; } + + QSqlError QxSession::firstError() const { return ((m_pImpl->m_lstSqlError.count() > 0) ? m_pImpl->m_lstSqlError.first() : QSqlError()); } + + QSqlError QxSession::lastError() const { return ((m_pImpl->m_lstSqlError.count() > 0) ? m_pImpl->m_lstSqlError.last() : QSqlError()); } + + QList QxSession::allErrors() const { return m_pImpl->m_lstSqlError; } + + const QSqlDatabase *QxSession::database() const { return (&m_pImpl->m_database); } + + QSqlDatabase *QxSession::database() { return (&m_pImpl->m_database); } + + bool QxSession::open() + { + if (m_pImpl->m_bTransaction) + { + return true; + } + if (!m_pImpl->m_database.isOpen()) + { + m_pImpl->m_bAutoOpenClose = m_pImpl->m_database.open(); + if (!m_pImpl->m_bAutoOpenClose) + { + m_pImpl->appendSqlError(m_pImpl->m_database.lastError()); + return false; + } + } + if (!m_pImpl->m_database.driver() || !m_pImpl->m_database.driver()->hasFeature(QSqlDriver::Transactions)) + { + return false; + } + m_pImpl->m_bTransaction = m_pImpl->m_database.transaction(); + return m_pImpl->m_bTransaction; + } + + bool QxSession::close() + { + bool bCloseOk = true; + if (m_pImpl->m_bTransaction && m_pImpl->m_bAutoRollbackWhenDestroyed) + { + bCloseOk = rollback(); + } + else if (m_pImpl->m_bTransaction && isValid()) + { + bCloseOk = commit(); + } + else if (m_pImpl->m_bTransaction) + { + bCloseOk = rollback(); + } + if (m_pImpl->m_bAutoOpenClose) + { + m_pImpl->m_database.close(); + m_pImpl->m_bAutoOpenClose = false; + } + return bCloseOk; + } + + bool QxSession::commit() + { + if (m_pImpl->m_bTransaction && !isValid()) + { + qDebug("[QxOrm] %s", "qx::QxSession is not valid and 'commit()' method is called"); + } + if (!m_pImpl->m_bTransaction) + { + m_pImpl->clear(); + return false; + } + bool bCommit = m_pImpl->m_database.commit(); + if (bCommit) + { + m_pImpl->clear(); + return true; + } + m_pImpl->appendSqlError(m_pImpl->m_database.lastError()); + m_pImpl->m_bTransaction = false; + return false; + } + + bool QxSession::rollback() + { + if (!m_pImpl->m_bTransaction) + { + m_pImpl->clear(); + return false; + } + qDebug("[QxOrm] qx::QxSession : '%s'", "rollback transaction"); + bool bRollback = m_pImpl->m_database.rollback(); + if (bRollback) + { + m_pImpl->clear(); + return true; + } + m_pImpl->appendSqlError(m_pImpl->m_database.lastError()); + m_pImpl->m_bTransaction = false; + return false; + } + + QxSession &QxSession::operator+=(const QSqlError &err) + { + m_pImpl->appendSqlError(err); + return (*this); + } + + void QxSession::ignoreSoftDelete(bool bIgnoreSoftDelete /* = true */, const QStringList &classesToIgnore /* = QStringList() */) + { + m_pImpl->m_bIgnoreSoftDelete = bIgnoreSoftDelete; + m_pImpl->m_lstIgnoreSoftDelete = classesToIgnore; + m_pImpl->m_lstIgnoreSoftDelete.sort(); + } + + bool QxSession::checkIgnoreSoftDelete(const QString &classKey) const + { + if (!m_pImpl->m_bIgnoreSoftDelete) + { + return false; + } + if (m_pImpl->m_lstIgnoreSoftDelete.isEmpty()) + { + return true; + } + if (m_pImpl->m_lstIgnoreSoftDelete.contains(classKey)) + { + return true; + } + return false; + } + + QString QxSession::getIgnoreSoftDeleteHash() const + { + if (!m_pImpl->m_bIgnoreSoftDelete) + { + return ""; + } + if (m_pImpl->m_lstIgnoreSoftDelete.isEmpty()) + { + return "[ALL]"; + } + return m_pImpl->m_lstIgnoreSoftDelete.join("_"); + } + +} // namespace qx diff --git a/src/QxDao/QxSoftDelete.cpp b/src/QxDao/QxSoftDelete.cpp new file mode 100644 index 0000000..bacace9 --- /dev/null +++ b/src/QxDao/QxSoftDelete.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +namespace qx +{ + + QxSoftDelete::QxSoftDelete() : m_eMode(QxSoftDelete::mode_date_time), m_bFetchInJoin(true) { ; } + + QxSoftDelete::QxSoftDelete(const QString &sColumn) : m_sColumn(sColumn), m_eMode(QxSoftDelete::mode_date_time), m_bFetchInJoin(true) { ; } + + QxSoftDelete::QxSoftDelete(const QString &sColumn, QxSoftDelete::mode eMode) : m_sColumn(sColumn), m_eMode(eMode), m_bFetchInJoin(true) { ; } + + QxSoftDelete::~QxSoftDelete() { ; } + + QString QxSoftDelete::getTableName() const { return m_sTable; } + + QString QxSoftDelete::getColumnName() const { return m_sColumn; } + + QString QxSoftDelete::getSqlQueryToFetch() const { return m_sSqlQueryToFetch; } + + QString QxSoftDelete::getSqlQueryToUpdate() const { return m_sSqlQueryToUpdate; } + + QString QxSoftDelete::getSqlQueryToCreateTable() const { return m_sSqlQueryToCreateTable; } + + QxSoftDelete::mode QxSoftDelete::getMode() const { return m_eMode; } + + bool QxSoftDelete::getSqlFetchInJoin() const { return m_bFetchInJoin; } + + void QxSoftDelete::setTableName(const QString &sTable) { m_sTable = sTable; } + + void QxSoftDelete::setColumnName(const QString &sColumn) { m_sColumn = sColumn; } + + void QxSoftDelete::setSqlQueryToFetch(const QString &s) { m_sSqlQueryToFetch = s; } + + void QxSoftDelete::setSqlQueryToUpdate(const QString &s) { m_sSqlQueryToUpdate = s; } + + void QxSoftDelete::setSqlQueryToCreateTable(const QString &s) { m_sSqlQueryToCreateTable = s; } + + void QxSoftDelete::setMode(QxSoftDelete::mode eMode) { m_eMode = eMode; } + + void QxSoftDelete::setSqlFetchInJoin(bool b) { m_bFetchInJoin = b; } + + bool QxSoftDelete::isEmpty() const { return (m_sTable.isEmpty() || m_sColumn.isEmpty()); } + + QString QxSoftDelete::buildSqlTablePointName(const QString &sTable /* = QString() */) const + { + if (this->isEmpty()) + { + return ""; + } + QString sCurrTable = (sTable.isEmpty() ? m_sTable : sTable); + sCurrTable.replace(".", "_"); + return (sCurrTable + "." + m_sColumn); + } + + QString QxSoftDelete::buildSqlQueryToFetch(const QString &sTable /* = QString() */) const + { + QString sCurrTable = (sTable.isEmpty() ? m_sTable : sTable); + sCurrTable.replace(".", "_"); + if (this->isEmpty()) + { + return ""; + } + else if (!m_sSqlQueryToFetch.isEmpty()) + { + return m_sSqlQueryToFetch; + } + else if (m_eMode == QxSoftDelete::mode_flag) + { + return ("(" + sCurrTable + "." + m_sColumn + " IS NULL" + " OR " + sCurrTable + "." + m_sColumn + " = ''" + " OR " + sCurrTable + "." + m_sColumn + " = '0'" + ")"); + } + else if (m_eMode == QxSoftDelete::mode_date_time) + { + return ("(" + sCurrTable + "." + m_sColumn + " IS NULL" + " OR " + sCurrTable + "." + m_sColumn + " = ''" + ")"); + } + qAssert(false); + return ""; + } + + QString QxSoftDelete::buildSqlQueryToUpdate() const + { + if (this->isEmpty()) + { + return ""; + } + else if (!m_sSqlQueryToUpdate.isEmpty()) + { + return m_sSqlQueryToUpdate; + } + else if (m_eMode == QxSoftDelete::mode_flag) + { + return (m_sColumn + " = '1'"); + } + else if (m_eMode == QxSoftDelete::mode_date_time) + { + return (m_sColumn + " = '" + QDateTime::currentDateTime().toString(QX_DAO_SOFT_DELETE_QDATETIME_FORMAT) + "'"); + } + qAssert(false); + return ""; + } + + QString QxSoftDelete::buildSqlQueryToCreateTable() const + { + if (this->isEmpty()) + { + return ""; + } + else if (!m_sSqlQueryToCreateTable.isEmpty()) + { + return m_sSqlQueryToCreateTable; + } + else if (m_eMode == QxSoftDelete::mode_flag) + { + return (m_sColumn + " " + "TEXT"); + } + else if (m_eMode == QxSoftDelete::mode_date_time) + { + return (m_sColumn + " " + "TEXT"); + } + qAssert(false); + return ""; + } + +} // namespace qx diff --git a/src/QxDao/QxSqlDatabase.cpp b/src/QxDao/QxSqlDatabase.cpp new file mode 100644 index 0000000..747b1e5 --- /dev/null +++ b/src/QxDao/QxSqlDatabase.cpp @@ -0,0 +1,1298 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +#include + +#ifdef _QX_ENABLE_MONGODB +#include +#endif // _QX_ENABLE_MONGODB + +#include + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) +#define QX_CONSTRUCT_QX_SQL_DATABASE_MUTEX() m_oDbMutex() +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) +#define QX_CONSTRUCT_QX_SQL_DATABASE_MUTEX() m_oDbMutex(QMutex::Recursive) +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + +#define QX_CONSTRUCT_QX_SQL_DATABASE() \ + QX_CONSTRUCT_QX_SQL_DATABASE_MUTEX(), m_iPort(-1), m_bTraceSqlQuery(true), m_bTraceSqlRecord(false), \ + m_bTraceSqlBoundValues(false), m_bTraceSqlBoundValuesOnError(true), \ + m_ePlaceHolderStyle(QxSqlDatabase::ph_style_2_point_name), m_bSessionThrowable(false), \ + m_bSessionAutoTransaction(true), m_bValidatorThrowable(false), \ + m_bAutoReplaceSqlAliasIntoQuery(true), m_bVerifyOffsetRelation(false), \ + m_bAddAutoIncrementIdToUpdateQuery(true), m_bForceParentIdToAllChildren(false), \ + m_bAddSqlSquareBracketsForTableName(false), m_bAddSqlSquareBracketsForColumnName(false), \ + m_bFormatSqlQueryBeforeLogging(false), m_iTraceSqlOnlySlowQueriesDatabase(-1), m_iTraceSqlOnlySlowQueriesTotal(-1), \ + m_bDisplayTimerDetails(false) + +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::QxSqlDatabase) + +namespace qx +{ + + struct Q_DECL_HIDDEN QxSqlDatabase::QxSqlDatabaseImpl + { + + QxSqlDatabase *m_pParent; //!< Parent instance of the private implementation idiom + QHash m_lstDbByThread; //!< Collection of databases connexions by thread id +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QRecursiveMutex m_oDbMutex; //!< Mutex => 'QxSqlDatabase' is thread-safe +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QMutex m_oDbMutex; //!< Mutex => 'QxSqlDatabase' is thread-safe +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QString m_sDriverName; //!< Driver name to connect to database + QString m_sConnectOptions; //!< Connect options to database + QString m_sDatabaseName; //!< Database name + QString m_sUserName; //!< Connection's user name + QString m_sPassword; //!< Connection's password + QString m_sHostName; //!< Connection's host name + int m_iPort; //!< Connection's port number + bool m_bTraceSqlQuery; //!< Trace each sql query executed + bool m_bTraceSqlRecord; //!< Trace each sql record + bool m_bTraceSqlBoundValues; //!< Trace sql bound values + bool m_bTraceSqlBoundValuesOnError; //!< Trace sql bound values (only when an error occurred) + QxSqlDatabase::ph_style m_ePlaceHolderStyle; //!< Place holder style to build sql query + bool m_bSessionThrowable; //!< An exception of type qx::dao::sql_error is thrown when a SQL error is appended to qx::QxSession object + bool m_bSessionAutoTransaction; //!< A transaction is automatically beginned when a qx::QxSession object is instantiated + bool m_bValidatorThrowable; //!< An exception of type qx::validator_error is thrown when invalid values are detected inserting or updating an element into database + qx::dao::detail::IxSqlGenerator_ptr m_pSqlGenerator; //!< SQL generator to build SQL query specific for each database + bool m_bAutoReplaceSqlAliasIntoQuery; //!< Replace all sql alias into sql query automatically + bool m_bVerifyOffsetRelation; //!< Only for debug purpose : assert if invalid offset detected fetching a relation + bool m_bAddAutoIncrementIdToUpdateQuery; //!< For Microsoft SqlServer database compatibility : add or not auto-increment id to SQL update query + bool m_bForceParentIdToAllChildren; //!< Force parent id to all children (for 1-n relationship for example) + QxSqlDatabase::type_fct_db_open m_fctDatabaseOpen; //!< Callback function called when a new database connection is opened (can be used for example with SQLite database to define some PRAGMAs before executing any SQL query) + bool m_bAddSqlSquareBracketsForTableName; //!< Add square brackets [] for table name in SQL queries (to support specific database keywords) + bool m_bAddSqlSquareBracketsForColumnName; //!< Add square brackets [] for column name in SQL queries (to support specific database keywords) + bool m_bFormatSqlQueryBeforeLogging; //!< Format SQL query (pretty-printing) before logging it (can be customized creating a qx::dao::detail::IxSqlGenerator sub-class) + QStringList m_lstSqlDelimiterForTableName; //!< Add delimiter characters for table name in SQL queries (to support specific database keywords) : for example, use ` for MySQL database + QStringList m_lstSqlDelimiterForColumnName; //!< Add delimiter characters for column name in SQL queries (to support specific database keywords) : for example, use ` for MySQL database + QStringList m_lstSqlDelimiterForTableNameAlias; //!< Add delimiter characters for table name alias in SQL queries (to support specific database keywords) : for example, use ` for MySQL database + QStringList m_lstSqlDelimiterForColumnNameAlias; //!< Add delimiter characters for column name alias in SQL queries (to support specific database keywords) : for example, use ` for MySQL database + int m_iTraceSqlOnlySlowQueriesDatabase; //!< Trace only slow sql queries (database execution time only, in milliseconds) + int m_iTraceSqlOnlySlowQueriesTotal; //!< Trace only slow sql queries (database execution time + C++ qx::dao execution time, in milliseconds) + bool m_bDisplayTimerDetails; //!< Display in logs all timers details (exec(), next(), prepare(), open(), etc...) + + QHash, QVariant> m_lstSettingsByThread; //!< List of settings per thread (override global settings) : can be useful to use different databases per thread (see bJustForCurrentThread parameter in all setXXXX() methods) + QHash m_lstGeneratorByThread; //!< List of SQL generator per thread (override global settings) : can be useful to use different databases per thread (see bJustForCurrentThread parameter in setSqlGenerator() method) + QHash, QVariant> m_lstSettingsByDatabase; //!< List of settings per database (override global settings and thread settings) : can be useful to use different databases in a same thread (see pJustForThisDatabase parameter in all setXXXX() method) + QHash m_lstGeneratorByDatabase; //!< List of SQL generator per database (override global settings and thread settings) : can be useful to use different databases in a same thread (see pJustForThisDatabase parameter in setSqlGenerator() method) + QHash m_lstCurrDatabaseKeyByThread; //!< Current database key (key = driverName + hostName + databaseName + connectionName) used per thread : this list is filled by each qx::dao::detail::IxDao_Helper instance using RAII + + QxSqlDatabaseImpl(QxSqlDatabase *p) : m_pParent(p), QX_CONSTRUCT_QX_SQL_DATABASE() { ; } + ~QxSqlDatabaseImpl() { ; } + + QSqlDatabase getDatabaseByCurrThreadId(QSqlError &dbError); + QSqlDatabase createDatabase(QSqlError &dbError); + + void displayLastError(const QSqlDatabase &db, const QString &sDesc) const; + QString formatLastError(const QSqlDatabase &db) const; + + bool isValid() const { return (!m_sDriverName.isEmpty() && !m_sDatabaseName.isEmpty()); } + QString computeDatabaseKey(QSqlDatabase *p) const { return (p ? (p->driverName() + p->hostName() + p->databaseName()) : (m_sDriverName + m_sHostName + m_sDatabaseName)); } + + QVariant getSetting(const QString &key) const + { + if (m_lstSettingsByDatabase.count() > 0) + { + Qt::HANDLE lCurrThreadId = QThread::currentThreadId(); + QString sDatabaseKey = (m_lstCurrDatabaseKeyByThread.contains(lCurrThreadId) ? m_lstCurrDatabaseKeyByThread.value(lCurrThreadId) : QString()); + QPair pairDatabase = qMakePair(sDatabaseKey, key); + if (m_lstSettingsByDatabase.contains(pairDatabase)) + { + return m_lstSettingsByDatabase.value(pairDatabase); + } + } + if (m_lstSettingsByThread.count() > 0) + { + Qt::HANDLE lCurrThreadId = QThread::currentThreadId(); + QPair pairThread = qMakePair(lCurrThreadId, key); + if (m_lstSettingsByThread.contains(pairThread)) + { + return m_lstSettingsByThread.value(pairThread); + } + } + return QVariant(); + } + + bool setSetting(const QString &key, const QVariant &val, bool bJustForCurrentThread, QSqlDatabase *pJustForThisDatabase) + { + bool bUpdateGlobal = true; + if (bJustForCurrentThread) + { + QMutexLocker locker(&m_oDbMutex); + Qt::HANDLE lCurrThreadId = QThread::currentThreadId(); + QPair pairThread = qMakePair(lCurrThreadId, key); + m_lstSettingsByThread.insert(pairThread, val); + bUpdateGlobal = false; + } + if (pJustForThisDatabase != NULL) + { + QMutexLocker locker(&m_oDbMutex); + QString sDatabaseKey = computeDatabaseKey(pJustForThisDatabase); + if (sDatabaseKey.isEmpty()) + { + qDebug("[QxOrm] qx::QxSqlDatabase::setSetting() : database parameters are empty ==> cannot add setting database '%s'", qPrintable(key)); + qAssert(false); + return false; + } + QPair pairDatabase = qMakePair(sDatabaseKey, key); + m_lstSettingsByDatabase.insert(pairDatabase, val); + bUpdateGlobal = false; + } + return bUpdateGlobal; + } + }; + + QxSqlDatabase::QxSqlDatabase() : QxSingleton("qx::QxSqlDatabase"), m_pImpl(new QxSqlDatabaseImpl(this)) { ; } + + QxSqlDatabase::~QxSqlDatabase() { ; } + + QString QxSqlDatabase::getDriverName() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_sDriverName; + } + QVariant setting = m_pImpl->getSetting("DriverName"); + if (!setting.isNull()) + { + return setting.toString(); + } + return m_pImpl->m_sDriverName; + } + + QString QxSqlDatabase::getConnectOptions() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_sConnectOptions; + } + QVariant setting = m_pImpl->getSetting("ConnectOptions"); + if (!setting.isNull()) + { + return setting.toString(); + } + return m_pImpl->m_sConnectOptions; + } + + QString QxSqlDatabase::getDatabaseName() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_sDatabaseName; + } + QVariant setting = m_pImpl->getSetting("DatabaseName"); + if (!setting.isNull()) + { + return setting.toString(); + } + return m_pImpl->m_sDatabaseName; + } + + QString QxSqlDatabase::getUserName() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_sUserName; + } + QVariant setting = m_pImpl->getSetting("UserName"); + if (!setting.isNull()) + { + return setting.toString(); + } + return m_pImpl->m_sUserName; + } + + QString QxSqlDatabase::getPassword() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_sPassword; + } + QVariant setting = m_pImpl->getSetting("Password"); + if (!setting.isNull()) + { + return setting.toString(); + } + return m_pImpl->m_sPassword; + } + + QString QxSqlDatabase::getHostName() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_sHostName; + } + QVariant setting = m_pImpl->getSetting("HostName"); + if (!setting.isNull()) + { + return setting.toString(); + } + return m_pImpl->m_sHostName; + } + + int QxSqlDatabase::getPort() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_iPort; + } + QVariant setting = m_pImpl->getSetting("Port"); + if (!setting.isNull()) + { + return setting.toInt(); + } + return m_pImpl->m_iPort; + } + + bool QxSqlDatabase::getTraceSqlQuery() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_bTraceSqlQuery; + } + QVariant setting = m_pImpl->getSetting("TraceSqlQuery"); + if (!setting.isNull()) + { + return setting.toBool(); + } + return m_pImpl->m_bTraceSqlQuery; + } + + bool QxSqlDatabase::getTraceSqlRecord() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_bTraceSqlRecord; + } + QVariant setting = m_pImpl->getSetting("TraceSqlRecord"); + if (!setting.isNull()) + { + return setting.toBool(); + } + return m_pImpl->m_bTraceSqlRecord; + } + + bool QxSqlDatabase::getTraceSqlBoundValues() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_bTraceSqlBoundValues; + } + QVariant setting = m_pImpl->getSetting("TraceSqlBoundValues"); + if (!setting.isNull()) + { + return setting.toBool(); + } + return m_pImpl->m_bTraceSqlBoundValues; + } + + bool QxSqlDatabase::getTraceSqlBoundValuesOnError() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_bTraceSqlBoundValuesOnError; + } + QVariant setting = m_pImpl->getSetting("TraceSqlBoundValuesOnError"); + if (!setting.isNull()) + { + return setting.toBool(); + } + return m_pImpl->m_bTraceSqlBoundValuesOnError; + } + + QxSqlDatabase::ph_style QxSqlDatabase::getSqlPlaceHolderStyle() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_ePlaceHolderStyle; + } + QVariant setting = m_pImpl->getSetting("SqlPlaceHolderStyle"); + if (!setting.isNull()) + { + return static_cast(setting.toInt()); + } + return m_pImpl->m_ePlaceHolderStyle; + } + + bool QxSqlDatabase::getSessionThrowable() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_bSessionThrowable; + } + QVariant setting = m_pImpl->getSetting("SessionThrowable"); + if (!setting.isNull()) + { + return setting.toBool(); + } + return m_pImpl->m_bSessionThrowable; + } + + bool QxSqlDatabase::getSessionAutoTransaction() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_bSessionAutoTransaction; + } + QVariant setting = m_pImpl->getSetting("SessionAutoTransaction"); + if (!setting.isNull()) + { + return setting.toBool(); + } + return m_pImpl->m_bSessionAutoTransaction; + } + + bool QxSqlDatabase::getValidatorThrowable() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_bValidatorThrowable; + } + QVariant setting = m_pImpl->getSetting("ValidatorThrowable"); + if (!setting.isNull()) + { + return setting.toBool(); + } + return m_pImpl->m_bValidatorThrowable; + } + + bool QxSqlDatabase::getAutoReplaceSqlAliasIntoQuery() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_bAutoReplaceSqlAliasIntoQuery; + } + QVariant setting = m_pImpl->getSetting("AutoReplaceSqlAliasIntoQuery"); + if (!setting.isNull()) + { + return setting.toBool(); + } + return m_pImpl->m_bAutoReplaceSqlAliasIntoQuery; + } + + bool QxSqlDatabase::getVerifyOffsetRelation() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_bVerifyOffsetRelation; + } + QVariant setting = m_pImpl->getSetting("VerifyOffsetRelation"); + if (!setting.isNull()) + { + return setting.toBool(); + } + return m_pImpl->m_bVerifyOffsetRelation; + } + + bool QxSqlDatabase::getAddAutoIncrementIdToUpdateQuery() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_bAddAutoIncrementIdToUpdateQuery; + } + QVariant setting = m_pImpl->getSetting("AddAutoIncrementIdToUpdateQuery"); + if (!setting.isNull()) + { + return setting.toBool(); + } + return m_pImpl->m_bAddAutoIncrementIdToUpdateQuery; + } + + bool QxSqlDatabase::getForceParentIdToAllChildren() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_bForceParentIdToAllChildren; + } + QVariant setting = m_pImpl->getSetting("ForceParentIdToAllChildren"); + if (!setting.isNull()) + { + return setting.toBool(); + } + return m_pImpl->m_bForceParentIdToAllChildren; + } + + QxSqlDatabase::type_fct_db_open QxSqlDatabase::getFctDatabaseOpen() const + { + return m_pImpl->m_fctDatabaseOpen; + } + + bool QxSqlDatabase::getAddSqlSquareBracketsForTableName() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_bAddSqlSquareBracketsForTableName; + } + QVariant setting = m_pImpl->getSetting("AddSqlSquareBracketsForTableName"); + if (!setting.isNull()) + { + return setting.toBool(); + } + return m_pImpl->m_bAddSqlSquareBracketsForTableName; + } + + bool QxSqlDatabase::getAddSqlSquareBracketsForColumnName() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_bAddSqlSquareBracketsForColumnName; + } + QVariant setting = m_pImpl->getSetting("AddSqlSquareBracketsForColumnName"); + if (!setting.isNull()) + { + return setting.toBool(); + } + return m_pImpl->m_bAddSqlSquareBracketsForColumnName; + } + + bool QxSqlDatabase::getFormatSqlQueryBeforeLogging() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_bFormatSqlQueryBeforeLogging; + } + QVariant setting = m_pImpl->getSetting("FormatSqlQueryBeforeLogging"); + if (!setting.isNull()) + { + return setting.toBool(); + } + return m_pImpl->m_bFormatSqlQueryBeforeLogging; + } + + QStringList QxSqlDatabase::getSqlDelimiterForTableName() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_lstSqlDelimiterForTableName; + } + QVariant setting = m_pImpl->getSetting("SqlDelimiterForTableName"); + if (!setting.isNull()) + { + return setting.toStringList(); + } + return m_pImpl->m_lstSqlDelimiterForTableName; + } + + QStringList QxSqlDatabase::getSqlDelimiterForColumnName() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_lstSqlDelimiterForColumnName; + } + QVariant setting = m_pImpl->getSetting("SqlDelimiterForColumnName"); + if (!setting.isNull()) + { + return setting.toStringList(); + } + return m_pImpl->m_lstSqlDelimiterForColumnName; + } + + QStringList QxSqlDatabase::getSqlDelimiterForTableNameAlias() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_lstSqlDelimiterForTableNameAlias; + } + QVariant setting = m_pImpl->getSetting("SqlDelimiterForTableNameAlias"); + if (!setting.isNull()) + { + return setting.toStringList(); + } + return m_pImpl->m_lstSqlDelimiterForTableNameAlias; + } + + QStringList QxSqlDatabase::getSqlDelimiterForColumnNameAlias() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_lstSqlDelimiterForColumnNameAlias; + } + QVariant setting = m_pImpl->getSetting("SqlDelimiterForColumnNameAlias"); + if (!setting.isNull()) + { + return setting.toStringList(); + } + return m_pImpl->m_lstSqlDelimiterForColumnNameAlias; + } + + int QxSqlDatabase::getTraceSqlOnlySlowQueriesDatabase() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_iTraceSqlOnlySlowQueriesDatabase; + } + QVariant setting = m_pImpl->getSetting("TraceSqlOnlySlowQueriesDatabase"); + if (!setting.isNull()) + { + return setting.toInt(); + } + return m_pImpl->m_iTraceSqlOnlySlowQueriesDatabase; + } + + int QxSqlDatabase::getTraceSqlOnlySlowQueriesTotal() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_iTraceSqlOnlySlowQueriesTotal; + } + QVariant setting = m_pImpl->getSetting("TraceSqlOnlySlowQueriesTotal"); + if (!setting.isNull()) + { + return setting.toInt(); + } + return m_pImpl->m_iTraceSqlOnlySlowQueriesTotal; + } + + bool QxSqlDatabase::getDisplayTimerDetails() const + { + if ((m_pImpl->m_lstSettingsByThread.count() <= 0) && (m_pImpl->m_lstSettingsByDatabase.count() <= 0)) + { + return m_pImpl->m_bDisplayTimerDetails; + } + QVariant setting = m_pImpl->getSetting("DisplayTimerDetails"); + if (!setting.isNull()) + { + return setting.toBool(); + } + return m_pImpl->m_bDisplayTimerDetails; + } + + void QxSqlDatabase::setDriverName(const QString &s, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("DriverName", s, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_sDriverName = s; + } + getSqlGenerator(); + } + + void QxSqlDatabase::setConnectOptions(const QString &s, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("ConnectOptions", s, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_sConnectOptions = s; + } + } + + void QxSqlDatabase::setDatabaseName(const QString &s, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("DatabaseName", s, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_sDatabaseName = s; + } + } + + void QxSqlDatabase::setUserName(const QString &s, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("UserName", s, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_sUserName = s; + } + } + + void QxSqlDatabase::setPassword(const QString &s, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("Password", s, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_sPassword = s; + } + } + + void QxSqlDatabase::setHostName(const QString &s, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("HostName", s, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_sHostName = s; + } + } + + void QxSqlDatabase::setPort(int i, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("Port", i, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_iPort = i; + } + } + + void QxSqlDatabase::setTraceSqlQuery(bool b, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("TraceSqlQuery", b, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_bTraceSqlQuery = b; + } + } + + void QxSqlDatabase::setTraceSqlRecord(bool b, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("TraceSqlRecord", b, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_bTraceSqlRecord = b; + } + } + + void QxSqlDatabase::setTraceSqlBoundValues(bool b, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("TraceSqlBoundValues", b, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_bTraceSqlBoundValues = b; + } + } + + void QxSqlDatabase::setTraceSqlBoundValuesOnError(bool b, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("TraceSqlBoundValuesOnError", b, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_bTraceSqlBoundValuesOnError = b; + } + } + + void QxSqlDatabase::setSqlPlaceHolderStyle(QxSqlDatabase::ph_style e, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + int i = static_cast(e); + bool bUpdateGlobal = m_pImpl->setSetting("SqlPlaceHolderStyle", i, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_ePlaceHolderStyle = e; + } + } + + void QxSqlDatabase::setSessionThrowable(bool b, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("SessionThrowable", b, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_bSessionThrowable = b; + } + } + + void QxSqlDatabase::setSessionAutoTransaction(bool b, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("SessionAutoTransaction", b, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_bSessionAutoTransaction = b; + } + } + + void QxSqlDatabase::setValidatorThrowable(bool b, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("ValidatorThrowable", b, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_bValidatorThrowable = b; + } + } + + void QxSqlDatabase::setSqlGenerator(qx::dao::detail::IxSqlGenerator_ptr p, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = true; + if (p && bJustForCurrentThread) + { + QMutexLocker locker(&m_pImpl->m_oDbMutex); + Qt::HANDLE lCurrThreadId = QThread::currentThreadId(); + m_pImpl->m_lstGeneratorByThread.insert(lCurrThreadId, p); + bUpdateGlobal = false; + } + if (p && (pJustForThisDatabase != NULL)) + { + QMutexLocker locker(&m_pImpl->m_oDbMutex); + QString sDatabaseKey = m_pImpl->computeDatabaseKey(pJustForThisDatabase); + if (sDatabaseKey.isEmpty()) + { + qDebug("[QxOrm] qx::QxSqlDatabase::setSqlGenerator() : database parameters are empty ==> cannot add setting database '%s'", "SqlGenerator"); + qAssert(false); + return; + } + m_pImpl->m_lstGeneratorByDatabase.insert(sDatabaseKey, p); + bUpdateGlobal = false; + } + + if (bUpdateGlobal) + { + m_pImpl->m_pSqlGenerator = p; + } + if (p) + { + p->init(); + } + } + + void QxSqlDatabase::setAutoReplaceSqlAliasIntoQuery(bool b, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("AutoReplaceSqlAliasIntoQuery", b, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_bAutoReplaceSqlAliasIntoQuery = b; + } + } + + void QxSqlDatabase::setVerifyOffsetRelation(bool b, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("VerifyOffsetRelation", b, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_bVerifyOffsetRelation = b; + } + } + + void QxSqlDatabase::setAddAutoIncrementIdToUpdateQuery(bool b, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("AddAutoIncrementIdToUpdateQuery", b, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_bAddAutoIncrementIdToUpdateQuery = b; + } + } + + void QxSqlDatabase::setForceParentIdToAllChildren(bool b, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("ForceParentIdToAllChildren", b, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_bForceParentIdToAllChildren = b; + } + } + + void QxSqlDatabase::setFctDatabaseOpen(QxSqlDatabase::type_fct_db_open fct, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + Q_UNUSED(bJustForCurrentThread); + Q_UNUSED(pJustForThisDatabase); + m_pImpl->m_fctDatabaseOpen = fct; + } + + void QxSqlDatabase::setAddSqlSquareBracketsForTableName(bool b, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("AddSqlSquareBracketsForTableName", b, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_bAddSqlSquareBracketsForTableName = b; + } + } + + void QxSqlDatabase::setAddSqlSquareBracketsForColumnName(bool b, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("AddSqlSquareBracketsForColumnName", b, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_bAddSqlSquareBracketsForColumnName = b; + } + } + + void QxSqlDatabase::setFormatSqlQueryBeforeLogging(bool b, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("FormatSqlQueryBeforeLogging", b, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_bFormatSqlQueryBeforeLogging = b; + } + } + + void QxSqlDatabase::setSqlDelimiterForTableName(const QStringList &lst, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("SqlDelimiterForTableName", lst, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_lstSqlDelimiterForTableName = lst; + } + } + + void QxSqlDatabase::setSqlDelimiterForColumnName(const QStringList &lst, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("SqlDelimiterForColumnName", lst, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_lstSqlDelimiterForColumnName = lst; + } + } + + void QxSqlDatabase::setSqlDelimiterForTableNameAlias(const QStringList &lst, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("SqlDelimiterForTableNameAlias", lst, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_lstSqlDelimiterForTableNameAlias = lst; + } + } + + void QxSqlDatabase::setSqlDelimiterForColumnNameAlias(const QStringList &lst, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("SqlDelimiterForColumnNameAlias", lst, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_lstSqlDelimiterForColumnNameAlias = lst; + } + } + + void QxSqlDatabase::setTraceSqlOnlySlowQueriesDatabase(int i, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("TraceSqlOnlySlowQueriesDatabase", i, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_iTraceSqlOnlySlowQueriesDatabase = i; + } + } + + void QxSqlDatabase::setTraceSqlOnlySlowQueriesTotal(int i, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("TraceSqlOnlySlowQueriesTotal", i, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_iTraceSqlOnlySlowQueriesTotal = i; + } + } + + void QxSqlDatabase::setDisplayTimerDetails(bool b, bool bJustForCurrentThread /* = false */, QSqlDatabase *pJustForThisDatabase /* = NULL */) + { + bool bUpdateGlobal = m_pImpl->setSetting("DisplayTimerDetails", b, bJustForCurrentThread, pJustForThisDatabase); + if (bUpdateGlobal) + { + m_pImpl->m_bDisplayTimerDetails = b; + } + } + + QSqlDatabase QxSqlDatabase::getDatabase(QSqlError &dbError) { return QxSqlDatabase::getSingleton()->m_pImpl->getDatabaseByCurrThreadId(dbError); } + + QSqlDatabase QxSqlDatabase::getDatabase() + { + QSqlError dbError; + Q_UNUSED(dbError); + return QxSqlDatabase::getDatabase(dbError); + } + + QSqlDatabase QxSqlDatabase::getDatabaseCloned() + { + QSqlError dbError; + Q_UNUSED(dbError); + QString sKeyClone = QUuid::createUuid().toString(); + return QSqlDatabase::cloneDatabase(QxSqlDatabase::getDatabase(dbError), sKeyClone); + } + + QSqlDatabase QxSqlDatabase::checkDatabaseByThread() + { + QxSqlDatabase::QxSqlDatabaseImpl *pImpl = QxSqlDatabase::getSingleton()->m_pImpl.get(); + QMutexLocker locker(&pImpl->m_oDbMutex); + Qt::HANDLE lCurrThreadId = QThread::currentThreadId(); + if (!pImpl->m_lstDbByThread.contains(lCurrThreadId)) + { + return QSqlDatabase(); + } + QString sDbKey = pImpl->m_lstDbByThread.value(lCurrThreadId); + if (!QSqlDatabase::contains(sDbKey)) + { + return QSqlDatabase(); + } + return QSqlDatabase::database(sDbKey); + } + + void QxSqlDatabase::removeDatabaseByThread() + { + QxSqlDatabase::QxSqlDatabaseImpl *pImpl = QxSqlDatabase::getSingleton()->m_pImpl.get(); + QMutexLocker locker(&pImpl->m_oDbMutex); + Qt::HANDLE lCurrThreadId = QThread::currentThreadId(); + if (!pImpl->m_lstDbByThread.contains(lCurrThreadId)) + { + return; + } + QString sDbKey = pImpl->m_lstDbByThread.value(lCurrThreadId); + if (!QSqlDatabase::contains(sDbKey)) + { + return; + } + QSqlDatabase::database(sDbKey).close(); + QSqlDatabase::removeDatabase(sDbKey); + pImpl->m_lstDbByThread.remove(lCurrThreadId); + } + + QSqlDatabase QxSqlDatabase::QxSqlDatabaseImpl::getDatabaseByCurrThreadId(QSqlError &dbError) + { + QMutexLocker locker(&m_oDbMutex); + dbError = QSqlError(); + + if (!isValid()) + { + qDebug("[QxOrm] qx::QxSqlDatabase : '%s'", "parameters are not valid"); + dbError = QSqlError("[QxOrm] qx::QxSqlDatabase : 'parameters are not valid'", "", QSqlError::UnknownError); + qAssert(false); + return QSqlDatabase(); + } + + Qt::HANDLE lCurrThreadId = QThread::currentThreadId(); + if (!lCurrThreadId) + { + qDebug("[QxOrm] qx::QxSqlDatabase : '%s'", "unable to find current thread id"); + dbError = QSqlError("[QxOrm] qx::QxSqlDatabase : 'unable to find current thread id'", "", QSqlError::UnknownError); + qAssert(false); + return QSqlDatabase(); + } + + if (!m_lstDbByThread.contains(lCurrThreadId)) + { + return createDatabase(dbError); + } + QString sDbKey = m_lstDbByThread.value(lCurrThreadId); + if (!QSqlDatabase::contains(sDbKey)) + { + return createDatabase(dbError); + } + QSqlDatabase db = QSqlDatabase::database(sDbKey); + if (!db.isValid()) + { + return createDatabase(dbError); + } + if (db.driver() && (db.driver()->thread() != QThread::currentThread())) + { + return createDatabase(dbError); + } + return db; + } + + namespace helper + { + + template + struct CvtQtHandle + { + static QString toString(T t) { return QString::number(static_cast(t)); } + }; + + template + struct CvtQtHandle + { + static QString toString(T t) + { + const void *ptr = static_cast(t); + QString value; + QTextStream stream(&value); + stream << ptr; + return value; + } + }; + + } // namespace helper + + QSqlDatabase QxSqlDatabase::QxSqlDatabaseImpl::createDatabase(QSqlError &dbError) + { + Qt::HANDLE lCurrThreadId = QThread::currentThreadId(); + QString sCurrThreadId = qx::helper::CvtQtHandle::value>::toString(lCurrThreadId); + QString sDbKeyNew = QUuid::createUuid().toString(); + dbError = QSqlError(); + bool bError = false; + + { + QSqlDatabase db = QSqlDatabase::addDatabase(m_pParent->getDriverName(), sDbKeyNew); + db.setConnectOptions(m_pParent->getConnectOptions()); + db.setDatabaseName(m_pParent->getDatabaseName()); + db.setUserName(m_pParent->getUserName()); + db.setPassword(m_pParent->getPassword()); + db.setHostName(m_pParent->getHostName()); + if (m_pParent->getPort() != -1) + { + db.setPort(m_pParent->getPort()); + } + if (!db.open()) + { + displayLastError(db, "unable to open connection to database"); + bError = true; + } + if (bError) + { + dbError = db.lastError(); + } + if (bError && !dbError.isValid()) + { + dbError = QSqlError("[QxOrm] qx::QxSqlDatabase : 'unable to open connection to database'", "", QSqlError::UnknownError); + } + } + + if (bError) + { + QSqlDatabase::removeDatabase(sDbKeyNew); + return QSqlDatabase(); + } + m_lstDbByThread.insert(lCurrThreadId, sDbKeyNew); + qDebug("[QxOrm] qx::QxSqlDatabase : create new database connection in thread '%s' with key '%s'", qPrintable(sCurrThreadId), qPrintable(sDbKeyNew)); + QSqlDatabase dbconn = QSqlDatabase::database(sDbKeyNew); + if (m_fctDatabaseOpen) + { + m_fctDatabaseOpen(dbconn); + } + return dbconn; + } + + void QxSqlDatabase::QxSqlDatabaseImpl::displayLastError(const QSqlDatabase &db, const QString &sDesc) const + { + QString sLastError = formatLastError(db); + if (sDesc.isEmpty()) + { + qDebug("[QxOrm] qx::QxSqlDatabase : '%s'", qPrintable(sLastError)); + } + else + { + qDebug("[QxOrm] qx::QxSqlDatabase : '%s'\n%s", qPrintable(sDesc), qPrintable(sLastError)); + } + } + + QString QxSqlDatabase::QxSqlDatabaseImpl::formatLastError(const QSqlDatabase &db) const + { + QString sLastError; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + if (!db.lastError().nativeErrorCode().isEmpty()) + { + sLastError += QString("Error number '") + db.lastError().nativeErrorCode() + QString("' : "); + } +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + if (db.lastError().number() != -1) + { + sLastError += QString("Error number '") + QString::number(db.lastError().number()) + QString("' : "); + } +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + if (!db.lastError().text().isEmpty()) + { + sLastError += db.lastError().text(); + } + else + { + sLastError += ""; + } + return sLastError; + } + + qx::dao::detail::IxSqlGenerator *QxSqlDatabase::getSqlGenerator() + { + if (m_pImpl->m_lstGeneratorByDatabase.count() > 0) + { + Qt::HANDLE lCurrThreadId = QThread::currentThreadId(); + QString sDatabaseKey = (m_pImpl->m_lstCurrDatabaseKeyByThread.contains(lCurrThreadId) ? m_pImpl->m_lstCurrDatabaseKeyByThread.value(lCurrThreadId) : QString()); + if (m_pImpl->m_lstGeneratorByDatabase.contains(sDatabaseKey)) + { + return m_pImpl->m_lstGeneratorByDatabase.value(sDatabaseKey).get(); + } + } + if (m_pImpl->m_lstGeneratorByThread.count() > 0) + { + Qt::HANDLE lCurrThreadId = QThread::currentThreadId(); + if (m_pImpl->m_lstGeneratorByThread.contains(lCurrThreadId)) + { + return m_pImpl->m_lstGeneratorByThread.value(lCurrThreadId).get(); + } + } + + if (m_pImpl->m_pSqlGenerator) + { + return m_pImpl->m_pSqlGenerator.get(); + } + QMutexLocker locker(&m_pImpl->m_oDbMutex); + + if (m_pImpl->m_sDriverName == "QMYSQL") + { + m_pImpl->m_pSqlGenerator = std::make_shared(); + } + else if (m_pImpl->m_sDriverName == "QPSQL") + { + m_pImpl->m_pSqlGenerator = std::make_shared(); + } + else if (m_pImpl->m_sDriverName == "QSQLITE") + { + m_pImpl->m_pSqlGenerator = std::make_shared(); + } + else if (m_pImpl->m_sDriverName == "QOCI") + { + m_pImpl->m_pSqlGenerator = std::make_shared(); + } + + if (!m_pImpl->m_pSqlGenerator) + { + m_pImpl->m_pSqlGenerator = std::make_shared(); + } + m_pImpl->m_pSqlGenerator->init(); + return m_pImpl->m_pSqlGenerator.get(); + } + + void QxSqlDatabase::closeAllDatabases() + { + qx::QxSqlDatabase *pSingleton = qx::QxSqlDatabase::getSingleton(); + if (!pSingleton) + { + qAssert(false); + return; + } + QMutexLocker locker(&pSingleton->m_pImpl->m_oDbMutex); + + Q_FOREACH (QString sDbKey, pSingleton->m_pImpl->m_lstDbByThread) + { + QSqlDatabase::database(sDbKey).close(); + QSqlDatabase::removeDatabase(sDbKey); + } + pSingleton->m_pImpl->m_lstDbByThread.clear(); + +#ifdef _QX_ENABLE_MONGODB + qx::dao::mongodb::QxMongoDB_Helper::clearPoolConnection(); +#endif // _QX_ENABLE_MONGODB + } + + void QxSqlDatabase::clearAllDatabases() + { + qx::QxSqlDatabase *pSingleton = qx::QxSqlDatabase::getSingleton(); + if (!pSingleton) + { + qAssert(false); + return; + } + QMutexLocker locker(&pSingleton->m_pImpl->m_oDbMutex); + + qx::QxSqlDatabase::closeAllDatabases(); + pSingleton->m_pImpl->m_sDriverName = ""; + pSingleton->m_pImpl->m_sConnectOptions = ""; + pSingleton->m_pImpl->m_sDatabaseName = ""; + pSingleton->m_pImpl->m_sUserName = ""; + pSingleton->m_pImpl->m_sPassword = ""; + pSingleton->m_pImpl->m_sHostName = ""; + pSingleton->m_pImpl->m_iPort = -1; + } + + bool QxSqlDatabase::isEmpty() + { + qx::QxSqlDatabase *pSingleton = qx::QxSqlDatabase::getSingleton(); + if (!pSingleton) + { + qAssert(false); + return true; + } + QMutexLocker locker(&pSingleton->m_pImpl->m_oDbMutex); + return pSingleton->m_pImpl->m_lstDbByThread.isEmpty(); + } + + bool QxSqlDatabase::setCurrentDatabaseByThread(QSqlDatabase *p) + { + if (!p) + { + return false; + } + QString sDatabaseKey = m_pImpl->computeDatabaseKey(p); + QString sCurrDatabaseKey = m_pImpl->computeDatabaseKey(NULL); + if (sDatabaseKey.toUpper() == sCurrDatabaseKey.toUpper()) + { + return false; + } + + QMutexLocker locker(&m_pImpl->m_oDbMutex); + Qt::HANDLE lCurrThreadId = QThread::currentThreadId(); + if (!sDatabaseKey.isEmpty() && !m_pImpl->m_lstCurrDatabaseKeyByThread.contains(lCurrThreadId)) + { + m_pImpl->m_lstCurrDatabaseKeyByThread.insert(lCurrThreadId, sDatabaseKey); + return true; + } + return false; + } + + void QxSqlDatabase::clearCurrentDatabaseByThread() + { + if (m_pImpl->m_lstCurrDatabaseKeyByThread.count() <= 0) + { + return; + } + QMutexLocker locker(&m_pImpl->m_oDbMutex); + Qt::HANDLE lCurrThreadId = QThread::currentThreadId(); + m_pImpl->m_lstCurrDatabaseKeyByThread.remove(lCurrThreadId); + } + + void QxSqlDatabase::clearAllSettingsForCurrentThread() + { + QMutexLocker locker(&m_pImpl->m_oDbMutex); + Qt::HANDLE lCurrThreadId = QThread::currentThreadId(); + QMutableHashIterator, QVariant> itr(m_pImpl->m_lstSettingsByThread); + while (itr.hasNext()) + { + itr.next(); + if (itr.key().first == lCurrThreadId) + { + itr.remove(); + } + } + QMutableHashIterator itr2(m_pImpl->m_lstGeneratorByThread); + while (itr2.hasNext()) + { + itr2.next(); + if (itr2.key() == lCurrThreadId) + { + itr2.remove(); + } + } + } + + void QxSqlDatabase::clearAllSettingsForDatabase(QSqlDatabase *p) + { + if (!p) + { + return; + } + QMutexLocker locker(&m_pImpl->m_oDbMutex); + QString sDatabaseKey = m_pImpl->computeDatabaseKey(p); + QMutableHashIterator, QVariant> itr(m_pImpl->m_lstSettingsByDatabase); + while (itr.hasNext()) + { + itr.next(); + if (itr.key().first == sDatabaseKey) + { + itr.remove(); + } + } + QMutableHashIterator itr2(m_pImpl->m_lstGeneratorByDatabase); + while (itr2.hasNext()) + { + itr2.next(); + if (itr2.key() == sDatabaseKey) + { + itr2.remove(); + } + } + } + +} // namespace qx diff --git a/src/QxDao/QxSqlElement/IxSqlElement.cpp b/src/QxDao/QxSqlElement/IxSqlElement.cpp new file mode 100644 index 0000000..2a74603 --- /dev/null +++ b/src/QxDao/QxSqlElement/IxSqlElement.cpp @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + IxSqlElement::IxSqlElement(int index) : m_iIndex(index), m_pSqlGenerator(NULL) + { + m_pSqlGenerator = qx::QxSqlDatabase::getSingleton()->getSqlGenerator(); + qAssert((m_iIndex >= 0) && (m_pSqlGenerator != NULL)); + } + + IxSqlElement::~IxSqlElement() { ; } + + void IxSqlElement::setColumn(const QString &column) + { + m_lstColumns.clear(); + m_lstColumns.append(column); + updateKeys(); + } + + void IxSqlElement::setColumns(const QStringList &columns) + { + m_lstColumns.clear(); + m_lstColumns = columns; + updateKeys(); + } + + void IxSqlElement::setValue(const QVariant &val) + { + m_lstValues.clear(); + m_lstValues.append(val); + } + + void IxSqlElement::setValues(const QVariantList &values) + { + m_lstValues.clear(); + m_lstValues = values; + } + + void IxSqlElement::clone(IxSqlElement *other) + { + if (!other) + { + return; + } + m_lstColumns = other->m_lstColumns; + m_lstValues = other->m_lstValues; + updateKeys(); + } + + void IxSqlElement::updateKeys() + { + m_lstKeys.clear(); + for (int i = 0; i < m_lstColumns.count(); i++) + { + QString sColumn = m_lstColumns.at(i); + QString sKey = sColumn.replace(".", "_") + "_" + QString::number(m_iIndex) + "_" + QString::number(i); + if (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark) + { + sKey = "?"; + } + else if (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_at_name) + { + sKey = QString("@") + sKey; + } + else + { + sKey = QString(":") + sKey; + } + m_lstKeys.append(sKey); + } + } + + IxSqlElement_ptr create_sql_element(IxSqlElement::type_class e) + { + IxSqlElement_ptr p; + switch (e) + { + case IxSqlElement::_sql_compare: + p = std::make_shared(); + break; + case IxSqlElement::_sql_element_temp: + p = std::make_shared(); + break; + case IxSqlElement::_sql_embed_query: + p = std::make_shared(); + break; + case IxSqlElement::_sql_expression: + p = std::make_shared(); + break; + case IxSqlElement::_sql_free_text: + p = std::make_shared(); + break; + case IxSqlElement::_sql_in: + p = std::make_shared(); + break; + case IxSqlElement::_sql_is_between: + p = std::make_shared(); + break; + case IxSqlElement::_sql_is_null: + p = std::make_shared(); + break; + case IxSqlElement::_sql_limit: + p = std::make_shared(); + break; + case IxSqlElement::_sql_sort: + p = std::make_shared(); + break; + default: + qAssert(false); + } + return p; + } + + } // namespace detail + } // namespace dao +} // namespace qx + +QDataStream &operator<<(QDataStream &stream, const qx::dao::detail::IxSqlElement &t) +{ + QString sExtraSettings = t.getExtraSettings(); + stream << t.m_iIndex; + stream << t.m_lstColumns; + stream << t.m_lstKeys; + stream << t.m_lstValues; + stream << sExtraSettings; + return stream; +} + +QDataStream &operator>>(QDataStream &stream, qx::dao::detail::IxSqlElement &t) +{ + QString sExtraSettings; + stream >> t.m_iIndex; + stream >> t.m_lstColumns; + stream >> t.m_lstKeys; + stream >> t.m_lstValues; + stream >> sExtraSettings; + t.setExtraSettings(sExtraSettings); + return stream; +} diff --git a/src/QxDao/QxSqlElement/QxSqlCompare.cpp b/src/QxDao/QxSqlElement/QxSqlCompare.cpp new file mode 100644 index 0000000..6286dc4 --- /dev/null +++ b/src/QxDao/QxSqlElement/QxSqlCompare.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + QxSqlCompare::QxSqlCompare() : IxSqlElement(0), m_type(QxSqlCompare::_is_equal_to) { ; } + + QxSqlCompare::QxSqlCompare(int index, QxSqlCompare::type t, const QString &sCustomOperator /* = QString() */) : IxSqlElement(index), m_type(t), m_sCustomOperator(sCustomOperator) { ; } + + QxSqlCompare::~QxSqlCompare() { ; } + + IxSqlElement::type_class QxSqlCompare::getTypeClass() const { return IxSqlElement::_sql_compare; } + + QString QxSqlCompare::toString() const + { + qAssert((m_lstColumns.count() == 1) && (m_lstKeys.count() == 1)); + QString sReturn, sColumn(m_lstColumns.at(0)), sKey(m_lstKeys.at(0)); + qAssert(!sColumn.isEmpty() && !sKey.isEmpty()); + + switch (m_type) + { + case _is_equal_to: + sReturn = sColumn + " = " + sKey; + break; + case _is_not_equal_to: + sReturn = sColumn + " <> " + sKey; + break; + case _is_greater_than: + sReturn = sColumn + " > " + sKey; + break; + case _is_greater_than_or_equal_to: + sReturn = sColumn + " >= " + sKey; + break; + case _is_less_than: + sReturn = sColumn + " < " + sKey; + break; + case _is_less_than_or_equal_to: + sReturn = sColumn + " <= " + sKey; + break; + case _like: + sReturn = sColumn + " LIKE " + sKey; + break; + case _not_like: + sReturn = sColumn + " NOT LIKE " + sKey; + break; + case _starts_with: + sReturn = sColumn + " LIKE " + sKey; + break; + case _ends_with: + sReturn = sColumn + " LIKE " + sKey; + break; + case _contains_string: + sReturn = sColumn + " LIKE " + sKey; + break; + case _custom_operator: + sReturn = sColumn + " " + m_sCustomOperator + " " + sKey; + break; + case _is_equal_to_select: + sReturn = sColumn + " = (" + sKey + ")"; + break; + case _is_not_equal_to_select: + sReturn = sColumn + " <> (" + sKey + ")"; + break; + default: + qAssert(false); + } + + return sReturn; + } + + void QxSqlCompare::resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch /* = NULL */) const + { + qAssert((m_lstKeys.count() == 1) && (m_lstValues.count() == 1)); + QString sKey(m_lstKeys.at(0)); + QVariant vValue(m_lstValues.at(0)); + + QString sWildCard = m_pSqlGenerator->getWildCard(); + if (m_type == _starts_with) + { + vValue = QVariant(vValue.toString() + sWildCard); + } + else if (m_type == _ends_with) + { + vValue = QVariant(sWildCard + vValue.toString()); + } + else if (m_type == _contains_string) + { + vValue = QVariant(sWildCard + vValue.toString() + sWildCard); + } + + if (pLstExecBatch) + { + if (!pLstExecBatch->exist(sKey)) + { + QVariantList empty; + pLstExecBatch->insert(sKey, empty); + } + QVariantList &values = const_cast(pLstExecBatch->getByKey(sKey)); + values.append(vValue); + } + else + { + bool bQuestionMark = (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark); + if (bQuestionMark) + { + query.addBindValue(vValue); + } + else + { + query.bindValue(sKey, vValue); + } + } + } + + void QxSqlCompare::postProcess(QString &sql) const { Q_UNUSED(sql); } + + QString QxSqlCompare::getExtraSettings() const { return (QString::number(static_cast(m_type)) + "|" + m_sCustomOperator); } + + void QxSqlCompare::setExtraSettings(const QString &s) + { + int iPos = s.indexOf("|"); + if (iPos == -1) + { + m_type = static_cast(s.toInt()); + return; + } + m_type = static_cast(s.left(iPos).toInt()); + m_sCustomOperator = s.right(s.size() - (iPos + 1)); + } + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/src/QxDao/QxSqlElement/QxSqlElementTemp.cpp b/src/QxDao/QxSqlElement/QxSqlElementTemp.cpp new file mode 100644 index 0000000..dd991de --- /dev/null +++ b/src/QxDao/QxSqlElement/QxSqlElementTemp.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + QxSqlElementTemp::QxSqlElementTemp() : IxSqlElement(0) { ; } + + QxSqlElementTemp::~QxSqlElementTemp() { ; } + + IxSqlElement::type_class QxSqlElementTemp::getTypeClass() const { return IxSqlElement::_sql_element_temp; } + + QString QxSqlElementTemp::toString() const { return ""; } + + void QxSqlElementTemp::resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch /* = NULL */) const + { + Q_UNUSED(query); + Q_UNUSED(pLstExecBatch); + } + + void QxSqlElementTemp::postProcess(QString &sql) const { Q_UNUSED(sql); } + + QString QxSqlElementTemp::getExtraSettings() const { return ""; } + + void QxSqlElementTemp::setExtraSettings(const QString &s) { Q_UNUSED(s); } + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/src/QxDao/QxSqlElement/QxSqlEmbedQuery.cpp b/src/QxDao/QxSqlElement/QxSqlEmbedQuery.cpp new file mode 100644 index 0000000..23c62ba --- /dev/null +++ b/src/QxDao/QxSqlElement/QxSqlEmbedQuery.cpp @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +#ifndef _QX_NO_JSON +#include +#endif // _QX_NO_JSON + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + struct Q_DECL_HIDDEN QxSqlEmbedQuery::QxSqlEmbedQueryImpl + { + + qx::QxSqlQuery m_query; //!< SQL sub-query to embed inside a parent SQL query + QxSqlEmbedQuery::type m_type; //!< Embed query type + + QxSqlEmbedQueryImpl(QxSqlEmbedQuery::type type) : m_type(type) { ; } + ~QxSqlEmbedQueryImpl() { ; } + }; + + QxSqlEmbedQuery::QxSqlEmbedQuery(QxSqlEmbedQuery::type type /* = QxSqlEmbedQuery::_none */) : IxSqlElement(0), m_pImpl(new QxSqlEmbedQueryImpl(type)) { ; } + + QxSqlEmbedQuery::QxSqlEmbedQuery(int index, QxSqlEmbedQuery::type type /* = QxSqlEmbedQuery::_none */) : IxSqlElement(index), m_pImpl(new QxSqlEmbedQueryImpl(type)) { ; } + + QxSqlEmbedQuery::~QxSqlEmbedQuery() { ; } + + IxSqlElement::type_class QxSqlEmbedQuery::getTypeClass() const { return IxSqlElement::_sql_embed_query; } + + void QxSqlEmbedQuery::setQuery(const qx::QxSqlQuery &query) { m_pImpl->m_query = query; } + + QString QxSqlEmbedQuery::toString() const + { + QString result; + QString column = m_lstColumns.at(0); + QString sql = m_pImpl->m_query.query(); + switch (m_pImpl->m_type) + { + case _none: + result = sql; + break; + case _in: + result = column + " IN (" + sql + ")"; + break; + case _not_in: + result = column + " NOT IN (" + sql + ")"; + break; + case _is_equal_to: + result = column + " = (" + sql + ")"; + break; + case _is_not_equal_to: + result = column + " <> (" + sql + ")"; + break; + default: + qAssert(false); + } + return result; + } + + void QxSqlEmbedQuery::resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch /* = NULL */) const { m_pImpl->m_query.resolve(query, pLstExecBatch); } + + void QxSqlEmbedQuery::postProcess(QString &sql) const { Q_UNUSED(sql); } + + QString QxSqlEmbedQuery::getExtraSettings() const + { +#ifndef _QX_NO_JSON + return (QString::number(static_cast(m_pImpl->m_type)) + "|" + qx::serialization::json::to_string(m_pImpl->m_query)); +#else // _QX_NO_JSON + return QString(); +#endif // _QX_NO_JSON + } + + void QxSqlEmbedQuery::setExtraSettings(const QString &s) + { +#ifndef _QX_NO_JSON + int pos = s.indexOf("|"); + if (pos == -1) + { + m_pImpl->m_type = static_cast(s.toInt()); + return; + } + m_pImpl->m_type = static_cast(s.left(pos).toInt()); + QString json = s.right(s.size() - (pos + 1)); + qx::serialization::json::from_string(m_pImpl->m_query, json); +#else // _QX_NO_JSON + Q_UNUSED(s); +#endif // _QX_NO_JSON + } + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/src/QxDao/QxSqlElement/QxSqlExpression.cpp b/src/QxDao/QxSqlElement/QxSqlExpression.cpp new file mode 100644 index 0000000..9bbc49f --- /dev/null +++ b/src/QxDao/QxSqlElement/QxSqlExpression.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + QxSqlExpression::QxSqlExpression() : IxSqlElement(0), m_type(QxSqlExpression::_where) { ; } + + QxSqlExpression::QxSqlExpression(int index, QxSqlExpression::type t) : IxSqlElement(index), m_type(t) { ; } + + QxSqlExpression::~QxSqlExpression() { ; } + + IxSqlElement::type_class QxSqlExpression::getTypeClass() const { return IxSqlElement::_sql_expression; } + + QString QxSqlExpression::toString() const + { + QString sReturn; + + switch (m_type) + { + case _where: + sReturn = "WHERE"; + break; + case _and: + sReturn = "AND"; + break; + case _or: + sReturn = "OR"; + break; + case _open_parenthesis: + sReturn = "("; + break; + case _close_parenthesis: + sReturn = ")"; + break; + default: + qAssert(false); + } + + return sReturn; + } + + void QxSqlExpression::resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch /* = NULL */) const + { + Q_UNUSED(query); + Q_UNUSED(pLstExecBatch); + } + + void QxSqlExpression::postProcess(QString &sql) const { Q_UNUSED(sql); } + + QString QxSqlExpression::getExtraSettings() const { return QString::number(static_cast(m_type)); } + + void QxSqlExpression::setExtraSettings(const QString &s) { m_type = static_cast(s.toInt()); } + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/src/QxDao/QxSqlElement/QxSqlFreeText.cpp b/src/QxDao/QxSqlElement/QxSqlFreeText.cpp new file mode 100644 index 0000000..da110cf --- /dev/null +++ b/src/QxDao/QxSqlElement/QxSqlFreeText.cpp @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + QxSqlFreeText::QxSqlFreeText() : IxSqlElement(0) { ; } + + QxSqlFreeText::QxSqlFreeText(int index) : IxSqlElement(index) { ; } + + QxSqlFreeText::~QxSqlFreeText() { ; } + + IxSqlElement::type_class QxSqlFreeText::getTypeClass() const { return IxSqlElement::_sql_free_text; } + + QString QxSqlFreeText::toString() const { return m_sText; } + + void QxSqlFreeText::resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch /* = NULL */) const + { + if (m_lstValues.count() <= 0) + { + return; + } + qx::QxSqlDatabase::ph_style phStyle = qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle(); + bool bQuestionMark = (phStyle == qx::QxSqlDatabase::ph_style_question_mark); + QString toFind = ((phStyle == qx::QxSqlDatabase::ph_style_2_point_name) ? QString(":") : QString("@")); + QString txt = m_sText.trimmed(); + int posBegin = -1; + int posEnd = -1; + + for (int i = 0; i < m_lstValues.count(); i++) + { + QString key = (QString("sql_free_text_") + QString::number(i)); + QVariant val(m_lstValues.at(i)); + if (!bQuestionMark) + { + posBegin = txt.indexOf(toFind); + posEnd = txt.indexOf(" ", (posBegin + 1)); + if ((posEnd == -1) && (i == (m_lstValues.count() - 1))) + { + posEnd = txt.size(); + } + if ((posBegin == -1) || (posEnd == -1) || (posEnd <= posBegin)) + { + qAssert(false); + break; + } + key = txt.mid(posBegin, (posEnd - posBegin)).replace(")", ""); + txt = txt.right(txt.size() - posEnd).trimmed(); + } + + if (pLstExecBatch) + { + if (!pLstExecBatch->exist(key)) + { + QVariantList empty; + pLstExecBatch->insert(key, empty); + } + QVariantList &values = const_cast(pLstExecBatch->getByKey(key)); + values.append(val); + } + else + { + if (bQuestionMark) + { + query.addBindValue(val); + } + else + { + query.bindValue(key, val); + } + } + } + } + + void QxSqlFreeText::postProcess(QString &sql) const { Q_UNUSED(sql); } + + QString QxSqlFreeText::getExtraSettings() const { return m_sText; } + + void QxSqlFreeText::setExtraSettings(const QString &s) { m_sText = s; } + + void QxSqlFreeText::setText(const QString &txt) { m_sText = txt; } + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/src/QxDao/QxSqlElement/QxSqlIn.cpp b/src/QxDao/QxSqlElement/QxSqlIn.cpp new file mode 100644 index 0000000..18b33bc --- /dev/null +++ b/src/QxDao/QxSqlElement/QxSqlIn.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + QxSqlIn::QxSqlIn() : IxSqlElement(0), m_type(QxSqlIn::_in) { ; } + + QxSqlIn::QxSqlIn(int index, QxSqlIn::type t) : IxSqlElement(index), m_type(t) { ; } + + QxSqlIn::~QxSqlIn() { ; } + + IxSqlElement::type_class QxSqlIn::getTypeClass() const { return IxSqlElement::_sql_in; } + + QString QxSqlIn::toString() const + { + qAssert((m_lstColumns.count() == 1) && (m_lstKeys.count() == 1) && (m_lstValues.count() >= 1)); + QString sReturn, sColumn(m_lstColumns.at(0)), sKey(m_lstKeys.at(0)); + bool bQuestionMark = (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark); + qAssert(!sColumn.isEmpty() && !sKey.isEmpty()); + + switch (m_type) + { + case _in: + sReturn = sColumn + " IN ("; + break; + case _not_in: + sReturn = sColumn + " NOT IN ("; + break; + case _in_select: + sReturn = sColumn + " IN ("; + break; + case _not_in_select: + sReturn = sColumn + " NOT IN ("; + break; + default: + qAssert(false); + } + + for (int i = 0; i < m_lstValues.count(); i++) + { + if ((m_type == _in_select) || (m_type == _not_in_select)) + { + sReturn += m_lstValues.at(i).toString(); + continue; + } + QString sCurrKey = (bQuestionMark ? QString("?") : (sKey + QString("_") + QString::number(i))); + sReturn += ((i == 0) ? QString("") : QString(", ")); + sReturn += sCurrKey; + } + + sReturn += QString(")"); + return sReturn; + } + + void QxSqlIn::resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch /* = NULL */) const + { + if ((m_type == _in_select) || (m_type == _not_in_select)) + { + return; + } + qAssert((m_lstKeys.count() == 1) && (m_lstValues.count() >= 1)); + bool bQuestionMark = (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark); + QString sKey(m_lstKeys.at(0)); + + for (int i = 0; i < m_lstValues.count(); i++) + { + QString sCurrKey = (sKey + QString("_") + QString::number(i)); + QVariant vValue(m_lstValues.at(i)); + if (pLstExecBatch) + { + if (!pLstExecBatch->exist(sCurrKey)) + { + QVariantList empty; + pLstExecBatch->insert(sCurrKey, empty); + } + QVariantList &values = const_cast(pLstExecBatch->getByKey(sCurrKey)); + values.append(vValue); + } + else + { + if (bQuestionMark) + { + query.addBindValue(vValue); + } + else + { + query.bindValue(sCurrKey, vValue); + } + } + } + } + + void QxSqlIn::postProcess(QString &sql) const { Q_UNUSED(sql); } + + QString QxSqlIn::getExtraSettings() const { return QString::number(static_cast(m_type)); } + + void QxSqlIn::setExtraSettings(const QString &s) { m_type = static_cast(s.toInt()); } + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/src/QxDao/QxSqlElement/QxSqlIsBetween.cpp b/src/QxDao/QxSqlElement/QxSqlIsBetween.cpp new file mode 100644 index 0000000..5621835 --- /dev/null +++ b/src/QxDao/QxSqlElement/QxSqlIsBetween.cpp @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + QxSqlIsBetween::QxSqlIsBetween() : IxSqlElement(0), m_type(QxSqlIsBetween::_is_between) { ; } + + QxSqlIsBetween::QxSqlIsBetween(int index, QxSqlIsBetween::type t) : IxSqlElement(index), m_type(t) { ; } + + QxSqlIsBetween::~QxSqlIsBetween() { ; } + + IxSqlElement::type_class QxSqlIsBetween::getTypeClass() const { return IxSqlElement::_sql_is_between; } + + QString QxSqlIsBetween::toString() const + { + qAssert((m_lstColumns.count() == 1) && (m_lstKeys.count() == 1)); + QString sReturn, sColumn(m_lstColumns.at(0)), sKey(m_lstKeys.at(0)); + qAssert(!sColumn.isEmpty() && !sKey.isEmpty()); + bool bQuestionMark = (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark); + QString sKey1(bQuestionMark ? QString("?") : (sKey + "_1")), sKey2(bQuestionMark ? QString("?") : (sKey + "_2")); + + switch (m_type) + { + case _is_between: + sReturn = sColumn + " BETWEEN " + sKey1 + " AND " + sKey2; + break; + case _is_not_between: + sReturn = sColumn + " NOT BETWEEN " + sKey1 + " AND " + sKey2; + break; + default: + qAssert(false); + } + + return sReturn; + } + + void QxSqlIsBetween::resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch /* = NULL */) const + { + qAssert((m_lstKeys.count() == 1) && (m_lstValues.count() == 2)); + bool bQuestionMark = (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark); + QString sKey1(m_lstKeys.at(0) + "_1"), sKey2(m_lstKeys.at(0) + "_2"); + QVariant vValue1(m_lstValues.at(0)), vValue2(m_lstValues.at(1)); + + if (pLstExecBatch) + { + if (!pLstExecBatch->exist(sKey1)) + { + QVariantList empty; + pLstExecBatch->insert(sKey1, empty); + } + if (!pLstExecBatch->exist(sKey2)) + { + QVariantList empty; + pLstExecBatch->insert(sKey2, empty); + } + QVariantList &values1 = const_cast(pLstExecBatch->getByKey(sKey1)); + QVariantList &values2 = const_cast(pLstExecBatch->getByKey(sKey2)); + values1.append(vValue1); + values2.append(vValue2); + } + else + { + if (bQuestionMark) + { + query.addBindValue(vValue1); + query.addBindValue(vValue2); + } + else + { + query.bindValue(sKey1, vValue1); + query.bindValue(sKey2, vValue2); + } + } + } + + void QxSqlIsBetween::postProcess(QString &sql) const { Q_UNUSED(sql); } + + QString QxSqlIsBetween::getExtraSettings() const { return QString::number(static_cast(m_type)); } + + void QxSqlIsBetween::setExtraSettings(const QString &s) { m_type = static_cast(s.toInt()); } + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/src/QxDao/QxSqlElement/QxSqlIsNull.cpp b/src/QxDao/QxSqlElement/QxSqlIsNull.cpp new file mode 100644 index 0000000..551a86e --- /dev/null +++ b/src/QxDao/QxSqlElement/QxSqlIsNull.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + QxSqlIsNull::QxSqlIsNull() : IxSqlElement(0), m_type(QxSqlIsNull::_is_null) { ; } + + QxSqlIsNull::QxSqlIsNull(int index, QxSqlIsNull::type t) : IxSqlElement(index), m_type(t) { ; } + + QxSqlIsNull::~QxSqlIsNull() { ; } + + IxSqlElement::type_class QxSqlIsNull::getTypeClass() const { return IxSqlElement::_sql_is_null; } + + QString QxSqlIsNull::toString() const + { + qAssert(m_lstColumns.count() == 1); + QString sReturn, sColumn(m_lstColumns.at(0)); + qAssert(!sColumn.isEmpty()); + + switch (m_type) + { + case _is_null: + sReturn = sColumn + " IS NULL"; + break; + case _is_not_null: + sReturn = sColumn + " IS NOT NULL"; + break; + default: + qAssert(false); + } + + return sReturn; + } + + void QxSqlIsNull::resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch /* = NULL */) const + { + Q_UNUSED(query); + Q_UNUSED(pLstExecBatch); + } + + void QxSqlIsNull::postProcess(QString &sql) const { Q_UNUSED(sql); } + + QString QxSqlIsNull::getExtraSettings() const { return QString::number(static_cast(m_type)); } + + void QxSqlIsNull::setExtraSettings(const QString &s) { m_type = static_cast(s.toInt()); } + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/src/QxDao/QxSqlElement/QxSqlLimit.cpp b/src/QxDao/QxSqlElement/QxSqlLimit.cpp new file mode 100644 index 0000000..5da573a --- /dev/null +++ b/src/QxDao/QxSqlElement/QxSqlLimit.cpp @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + QxSqlLimit::QxSqlLimit() : IxSqlElement(0) { ; } + + QxSqlLimit::QxSqlLimit(int index) : IxSqlElement(index) { ; } + + QxSqlLimit::~QxSqlLimit() { ; } + + IxSqlElement::type_class QxSqlLimit::getTypeClass() const { return IxSqlElement::_sql_limit; } + + QString QxSqlLimit::toString() const + { + if (!m_pSqlGenerator) + { + qAssert(false); + return ""; + } + return m_pSqlGenerator->getLimit(this); + } + + void QxSqlLimit::resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch /* = NULL */) const + { + if (!m_pSqlGenerator) + { + qAssert(false); + return; + } + m_pSqlGenerator->resolveLimit(query, this, pLstExecBatch); + } + + void QxSqlLimit::postProcess(QString &sql) const + { + if (!m_pSqlGenerator) + { + qAssert(false); + return; + } + m_pSqlGenerator->postProcess(sql, this); + } + + int QxSqlLimit::getStartRow() const + { + qAssert(m_lstValues.count() >= 2); + return ((m_lstValues.count() > 0) ? m_lstValues.at(0).toInt() : 0); + } + + int QxSqlLimit::getRowsCount() const + { + qAssert(m_lstValues.count() >= 2); + return ((m_lstValues.count() > 1) ? m_lstValues.at(1).toInt() : 0); + } + + int QxSqlLimit::getMaxRow() const + { + qAssert(m_lstValues.count() >= 2); + return ((m_lstValues.count() > 1) ? (m_lstValues.at(0).toInt() + m_lstValues.at(1).toInt()) : 0); + } + + bool QxSqlLimit::getWithTies() const + { + qAssert(m_lstValues.count() >= 3); + return ((m_lstValues.count() > 2) ? m_lstValues.at(2).toBool() : false); + } + + QString QxSqlLimit::getStartRow_ParamKey() const + { + QString sStartRow("offset_start_row"); + sStartRow += "_" + QString::number(m_iIndex) + "_0"; + if (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark) + { + sStartRow = "?"; + } + else if (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_at_name) + { + sStartRow = "@" + sStartRow; + } + else + { + sStartRow = ":" + sStartRow; + } + return sStartRow; + } + + QString QxSqlLimit::getRowsCount_ParamKey() const + { + QString sRowsCount("limit_rows_count"); + sRowsCount += "_" + QString::number(m_iIndex) + "_0"; + if (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark) + { + sRowsCount = "?"; + } + else if (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_at_name) + { + sRowsCount = "@" + sRowsCount; + } + else + { + sRowsCount = ":" + sRowsCount; + } + return sRowsCount; + } + + QString QxSqlLimit::getMaxRow_ParamKey() const + { + QString sMaxRow("offset_max_row"); + sMaxRow += "_" + QString::number(m_iIndex) + "_0"; + if (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark) + { + sMaxRow = "?"; + } + else if (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_at_name) + { + sMaxRow = "@" + sMaxRow; + } + else + { + sMaxRow = ":" + sMaxRow; + } + return sMaxRow; + } + + QString QxSqlLimit::getExtraSettings() const { return ""; } + + void QxSqlLimit::setExtraSettings(const QString &s) { Q_UNUSED(s); } + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/src/QxDao/QxSqlElement/QxSqlSort.cpp b/src/QxDao/QxSqlElement/QxSqlSort.cpp new file mode 100644 index 0000000..dad5a25 --- /dev/null +++ b/src/QxDao/QxSqlElement/QxSqlSort.cpp @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + QxSqlSort::QxSqlSort() : IxSqlElement(0), m_type(QxSqlSort::_order_asc) { ; } + + QxSqlSort::QxSqlSort(int index, QxSqlSort::type t) : IxSqlElement(index), m_type(t) { ; } + + QxSqlSort::~QxSqlSort() { ; } + + IxSqlElement::type_class QxSqlSort::getTypeClass() const { return IxSqlElement::_sql_sort; } + + QString QxSqlSort::toString() const + { + qAssert(m_lstColumns.count() >= 1); + QString sReturn; + + switch (m_type) + { + case _order_asc: + sReturn = "ORDER BY "; + break; + case _order_desc: + sReturn = "ORDER BY "; + break; + case _group_by: + sReturn = "GROUP BY "; + break; + default: + qAssert(false); + } + + for (int i = 0; i < m_lstColumns.count(); i++) + { + sReturn += ((i == 0) ? QString("") : QString(", ")); + QString sColumn = m_lstColumns.at(i); + qAssert(!sColumn.isEmpty()); + + switch (m_type) + { + case _order_asc: + sReturn += sColumn + " ASC"; + break; + case _order_desc: + sReturn += sColumn + " DESC"; + break; + case _group_by: + sReturn += sColumn; + break; + } + } + + return sReturn; + } + + void QxSqlSort::resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch /* = NULL */) const + { + Q_UNUSED(query); + Q_UNUSED(pLstExecBatch); + } + + void QxSqlSort::postProcess(QString &sql) const { Q_UNUSED(sql); } + + QString QxSqlSort::getExtraSettings() const { return QString::number(static_cast(m_type)); } + + void QxSqlSort::setExtraSettings(const QString &s) { m_type = static_cast(s.toInt()); } + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/src/QxDao/QxSqlGenerator/IxSqlGenerator.cpp b/src/QxDao/QxSqlGenerator/IxSqlGenerator.cpp new file mode 100644 index 0000000..7e6f8bf --- /dev/null +++ b/src/QxDao/QxSqlGenerator/IxSqlGenerator.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + IxSqlGenerator::IxSqlGenerator() { ; } + + IxSqlGenerator::~IxSqlGenerator() { ; } + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/src/QxDao/QxSqlGenerator/QxSqlGenerator_MSSQLServer.cpp b/src/QxDao/QxSqlGenerator/QxSqlGenerator_MSSQLServer.cpp new file mode 100644 index 0000000..b1b246c --- /dev/null +++ b/src/QxDao/QxSqlGenerator/QxSqlGenerator_MSSQLServer.cpp @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + QxSqlGenerator_MSSQLServer::QxSqlGenerator_MSSQLServer() : QxSqlGenerator_Standard() { this->initSqlTypeByClassName(); } + + QxSqlGenerator_MSSQLServer::~QxSqlGenerator_MSSQLServer() { ; } + + void QxSqlGenerator_MSSQLServer::init() { qx::QxSqlDatabase::getSingleton()->setAddAutoIncrementIdToUpdateQuery(false); } + + QString QxSqlGenerator_MSSQLServer::getLimit(const QxSqlLimit *pLimit) const + { + Q_UNUSED(pLimit); + return ""; + } + + void QxSqlGenerator_MSSQLServer::resolveLimit(QSqlQuery &query, const QxSqlLimit *pLimit, qx::QxCollection *pLstExecBatch /* = NULL */) const + { + if (!pLimit) + { + qAssert(false); + return; + } + QString sRowsCount = pLimit->getRowsCount_ParamKey(); + int iRowsCount(pLimit->getRowsCount()); + bool bQuestionMark = (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark); + + if (pLstExecBatch) + { + if (!pLstExecBatch->exist(sRowsCount)) + { + QVariantList empty; + pLstExecBatch->insert(sRowsCount, empty); + } + QVariantList &values = const_cast(pLstExecBatch->getByKey(sRowsCount)); + values.append(iRowsCount); + } + else + { + if (bQuestionMark) + { + query.addBindValue(iRowsCount); + } + else + { + query.bindValue(sRowsCount, iRowsCount); + } + } + } + + void QxSqlGenerator_MSSQLServer::postProcess(QString &sql, const QxSqlLimit *pLimit) const + { + if (!pLimit) + { + qAssert(false); + return; + } + if (!sql.left(7).contains("SELECT ", Qt::CaseInsensitive)) + { + qAssert(false); + return; + } + QString sRowsCount = pLimit->getRowsCount_ParamKey(); + sql = sql.right(sql.size() - 7); + sql = "SELECT TOP " + sRowsCount + " " + sql; + } + + void QxSqlGenerator_MSSQLServer::initSqlTypeByClassName() const + { + QHash *lstSqlType = qx::QxClassX::getAllSqlTypeByClassName(); + if (!lstSqlType) + { + qAssert(false); + return; + } + + lstSqlType->insert("bool", "TINYINT"); + lstSqlType->insert("qx_bool", "TEXT"); + lstSqlType->insert("short", "SMALLINT"); + lstSqlType->insert("int", "INT"); + lstSqlType->insert("long", "INT"); + lstSqlType->insert("long long", "BIGINT"); + lstSqlType->insert("float", "FLOAT"); + lstSqlType->insert("double", "FLOAT"); + lstSqlType->insert("long double", "FLOAT"); + lstSqlType->insert("unsigned short", "SMALLINT"); + lstSqlType->insert("unsigned int", "INT"); + lstSqlType->insert("unsigned long", "INT"); + lstSqlType->insert("unsigned long long", "BIGINT"); + lstSqlType->insert("std::string", "TEXT"); + lstSqlType->insert("std::wstring", "TEXT"); + lstSqlType->insert("QString", "TEXT"); + lstSqlType->insert("QVariant", "TEXT"); + lstSqlType->insert("QUuid", "TEXT"); + lstSqlType->insert("QDate", "DATE"); + lstSqlType->insert("QTime", "TIME"); + lstSqlType->insert("QDateTime", "TIMESTAMP"); + lstSqlType->insert("QByteArray", "IMAGE"); + lstSqlType->insert("qx::QxDateNeutral", "TEXT"); + lstSqlType->insert("qx::QxTimeNeutral", "TEXT"); + lstSqlType->insert("qx::QxDateTimeNeutral", "TEXT"); + } + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/src/QxDao/QxSqlGenerator/QxSqlGenerator_MySQL.cpp b/src/QxDao/QxSqlGenerator/QxSqlGenerator_MySQL.cpp new file mode 100644 index 0000000..ee76e76 --- /dev/null +++ b/src/QxDao/QxSqlGenerator/QxSqlGenerator_MySQL.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + QxSqlGenerator_MySQL::QxSqlGenerator_MySQL() : QxSqlGenerator_Standard() { this->initSqlTypeByClassName(); } + + QxSqlGenerator_MySQL::~QxSqlGenerator_MySQL() { ; } + + QString QxSqlGenerator_MySQL::getAutoIncrement() const { return "AUTO_INCREMENT"; } + + void QxSqlGenerator_MySQL::initSqlTypeByClassName() const + { + QHash *lstSqlType = qx::QxClassX::getAllSqlTypeByClassName(); + if (!lstSqlType) + { + qAssert(false); + return; + } + + lstSqlType->insert("bool", "SMALLINT"); + lstSqlType->insert("qx_bool", "TEXT"); + lstSqlType->insert("short", "SMALLINT"); + lstSqlType->insert("int", "INTEGER"); + lstSqlType->insert("long", "INTEGER"); + lstSqlType->insert("long long", "BIGINT"); + lstSqlType->insert("float", "FLOAT"); + lstSqlType->insert("double", "DOUBLE"); + lstSqlType->insert("long double", "DOUBLE"); + lstSqlType->insert("unsigned short", "SMALLINT"); + lstSqlType->insert("unsigned int", "INTEGER"); + lstSqlType->insert("unsigned long", "INTEGER"); + lstSqlType->insert("unsigned long long", "BIGINT"); + lstSqlType->insert("std::string", "TEXT"); + lstSqlType->insert("std::wstring", "TEXT"); + lstSqlType->insert("QString", "TEXT"); + lstSqlType->insert("QVariant", "TEXT"); + lstSqlType->insert("QUuid", "TEXT"); + lstSqlType->insert("QDate", "DATE"); + lstSqlType->insert("QTime", "TIME"); + lstSqlType->insert("QDateTime", "TIMESTAMP"); + lstSqlType->insert("QByteArray", "LONGBLOB"); + lstSqlType->insert("qx::QxDateNeutral", "TEXT"); + lstSqlType->insert("qx::QxTimeNeutral", "TEXT"); + lstSqlType->insert("qx::QxDateTimeNeutral", "TEXT"); + } + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/src/QxDao/QxSqlGenerator/QxSqlGenerator_Oracle.cpp b/src/QxDao/QxSqlGenerator/QxSqlGenerator_Oracle.cpp new file mode 100644 index 0000000..4b35cd7 --- /dev/null +++ b/src/QxDao/QxSqlGenerator/QxSqlGenerator_Oracle.cpp @@ -0,0 +1,314 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + QxSqlGenerator_Oracle::QxSqlGenerator_Oracle() : QxSqlGenerator_Standard(), m_bOldLimitSyntax(false), m_bManageLastInsertId(false) { this->initSqlTypeByClassName(); } + + QxSqlGenerator_Oracle::QxSqlGenerator_Oracle(bool bManageLastInsertId) : QxSqlGenerator_Standard(), m_bOldLimitSyntax(false), m_bManageLastInsertId(bManageLastInsertId) { this->initSqlTypeByClassName(); } + + QxSqlGenerator_Oracle::~QxSqlGenerator_Oracle() { ; } + + bool QxSqlGenerator_Oracle::getOldLimitSyntax() const { return m_bOldLimitSyntax; } + + void QxSqlGenerator_Oracle::setOldLimitSyntax(bool b) { m_bOldLimitSyntax = b; } + + bool QxSqlGenerator_Oracle::getManageLastInsertId() const { return m_bManageLastInsertId; } + + void QxSqlGenerator_Oracle::setManageLastInsertId(bool b) { m_bManageLastInsertId = b; } + + QString QxSqlGenerator_Oracle::getTableAliasSep() const { return " "; } + + QString QxSqlGenerator_Oracle::getLimit(const QxSqlLimit *pLimit) const + { + Q_UNUSED(pLimit); + return ""; + } + + void QxSqlGenerator_Oracle::resolveLimit(QSqlQuery &query, const QxSqlLimit *pLimit, qx::QxCollection *pLstExecBatch /* = NULL */) const + { + if (!m_bOldLimitSyntax) + { + return; + } + if (!pLimit) + { + qAssert(false); + return; + } + QString sMinRow = pLimit->getStartRow_ParamKey(); + QString sMaxRow = pLimit->getMaxRow_ParamKey(); + int iMinRow(pLimit->getStartRow()), iMaxRow(pLimit->getMaxRow()); + bool bQuestionMark = (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark); + + if (pLstExecBatch) + { + if (!pLstExecBatch->exist(sMaxRow)) + { + QVariantList empty; + pLstExecBatch->insert(sMaxRow, empty); + } + if (!pLstExecBatch->exist(sMinRow)) + { + QVariantList empty; + pLstExecBatch->insert(sMinRow, empty); + } + QVariantList &valuesMaxRow = const_cast(pLstExecBatch->getByKey(sMaxRow)); + QVariantList &valuesMinRow = const_cast(pLstExecBatch->getByKey(sMinRow)); + valuesMaxRow.append(iMaxRow); + valuesMinRow.append(iMinRow); + } + else + { + if (bQuestionMark) + { + query.addBindValue(iMaxRow); + query.addBindValue(iMinRow); + } + else + { + query.bindValue(sMaxRow, iMaxRow); + query.bindValue(sMinRow, iMinRow); + } + } + } + + void QxSqlGenerator_Oracle::postProcess(QString &sql, const QxSqlLimit *pLimit) const + { + if (!pLimit) + { + qAssert(false); + return; + } + QString sMinRow = pLimit->getStartRow_ParamKey(); + QString sMaxRow = pLimit->getMaxRow_ParamKey(); + int iMinRow(pLimit->getStartRow()), iRowsCount(pLimit->getRowsCount()); + bool bWithTies = pLimit->getWithTies(); + + if (m_bOldLimitSyntax) + { + QString sqlPaging; + QString sReplace = "%SQL_QUERY%"; + sqlPaging += "SELECT * FROM "; + sqlPaging += " ( SELECT a.*, ROWNUM rnum FROM "; + sqlPaging += " ( " + sReplace + " ) a "; + sqlPaging += " WHERE ROWNUM <= " + sMaxRow + " ) "; + sqlPaging += "WHERE rnum >= " + sMinRow; + sqlPaging.replace(sReplace, sql); + sql = sqlPaging; + return; + } + + if (iMinRow <= 0) + { + if (bWithTies) + { + sql += " FETCH FIRST " + QString::number(iRowsCount) + " ROWS WITH TIES"; + } + else + { + sql += " FETCH FIRST " + QString::number(iRowsCount) + " ROWS ONLY"; + } + } + else + { + if (bWithTies) + { + sql += " OFFSET " + QString::number(iMinRow) + " ROWS FETCH NEXT " + QString::number(iRowsCount) + " ROWS WITH TIES"; + } + else + { + sql += " OFFSET " + QString::number(iMinRow) + " ROWS FETCH NEXT " + QString::number(iRowsCount) + " ROWS ONLY"; + } + } + } + + void QxSqlGenerator_Oracle::checkSqlInsert(IxDao_Helper *pDaoHelper, QString &sql) const + { + if (!m_bManageLastInsertId) + { + return; + } + if (!pDaoHelper) + { + qAssert(false); + return; + } + if (!pDaoHelper->getDataId()) + { + return; + } + qx::IxDataMember *pId = pDaoHelper->getDataId(); + if (!pId->getAutoIncrement()) + { + return; + } + if (pId->getNameCount() > 1) + { + qAssert(false); + return; + } + QString sqlToAdd = " RETURNING ID INTO :ID; END;"; + if (sql.right(sqlToAdd.size()) == sqlToAdd) + { + return; + } + sql = "BEGIN " + sql + sqlToAdd; + pDaoHelper->builder().setSqlQuery(sql); + } + + void QxSqlGenerator_Oracle::onBeforeInsert(IxDao_Helper *pDaoHelper, void *pOwner) const + { + if (!m_bManageLastInsertId) + { + return; + } + if (!pDaoHelper || !pOwner) + { + qAssert(false); + return; + } + if (!pDaoHelper->getDataId()) + { + return; + } + qx::IxDataMember *pId = pDaoHelper->getDataId(); + if (!pId->getAutoIncrement()) + { + return; + } + if (pId->getNameCount() > 1) + { + qAssert(false); + return; + } + QString key = ":ID"; + if (pDaoHelper->getUseExecBatch()) + { + qx::QxCollection &lstExecBatch = pDaoHelper->getListExecBatch(); + if (!lstExecBatch.exist(key)) + { + QVariantList empty; + lstExecBatch.insert(key, empty); + } + QVariantList &values = const_cast(lstExecBatch.getByKey(key)); + values.append(0); + } + else + { + pDaoHelper->query().bindValue(key, 0, QSql::InOut); + } + } + + void QxSqlGenerator_Oracle::onAfterInsert(IxDao_Helper *pDaoHelper, void *pOwner) const + { + if (!m_bManageLastInsertId) + { + return; + } + if (!pDaoHelper || !pOwner) + { + qAssert(false); + return; + } + if (!pDaoHelper->getDataId()) + { + return; + } + qx::IxDataMember *pId = pDaoHelper->getDataId(); + if (!pId->getAutoIncrement()) + { + return; + } + if (pId->getNameCount() > 1) + { + qAssert(false); + return; + } + QVariant vId = pDaoHelper->query().boundValue(":ID"); + pId->fromVariant(pOwner, vId, -1, qx::cvt::context::e_database); + } + + void QxSqlGenerator_Oracle::initSqlTypeByClassName() const + { + QHash *lstSqlType = qx::QxClassX::getAllSqlTypeByClassName(); + if (!lstSqlType) + { + qAssert(false); + return; + } + + lstSqlType->insert("bool", "SMALLINT"); + lstSqlType->insert("qx_bool", "VARCHAR2(4000)"); + lstSqlType->insert("short", "SMALLINT"); + lstSqlType->insert("int", "INTEGER"); + lstSqlType->insert("long", "INTEGER"); + lstSqlType->insert("long long", "INTEGER"); + lstSqlType->insert("float", "FLOAT"); + lstSqlType->insert("double", "FLOAT"); + lstSqlType->insert("long double", "FLOAT"); + lstSqlType->insert("unsigned short", "SMALLINT"); + lstSqlType->insert("unsigned int", "INTEGER"); + lstSqlType->insert("unsigned long", "INTEGER"); + lstSqlType->insert("unsigned long long", "INTEGER"); + lstSqlType->insert("std::string", "VARCHAR2(4000)"); + lstSqlType->insert("std::wstring", "VARCHAR2(4000)"); + lstSqlType->insert("QString", "VARCHAR2(4000)"); + lstSqlType->insert("QVariant", "CLOB"); + lstSqlType->insert("QUuid", "VARCHAR2(255)"); + lstSqlType->insert("QDate", "DATE"); + lstSqlType->insert("QTime", "DATE"); + lstSqlType->insert("QDateTime", "TIMESTAMP"); + lstSqlType->insert("QByteArray", "BLOB"); + lstSqlType->insert("qx::QxDateNeutral", "VARCHAR2(8)"); + lstSqlType->insert("qx::QxTimeNeutral", "VARCHAR2(6)"); + lstSqlType->insert("qx::QxDateTimeNeutral", "VARCHAR2(14)"); + } + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/src/QxDao/QxSqlGenerator/QxSqlGenerator_PostgreSQL.cpp b/src/QxDao/QxSqlGenerator/QxSqlGenerator_PostgreSQL.cpp new file mode 100644 index 0000000..ff0245b --- /dev/null +++ b/src/QxDao/QxSqlGenerator/QxSqlGenerator_PostgreSQL.cpp @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + QxSqlGenerator_PostgreSQL::QxSqlGenerator_PostgreSQL() : QxSqlGenerator_Standard() { this->initSqlTypeByClassName(); } + + QxSqlGenerator_PostgreSQL::~QxSqlGenerator_PostgreSQL() { ; } + + void QxSqlGenerator_PostgreSQL::checkSqlInsert(IxDao_Helper *pDaoHelper, QString &sql) const + { + if (!pDaoHelper) + { + qAssert(false); + return; + } + if (!pDaoHelper->getDataId()) + { + return; + } + qx::IxDataMember *pId = pDaoHelper->getDataId(); + if (!pId->getAutoIncrement()) + { + return; + } + if (pId->getNameCount() > 1) + { + qAssert(false); + return; + } + QString sqlToAdd = " RETURNING " + pId->getName(); + if (sql.right(sqlToAdd.size()) == sqlToAdd) + { + return; + } + sql += sqlToAdd; + pDaoHelper->builder().setSqlQuery(sql); + } + + void QxSqlGenerator_PostgreSQL::onAfterInsert(IxDao_Helper *pDaoHelper, void *pOwner) const + { + if (!pDaoHelper || !pOwner) + { + qAssert(false); + return; + } + if (!pDaoHelper->getDataId()) + { + return; + } + qx::IxDataMember *pId = pDaoHelper->getDataId(); + if (!pId->getAutoIncrement()) + { + return; + } + if (pId->getNameCount() > 1) + { + qAssert(false); + return; + } + if (!pDaoHelper->nextRecord()) + { + qAssert(false); + return; + } + QVariant vId = pDaoHelper->query().value(0); + pId->fromVariant(pOwner, vId, -1, qx::cvt::context::e_database); + } + + void QxSqlGenerator_PostgreSQL::initSqlTypeByClassName() const + { + QHash *lstSqlType = qx::QxClassX::getAllSqlTypeByClassName(); + if (!lstSqlType) + { + qAssert(false); + return; + } + + lstSqlType->insert("bool", "BOOLEAN"); + lstSqlType->insert("qx_bool", "TEXT"); + lstSqlType->insert("short", "SMALLINT"); + lstSqlType->insert("int", "INTEGER"); + lstSqlType->insert("long", "INTEGER"); + lstSqlType->insert("long long", "BIGINT"); + lstSqlType->insert("float", "FLOAT"); + lstSqlType->insert("double", "FLOAT"); + lstSqlType->insert("long double", "FLOAT"); + lstSqlType->insert("unsigned short", "SMALLINT"); + lstSqlType->insert("unsigned int", "INTEGER"); + lstSqlType->insert("unsigned long", "INTEGER"); + lstSqlType->insert("unsigned long long", "BIGINT"); + lstSqlType->insert("std::string", "TEXT"); + lstSqlType->insert("std::wstring", "TEXT"); + lstSqlType->insert("QString", "TEXT"); + lstSqlType->insert("QVariant", "TEXT"); + lstSqlType->insert("QUuid", "TEXT"); + lstSqlType->insert("QDate", "DATE"); + lstSqlType->insert("QTime", "TIME"); + lstSqlType->insert("QDateTime", "TIMESTAMP"); + lstSqlType->insert("QByteArray", "BYTEA"); + lstSqlType->insert("qx::QxDateNeutral", "TEXT"); + lstSqlType->insert("qx::QxTimeNeutral", "TEXT"); + lstSqlType->insert("qx::QxDateTimeNeutral", "TEXT"); + } + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/src/QxDao/QxSqlGenerator/QxSqlGenerator_SQLite.cpp b/src/QxDao/QxSqlGenerator/QxSqlGenerator_SQLite.cpp new file mode 100644 index 0000000..1f4c250 --- /dev/null +++ b/src/QxDao/QxSqlGenerator/QxSqlGenerator_SQLite.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + QxSqlGenerator_SQLite::QxSqlGenerator_SQLite() : QxSqlGenerator_Standard() { this->initSqlTypeByClassName(); } + + QxSqlGenerator_SQLite::~QxSqlGenerator_SQLite() { ; } + + void QxSqlGenerator_SQLite::initSqlTypeByClassName() const + { + QHash *lstSqlType = qx::QxClassX::getAllSqlTypeByClassName(); + if (!lstSqlType) + { + qAssert(false); + return; + } + + lstSqlType->insert("bool", "SMALLINT"); + lstSqlType->insert("qx_bool", "TEXT"); + lstSqlType->insert("short", "SMALLINT"); + lstSqlType->insert("int", "INTEGER"); + lstSqlType->insert("long", "INTEGER"); + lstSqlType->insert("long long", "INTEGER"); + lstSqlType->insert("float", "FLOAT"); + lstSqlType->insert("double", "FLOAT"); + lstSqlType->insert("long double", "FLOAT"); + lstSqlType->insert("unsigned short", "SMALLINT"); + lstSqlType->insert("unsigned int", "INTEGER"); + lstSqlType->insert("unsigned long", "INTEGER"); + lstSqlType->insert("unsigned long long", "INTEGER"); + lstSqlType->insert("std::string", "TEXT"); + lstSqlType->insert("std::wstring", "TEXT"); + lstSqlType->insert("QString", "TEXT"); + lstSqlType->insert("QVariant", "TEXT"); + lstSqlType->insert("QUuid", "TEXT"); + lstSqlType->insert("QDate", "DATE"); + lstSqlType->insert("QTime", "TIME"); + lstSqlType->insert("QDateTime", "TIMESTAMP"); + lstSqlType->insert("QByteArray", "BLOB"); + lstSqlType->insert("qx::QxDateNeutral", "TEXT"); + lstSqlType->insert("qx::QxTimeNeutral", "TEXT"); + lstSqlType->insert("qx::QxDateTimeNeutral", "TEXT"); + } + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/src/QxDao/QxSqlGenerator/QxSqlGenerator_Standard.cpp b/src/QxDao/QxSqlGenerator/QxSqlGenerator_Standard.cpp new file mode 100644 index 0000000..cb49af1 --- /dev/null +++ b/src/QxDao/QxSqlGenerator/QxSqlGenerator_Standard.cpp @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include +#include + +#include + +namespace qx +{ + namespace dao + { + namespace detail + { + + QxSqlGenerator_Standard::QxSqlGenerator_Standard() : IxSqlGenerator() { ; } + + QxSqlGenerator_Standard::~QxSqlGenerator_Standard() { ; } + + void QxSqlGenerator_Standard::init() { ; } + + QString QxSqlGenerator_Standard::getAutoIncrement() const { return "AUTOINCREMENT"; } + + QString QxSqlGenerator_Standard::getWildCard() const { return "%"; } + + QString QxSqlGenerator_Standard::getTableAliasSep() const { return " AS "; } + + QString QxSqlGenerator_Standard::getLimit(const QxSqlLimit *pLimit) const + { + if (!pLimit) + { + qAssert(false); + return ""; + } + QString sStartRow = pLimit->getStartRow_ParamKey(); + QString sRowsCount = pLimit->getRowsCount_ParamKey(); + return ("LIMIT " + sRowsCount + " OFFSET " + sStartRow); + } + + void QxSqlGenerator_Standard::resolveLimit(QSqlQuery &query, const QxSqlLimit *pLimit, qx::QxCollection *pLstExecBatch /* = NULL */) const + { + if (!pLimit) + { + qAssert(false); + return; + } + QString sStartRow = pLimit->getStartRow_ParamKey(); + QString sRowsCount = pLimit->getRowsCount_ParamKey(); + int iStartRow(pLimit->getStartRow()), iRowsCount(pLimit->getRowsCount()); + bool bQuestionMark = (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark); + + if (pLstExecBatch) + { + if (!pLstExecBatch->exist(sRowsCount)) + { + QVariantList empty; + pLstExecBatch->insert(sRowsCount, empty); + } + if (!pLstExecBatch->exist(sStartRow)) + { + QVariantList empty; + pLstExecBatch->insert(sStartRow, empty); + } + QVariantList &valuesRowsCount = const_cast(pLstExecBatch->getByKey(sRowsCount)); + QVariantList &valuesStartRow = const_cast(pLstExecBatch->getByKey(sStartRow)); + valuesRowsCount.append(iRowsCount); + valuesStartRow.append(iStartRow); + } + else + { + if (bQuestionMark) + { + query.addBindValue(iRowsCount); + query.addBindValue(iStartRow); + } + else + { + query.bindValue(sRowsCount, iRowsCount); + query.bindValue(sStartRow, iStartRow); + } + } + } + + void QxSqlGenerator_Standard::postProcess(QString &sql, const QxSqlLimit *pLimit) const + { + Q_UNUSED(sql); + Q_UNUSED(pLimit); + } + + void QxSqlGenerator_Standard::onBeforeInsert(IxDao_Helper *pDaoHelper, void *pOwner) const + { + Q_UNUSED(pDaoHelper); + Q_UNUSED(pOwner); + } + + void QxSqlGenerator_Standard::onAfterInsert(IxDao_Helper *pDaoHelper, void *pOwner) const + { + Q_UNUSED(pDaoHelper); + Q_UNUSED(pOwner); + } + + void QxSqlGenerator_Standard::onBeforeUpdate(IxDao_Helper *pDaoHelper, void *pOwner) const + { + Q_UNUSED(pDaoHelper); + Q_UNUSED(pOwner); + } + + void QxSqlGenerator_Standard::onAfterUpdate(IxDao_Helper *pDaoHelper, void *pOwner) const + { + Q_UNUSED(pDaoHelper); + Q_UNUSED(pOwner); + } + + void QxSqlGenerator_Standard::onBeforeDelete(IxDao_Helper *pDaoHelper, void *pOwner) const + { + Q_UNUSED(pDaoHelper); + Q_UNUSED(pOwner); + } + + void QxSqlGenerator_Standard::onAfterDelete(IxDao_Helper *pDaoHelper, void *pOwner) const + { + Q_UNUSED(pDaoHelper); + Q_UNUSED(pOwner); + } + + void QxSqlGenerator_Standard::checkSqlInsert(IxDao_Helper *pDaoHelper, QString &sql) const + { + Q_UNUSED(pDaoHelper); + Q_UNUSED(sql); + } + + void QxSqlGenerator_Standard::onBeforeSqlPrepare(IxDao_Helper *pDaoHelper, QString &sql) const + { + Q_UNUSED(pDaoHelper); + Q_UNUSED(sql); + } + + void QxSqlGenerator_Standard::formatSqlQuery(IxDao_Helper *pDaoHelper, QString &sql) const + { + Q_UNUSED(pDaoHelper); + + if (sql.startsWith("SELECT ")) + { + sql = "\nSELECT " + sql.right(sql.size() - 7); + } + else if (sql.startsWith("INSERT ")) + { + sql = "\nINSERT " + sql.right(sql.size() - 7); + } + else if (sql.startsWith("UPDATE ")) + { + sql = "\nUPDATE " + sql.right(sql.size() - 7); + } + else if (sql.startsWith("DELETE ")) + { + sql = "\nDELETE " + sql.right(sql.size() - 7); + } + else if (sql.startsWith("CREATE ")) + { + sql = "\nCREATE " + sql.right(sql.size() - 7); + } + + sql.replace(" FROM ", "\n FROM "); + sql.replace(" WHERE ", "\n WHERE "); + sql.replace(" LEFT OUTER JOIN ", "\n LEFT OUTER JOIN "); + sql.replace(" INNER JOIN ", "\n INNER JOIN "); + sql.replace(" ORDER BY ", "\n ORDER BY "); + sql.replace(" GROUP BY ", "\n GROUP BY "); + sql.replace(" AND ", "\n AND "); + sql.replace(" OR ", "\n OR "); + sql.replace(" VALUES ", "\n VALUES "); + sql.replace(" SET ", "\n SET "); + sql.replace(" RETURNING ", "\n RETURNING "); + sql.replace(" LIMIT ", "\n LIMIT "); + sql += "\n"; + } + + } // namespace detail + } // namespace dao +} // namespace qx diff --git a/src/QxDao/QxSqlQuery.cpp b/src/QxDao/QxSqlQuery.cpp new file mode 100644 index 0000000..ec92613 --- /dev/null +++ b/src/QxDao/QxSqlQuery.cpp @@ -0,0 +1,1604 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +namespace qx +{ + + QxSqlQuery::QxSqlQuery() : m_iSqlElementIndex(0), m_iParenthesisCount(0), m_bDistinct(false) { m_sQuery << QString(); } + + QxSqlQuery::QxSqlQuery(const char *query, const QVariantList &values /* = QVariantList() */) : m_iSqlElementIndex(0), m_iParenthesisCount(0), m_bDistinct(false) + { + if (values.count() <= 0) + { + m_sQuery << QString(query); + } + else + { + addFreeText(query, values); + } + } + + QxSqlQuery::QxSqlQuery(const QString &query, const QVariantList &values /* = QVariantList() */) : m_iSqlElementIndex(0), m_iParenthesisCount(0), m_bDistinct(false) + { + if (values.count() <= 0) + { + m_sQuery << query; + } + else + { + addFreeText(query, values); + } + } + + QxSqlQuery::QxSqlQuery(const QStringList &query) : m_sQuery(query), m_iSqlElementIndex(0), m_iParenthesisCount(0), m_bDistinct(false) { ; } + + QxSqlQuery::QxSqlQuery(const QString &type, const QString &query) : m_iSqlElementIndex(0), m_iParenthesisCount(0), m_bDistinct(false), m_sType(type) { m_sQuery << query; } + + QxSqlQuery::QxSqlQuery(const QString &type, const QStringList &query) : m_sQuery(query), m_iSqlElementIndex(0), m_iParenthesisCount(0), m_bDistinct(false), m_sType(type) { ; } + +#ifndef _QX_NO_JSON +#ifdef Q_COMPILER_INITIALIZER_LISTS + + QxSqlQuery::QxSqlQuery(std::initializer_list> json) : m_iSqlElementIndex(0), m_iParenthesisCount(0), m_bDistinct(false) + { + QJsonObject obj; + for (std::initializer_list>::const_iterator itr = json.begin(); itr != json.end(); ++itr) + { + obj.insert(itr->first, itr->second); + } + QJsonDocument doc(obj); + m_sQuery << QString::fromUtf8(doc.toJson()); + } + + QxSqlQuery::QxSqlQuery(std::initializer_list> json, std::initializer_list> opts) : m_iSqlElementIndex(0), m_iParenthesisCount(0), m_bDistinct(false) + { + QJsonObject obj1; + for (std::initializer_list>::const_iterator itr = json.begin(); itr != json.end(); ++itr) + { + obj1.insert(itr->first, itr->second); + } + QJsonObject obj2; + for (std::initializer_list>::const_iterator itr = opts.begin(); itr != opts.end(); ++itr) + { + obj2.insert(itr->first, itr->second); + } + QJsonDocument doc1(obj1); + QJsonDocument doc2(obj2); + m_sQuery << QString::fromUtf8(doc1.toJson()) << QString::fromUtf8(doc2.toJson()); + } + + QxSqlQuery::QxSqlQuery(const QString &type, std::initializer_list> json) : m_iSqlElementIndex(0), m_iParenthesisCount(0), m_bDistinct(false), m_sType(type) + { + QJsonObject obj; + for (std::initializer_list>::const_iterator itr = json.begin(); itr != json.end(); ++itr) + { + obj.insert(itr->first, itr->second); + } + QJsonDocument doc(obj); + m_sQuery << QString::fromUtf8(doc.toJson()); + } + + QxSqlQuery::QxSqlQuery(const QString &type, std::initializer_list> json, std::initializer_list> opts) : m_iSqlElementIndex(0), m_iParenthesisCount(0), m_bDistinct(false), m_sType(type) + { + QJsonObject obj1; + for (std::initializer_list>::const_iterator itr = json.begin(); itr != json.end(); ++itr) + { + obj1.insert(itr->first, itr->second); + } + QJsonObject obj2; + for (std::initializer_list>::const_iterator itr = opts.begin(); itr != opts.end(); ++itr) + { + obj2.insert(itr->first, itr->second); + } + QJsonDocument doc1(obj1); + QJsonDocument doc2(obj2); + m_sQuery << QString::fromUtf8(doc1.toJson()) << QString::fromUtf8(doc2.toJson()); + } + +#endif // Q_COMPILER_INITIALIZER_LISTS +#endif // _QX_NO_JSON + + QxSqlQuery::~QxSqlQuery() { ; } + + void QxSqlQuery::verifyQuery() const + { +#ifdef _QX_MODE_DEBUG + if (queryAt(0).isEmpty() || (m_lstSqlElement.count() <= 0)) + { + return; + } + qDebug("[QxOrm] qx::QxSqlQuery::verifyQuery() : '%s'", "invalid SQL query, you cannot mix classic SQL and C++ syntax"); + qAssert(false); +#endif // _QX_MODE_DEBUG + } + + QString QxSqlQuery::query() + { + verifyQuery(); + if (m_lstSqlElement.count() <= 0) + { + return queryAt(0); + } + while (m_iParenthesisCount > 0) + { + closeParenthesis(); + } + + QString sQuery; + for (int i = 0; i < m_lstSqlElement.count(); i++) + { + sQuery += m_lstSqlElement.at(i)->toString() + " "; + } + return sQuery; + } + + QString QxSqlQuery::queryAt(int idx) const + { + if ((idx < 0) || (idx >= m_sQuery.count())) + { + return QString(); + } + return m_sQuery.at(idx); + } + + void QxSqlQuery::queryAt(int idx, const QString &query) + { + while ((m_sQuery.count() - 1) < idx) + { + m_sQuery << QString(); + } + m_sQuery.replace(idx, query); + qAssert(m_sQuery.at(idx) == query); + } + + QVariant QxSqlQuery::response() const + { + return m_vResponse; + } + + void QxSqlQuery::setResponse(const QVariant &v) + { + m_vResponse = v; + } + + QString QxSqlQuery::type() const + { + return m_sType; + } + + void QxSqlQuery::setType(const QString &s) + { + m_sType = s; + } + + bool QxSqlQuery::isEmpty() const + { + return (queryAt(0).isEmpty() && (m_lstSqlElement.count() <= 0) && (m_lstJoinQueryUser.count() <= 0)); + } + + bool QxSqlQuery::isDistinct() const + { + return m_bDistinct; + } + + void QxSqlQuery::clear() + { + m_sQuery.clear(); + m_lstValue.clear(); + m_pSqlElementTemp.reset(); + m_lstSqlElement.clear(); + m_iSqlElementIndex = 0; + m_iParenthesisCount = 0; + m_vResponse = QVariant(); + m_sType = ""; + m_lstJoinQueryUser.clear(); + m_lstJoinQueryToResolve.clear(); + } + + QxSqlQuery &QxSqlQuery::query(const QString &sQuery) + { + if (!sQuery.isEmpty()) + { + qAssert(m_lstSqlElement.count() <= 0); + } + + clear(); + m_sQuery << sQuery; + return (*this); + } + + QxSqlQuery &QxSqlQuery::bind(const QVariant &vValue, QSql::ParamType paramType /* = QSql::In */) + { + verifyQuery(); + qAssert(m_lstSqlElement.count() <= 0); + qAssert(!queryAt(0).isEmpty() && (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark)); + + QString sKey = QString::number(m_lstValue.count() + 1); + m_lstValue.insert(sKey, type_bind_value(vValue, paramType)); + return (*this); + } + + QxSqlQuery &QxSqlQuery::bind(const QString &sKey, const QVariant &vValue, QSql::ParamType paramType /* = QSql::In */) + { + verifyQuery(); + qAssert(m_lstSqlElement.count() <= 0); + qAssert(!queryAt(0).isEmpty() && (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() != qx::QxSqlDatabase::ph_style_question_mark)); + + if (sKey.isEmpty() || m_lstValue.exist(sKey)) + { + qAssert(false); + return (*this); + } + if (!queryAt(0).contains(sKey)) + { + qAssert(false); + return (*this); + } + m_lstValue.insert(sKey, type_bind_value(vValue, paramType)); + return (*this); + } + + QVariant QxSqlQuery::boundValue(const QString &sKey) const + { + if (sKey.isEmpty() || !m_lstValue.exist(sKey)) + { + qAssert(false); + return QVariant(); + } + return std::get<0>(m_lstValue.getByKey(sKey)); + } + + QVariant QxSqlQuery::boundValue(int iPosition) const + { + if ((iPosition < 0) || (iPosition >= m_lstValue.count())) + { + qAssert(false); + return QVariant(); + } + return std::get<0>(m_lstValue.getByIndex(iPosition)); + } + + void QxSqlQuery::resolve(QSqlQuery &query, qx::QxCollection *pLstExecBatch /* = NULL */) const + { + verifyQuery(); + + for (int i = 0; i < m_lstJoinQueryToResolve.count(); i++) + { + std::shared_ptr joinQuery = m_lstJoinQueryToResolve.at(i); + joinQuery->resolve(query, pLstExecBatch); + } + + if (m_lstSqlElement.count() > 0) + { + for (int i = 0; i < m_lstSqlElement.count(); i++) + { + m_lstSqlElement.at(i)->resolve(query, pLstExecBatch); + } + return; + } + + bool bQuestionMark = (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark); + QxCollectionIterator itr(m_lstValue); + while (itr.next()) + { + if (pLstExecBatch) + { + QString key = itr.key(); + if (!pLstExecBatch->exist(key)) + { + QVariantList empty; + pLstExecBatch->insert(key, empty); + } + QVariantList &values = const_cast(pLstExecBatch->getByKey(key)); + values.append(std::get<0>(itr.value())); + } + else + { + if (bQuestionMark) + { + query.addBindValue(std::get<0>(itr.value()), std::get<1>(itr.value())); + } + else + { + query.bindValue(itr.key(), std::get<0>(itr.value()), std::get<1>(itr.value())); + } + } + } + } + + void QxSqlQuery::resolveOutput(QSqlQuery &query, bool bFetchSqlResult) + { + QxCollection lst; + lst.reserve(m_lstValue.count()); + bool bQuestionMark = (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark); + for (long l = 0; l < m_lstValue.count(); l++) + { + QVariant outputValue; + QString key = m_lstValue.getKeyByIndex(l); + type_bind_value val = m_lstValue.getByIndex(l); + QSql::ParamType paramType = std::get<1>(val); + if (paramType == QSql::In) + { + lst.insert(key, val); + continue; + } + if (bQuestionMark) + { + outputValue = query.boundValue(l); + } + else + { + outputValue = query.boundValue(key); + } + lst.insert(key, type_bind_value(outputValue, paramType)); + } + if (bFetchSqlResult) + { + fetchSqlResult(query); + } + m_lstValue = lst; + } + + QString QxSqlQuery::getJoinQuery(const QString &relationKey, const QString &relationAlias) + { + if (m_lstJoinQueryUser.contains(relationKey)) + { + std::shared_ptr query = m_lstJoinQueryUser.value(relationKey); + m_lstJoinQueryToResolve.append(query); + return query->query().trimmed(); + } + else if (m_lstJoinQueryUser.contains(relationAlias)) + { + std::shared_ptr query = m_lstJoinQueryUser.value(relationAlias); + m_lstJoinQueryToResolve.append(query); + return query->query().trimmed(); + } + return QString(); + } + + QString QxSqlQuery::getJoinQueryHash() + { + QString hash; + QHashIterator> itr(m_lstJoinQueryUser); + while (itr.hasNext()) + { + itr.next(); + std::shared_ptr tmp = itr.value(); + hash += "|" + itr.key() + "|" + tmp->query(); + } + return hash; + } + + void QxSqlQuery::dumpBoundValues(const QSqlQuery &query) + { + QString sBoundValues = ""; +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QVariantList lstBoundValues = query.boundValues(); +#else // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QMap lstBoundValues = query.boundValues(); +#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + if (lstBoundValues.count() <= 0) + { + return; + } + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + for (int i = 0; i < lstBoundValues.size(); ++i) + { + sBoundValues += "\n - position '" + QString::number(i) + "' : " + lstBoundValues.at(i).toString(); + } +#else // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + if (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark) + { + QList lst = lstBoundValues.values(); + for (int i = 0; i < lst.size(); ++i) + { + sBoundValues += "\n - position '" + QString::number(i) + "' : " + lst.at(i).toString(); + } + } + else + { + QMapIterator itr(lstBoundValues); + while (itr.hasNext()) + { + itr.next(); + sBoundValues += "\n - " + itr.key() + " : " + itr.value().toString(); + } + } +#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + + if (!sBoundValues.isEmpty()) + { + qDebug("[QxOrm] dump sql query bound values : %s", qPrintable(sBoundValues)); + } + } + + QxSqlQuery &QxSqlQuery::setFctOnBeforeSqlPrepare(QxSqlQuery::type_fct_on_before_sql_prepare fct) + { + m_fctOnBeforeSqlPrepare = fct; + return (*this); + } + + void QxSqlQuery::onBeforeSqlPrepare(QString &sql) + { + if (m_fctOnBeforeSqlPrepare) + { + m_fctOnBeforeSqlPrepare(sql); + } + } + + void QxSqlQuery::fetchSqlResult(QSqlQuery &query) + { + bool bCheckRecord = true; + m_pSqlResult = std::make_shared(); + if (query.size() > 0) + { + m_pSqlResult->values.reserve(query.size()); + } + while (query.next()) + { + if (bCheckRecord) + { + bCheckRecord = false; + QSqlRecord record = query.record(); + m_pSqlResult->positionByKey.reserve(record.count()); + for (int i = 0; i < record.count(); i++) + { + m_pSqlResult->positionByKey.insert(record.fieldName(i), i); + } + qAssert(record.count() == m_pSqlResult->positionByKey.count()); + } + QVector lst; + lst.reserve(m_pSqlResult->positionByKey.count()); + for (long j = 0; j < m_pSqlResult->positionByKey.count(); j++) + { + lst.append(query.value(j)); + } + qAssert(lst.count() == m_pSqlResult->positionByKey.count()); + m_pSqlResult->values.append(lst); + } + } + + long QxSqlQuery::getSqlResultRowCount() const + { + if (!m_pSqlResult) + { + return 0; + } + return m_pSqlResult->values.count(); + } + + long QxSqlQuery::getSqlResultColumnCount() const + { + if (!m_pSqlResult) + { + return 0; + } + return m_pSqlResult->positionByKey.count(); + } + + QVariant QxSqlQuery::getSqlResultAt(long row, long column) const + { + if (!m_pSqlResult) + { + return QVariant(); + } + if ((row < 0) || (row >= m_pSqlResult->values.count())) + { + return QVariant(); + } + if ((column < 0) || (column >= m_pSqlResult->positionByKey.count())) + { + return QVariant(); + } + return m_pSqlResult->values.at(row).at(column); + } + + QVariant QxSqlQuery::getSqlResultAt(long row, const QString &column, bool caseSensitive /* = false */) const + { + if (!m_pSqlResult) + { + return QVariant(); + } + if ((row < 0) || (row >= m_pSqlResult->values.count())) + { + return QVariant(); + } + + int i(-1); + int col = m_pSqlResult->positionByKey.value(column, i); + if (col >= 0) + { + return m_pSqlResult->values.at(row).at(col); + } + if (caseSensitive) + { + return QVariant(); + } + + if (m_pSqlResult->positionByKeyUpper.count() <= 0) + { + QHashIterator itr(m_pSqlResult->positionByKey); + while (itr.hasNext()) + { + itr.next(); + QString keyUpper = itr.key().toUpper(); + int val = itr.value(); + m_pSqlResult->positionByKeyUpper.insert(keyUpper, val); + } + } + + QString columnUpper = column.toUpper(); + int j(-1); + col = m_pSqlResult->positionByKeyUpper.value(columnUpper, j); + return ((col >= 0) ? m_pSqlResult->values.at(row).at(col) : QVariant()); + } + + QVector QxSqlQuery::getSqlResultAt(long row) const + { + if (!m_pSqlResult) + { + return QVector(); + } + if ((row < 0) || (row >= m_pSqlResult->values.count())) + { + return QVector(); + } + return m_pSqlResult->values.at(row); + } + + QVector QxSqlQuery::getSqlResultAllColumns() const + { + if (!m_pSqlResult) + { + return QVector(); + } + QVector lstAllColumns(m_pSqlResult->positionByKey.count()); + QHashIterator itr(m_pSqlResult->positionByKey); + while (itr.hasNext()) + { + itr.next(); + lstAllColumns[itr.value()] = itr.key(); + } + return lstAllColumns; + } + + void QxSqlQuery::dumpSqlResult() + { + if (!m_pSqlResult) + { + return; + } + QString sql(this->query()), sColumns("#"), sOutput; + qDebug("[QxOrm] start dump sql result : '%s'", qPrintable(sql)); + QVector lstColumns = this->getSqlResultAllColumns(); + for (long i = 0; i < lstColumns.count(); i++) + { + sColumns += "|" + lstColumns.at(i); + } + qDebug("%s", qPrintable(sColumns)); + for (long j = 0; j < m_pSqlResult->values.count(); j++) + { + sOutput = QString::number(j); + for (long k = 0; k < m_pSqlResult->positionByKey.count(); k++) + { + sOutput += "|" + m_pSqlResult->values.at(j).at(k).toString(); + } + qDebug("%s", qPrintable(sOutput)); + } + qDebug("[QxOrm] end dump sql result : '%s'", qPrintable(sql)); + } + + void QxSqlQuery::postProcess(QString &sql) const + { + verifyQuery(); + for (int i = 0; i < m_lstSqlElement.count(); i++) + { + m_lstSqlElement.at(i)->postProcess(sql); + } + } + + QxSqlQuery &QxSqlQuery::distinct() + { + m_bDistinct = true; + return (*this); + } + + QxSqlQuery &QxSqlQuery::where(const QString &column) + { + return addSqlExpression(column, qx::dao::detail::QxSqlExpression::_where); + } + + QxSqlQuery &QxSqlQuery::where_OpenParenthesis(const QString &column) + { + where(column); + return openParenthesis(); + } + + QxSqlQuery &QxSqlQuery::and_(const QString &column) + { + return addSqlExpression(column, qx::dao::detail::QxSqlExpression::_and); + } + + QxSqlQuery &QxSqlQuery::and_OpenParenthesis(const QString &column) + { + and_(column); + return openParenthesis(); + } + + QxSqlQuery &QxSqlQuery::or_(const QString &column) + { + return addSqlExpression(column, qx::dao::detail::QxSqlExpression::_or); + } + + QxSqlQuery &QxSqlQuery::or_OpenParenthesis(const QString &column) + { + or_(column); + return openParenthesis(); + } + + QxSqlQuery &QxSqlQuery::openParenthesis() + { + qx::dao::detail::QxSqlExpression_ptr p; + p = std::make_shared(m_iSqlElementIndex++, qx::dao::detail::QxSqlExpression::_open_parenthesis); + m_lstSqlElement.append(p); + m_iParenthesisCount++; + return (*this); + } + + QxSqlQuery &QxSqlQuery::closeParenthesis() + { + if (m_iParenthesisCount <= 0) + { + return (*this); + } + qx::dao::detail::QxSqlExpression_ptr p; + p = std::make_shared(m_iSqlElementIndex++, qx::dao::detail::QxSqlExpression::_close_parenthesis); + m_lstSqlElement.append(p); + m_iParenthesisCount--; + m_pSqlElementTemp.reset(); + return (*this); + } + + QxSqlQuery &QxSqlQuery::orderAsc(const QStringList &columns) + { + return addSqlSort(columns, qx::dao::detail::QxSqlSort::_order_asc); + } + + QxSqlQuery &QxSqlQuery::orderAsc(const QString &col1) + { + return orderAsc(QStringList() << col1); + } + + QxSqlQuery &QxSqlQuery::orderAsc(const QString &col1, const QString &col2) + { + return orderAsc(QStringList() << col1 << col2); + } + + QxSqlQuery &QxSqlQuery::orderAsc(const QString &col1, const QString &col2, const QString &col3) + { + return orderAsc(QStringList() << col1 << col2 << col3); + } + + QxSqlQuery &QxSqlQuery::orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4) + { + return orderAsc(QStringList() << col1 << col2 << col3 << col4); + } + + QxSqlQuery &QxSqlQuery::orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5) + { + return orderAsc(QStringList() << col1 << col2 << col3 << col4 << col5); + } + + QxSqlQuery &QxSqlQuery::orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6) + { + return orderAsc(QStringList() << col1 << col2 << col3 << col4 << col5 << col6); + } + + QxSqlQuery &QxSqlQuery::orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7) + { + return orderAsc(QStringList() << col1 << col2 << col3 << col4 << col5 << col6 << col7); + } + + QxSqlQuery &QxSqlQuery::orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8) + { + return orderAsc(QStringList() << col1 << col2 << col3 << col4 << col5 << col6 << col7 << col8); + } + + QxSqlQuery &QxSqlQuery::orderAsc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8, const QString &col9) + { + return orderAsc(QStringList() << col1 << col2 << col3 << col4 << col5 << col6 << col7 << col8 << col9); + } + + QxSqlQuery &QxSqlQuery::orderDesc(const QStringList &columns) + { + return addSqlSort(columns, qx::dao::detail::QxSqlSort::_order_desc); + } + + QxSqlQuery &QxSqlQuery::orderDesc(const QString &col1) + { + return orderDesc(QStringList() << col1); + } + + QxSqlQuery &QxSqlQuery::orderDesc(const QString &col1, const QString &col2) + { + return orderDesc(QStringList() << col1 << col2); + } + + QxSqlQuery &QxSqlQuery::orderDesc(const QString &col1, const QString &col2, const QString &col3) + { + return orderDesc(QStringList() << col1 << col2 << col3); + } + + QxSqlQuery &QxSqlQuery::orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4) + { + return orderDesc(QStringList() << col1 << col2 << col3 << col4); + } + + QxSqlQuery &QxSqlQuery::orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5) + { + return orderDesc(QStringList() << col1 << col2 << col3 << col4 << col5); + } + + QxSqlQuery &QxSqlQuery::orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6) + { + return orderDesc(QStringList() << col1 << col2 << col3 << col4 << col5 << col6); + } + + QxSqlQuery &QxSqlQuery::orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7) + { + return orderDesc(QStringList() << col1 << col2 << col3 << col4 << col5 << col6 << col7); + } + + QxSqlQuery &QxSqlQuery::orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8) + { + return orderDesc(QStringList() << col1 << col2 << col3 << col4 << col5 << col6 << col7 << col8); + } + + QxSqlQuery &QxSqlQuery::orderDesc(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8, const QString &col9) + { + return orderDesc(QStringList() << col1 << col2 << col3 << col4 << col5 << col6 << col7 << col8 << col9); + } + + QxSqlQuery &QxSqlQuery::groupBy(const QStringList &columns) + { + return addSqlSort(columns, qx::dao::detail::QxSqlSort::_group_by); + } + + QxSqlQuery &QxSqlQuery::groupBy(const QString &col1) + { + return groupBy(QStringList() << col1); + } + + QxSqlQuery &QxSqlQuery::groupBy(const QString &col1, const QString &col2) + { + return groupBy(QStringList() << col1 << col2); + } + + QxSqlQuery &QxSqlQuery::groupBy(const QString &col1, const QString &col2, const QString &col3) + { + return groupBy(QStringList() << col1 << col2 << col3); + } + + QxSqlQuery &QxSqlQuery::groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4) + { + return groupBy(QStringList() << col1 << col2 << col3 << col4); + } + + QxSqlQuery &QxSqlQuery::groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5) + { + return groupBy(QStringList() << col1 << col2 << col3 << col4 << col5); + } + + QxSqlQuery &QxSqlQuery::groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6) + { + return groupBy(QStringList() << col1 << col2 << col3 << col4 << col5 << col6); + } + + QxSqlQuery &QxSqlQuery::groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7) + { + return groupBy(QStringList() << col1 << col2 << col3 << col4 << col5 << col6 << col7); + } + + QxSqlQuery &QxSqlQuery::groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8) + { + return groupBy(QStringList() << col1 << col2 << col3 << col4 << col5 << col6 << col7 << col8); + } + + QxSqlQuery &QxSqlQuery::groupBy(const QString &col1, const QString &col2, const QString &col3, const QString &col4, const QString &col5, const QString &col6, const QString &col7, const QString &col8, const QString &col9) + { + return groupBy(QStringList() << col1 << col2 << col3 << col4 << col5 << col6 << col7 << col8 << col9); + } + + QxSqlQuery &QxSqlQuery::limit(int rowsCount, int startRow /* = 0 */, bool withTies /* = false */) + { + qx::dao::detail::QxSqlLimit_ptr p; + p = std::make_shared(m_iSqlElementIndex++); + p->setValues(QVariantList() << QVariant(startRow) << QVariant(rowsCount) << QVariant(withTies)); + m_lstSqlElement.append(p); + return (*this); + } + + QxSqlQuery &QxSqlQuery::like(const QString &val) + { + return addSqlCompare(QVariant(val), qx::dao::detail::QxSqlCompare::_like); + } + + QxSqlQuery &QxSqlQuery::notLike(const QString &val) + { + return addSqlCompare(QVariant(val), qx::dao::detail::QxSqlCompare::_not_like); + } + + QxSqlQuery &QxSqlQuery::startsWith(const QString &val) + { + return addSqlCompare(QVariant(val), qx::dao::detail::QxSqlCompare::_starts_with); + } + + QxSqlQuery &QxSqlQuery::endsWith(const QString &val) + { + return addSqlCompare(QVariant(val), qx::dao::detail::QxSqlCompare::_ends_with); + } + + QxSqlQuery &QxSqlQuery::containsString(const QString &val) + { + return addSqlCompare(QVariant(val), qx::dao::detail::QxSqlCompare::_contains_string); + } + + QxSqlQuery &QxSqlQuery::isEqualTo(const QVariant &val) + { + return addSqlCompare(val, qx::dao::detail::QxSqlCompare::_is_equal_to); + } + + QxSqlQuery &QxSqlQuery::isNotEqualTo(const QVariant &val) + { + return addSqlCompare(val, qx::dao::detail::QxSqlCompare::_is_not_equal_to); + } + + QxSqlQuery &QxSqlQuery::isGreaterThan(const QVariant &val) + { + return addSqlCompare(val, qx::dao::detail::QxSqlCompare::_is_greater_than); + } + + QxSqlQuery &QxSqlQuery::isGreaterThanOrEqualTo(const QVariant &val) + { + return addSqlCompare(val, qx::dao::detail::QxSqlCompare::_is_greater_than_or_equal_to); + } + + QxSqlQuery &QxSqlQuery::isLessThan(const QVariant &val) + { + return addSqlCompare(val, qx::dao::detail::QxSqlCompare::_is_less_than); + } + + QxSqlQuery &QxSqlQuery::isLessThanOrEqualTo(const QVariant &val) + { + return addSqlCompare(val, qx::dao::detail::QxSqlCompare::_is_less_than_or_equal_to); + } + + QxSqlQuery &QxSqlQuery::customOperator(const QString &sCustomOperator, const QVariant &val) + { + return addSqlCompare(val, qx::dao::detail::QxSqlCompare::_custom_operator, sCustomOperator); + } + + QxSqlQuery &QxSqlQuery::in(const QVariantList &values) + { + return addSqlIn(values, qx::dao::detail::QxSqlIn::_in); + } + + QxSqlQuery &QxSqlQuery::in(const QVariant &val1) + { + return in(QVariantList() << val1); + } + + QxSqlQuery &QxSqlQuery::in(const QVariant &val1, const QVariant &val2) + { + return in(QVariantList() << val1 << val2); + } + + QxSqlQuery &QxSqlQuery::in(const QVariant &val1, const QVariant &val2, const QVariant &val3) + { + return in(QVariantList() << val1 << val2 << val3); + } + + QxSqlQuery &QxSqlQuery::in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4) + { + return in(QVariantList() << val1 << val2 << val3 << val4); + } + + QxSqlQuery &QxSqlQuery::in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5) + { + return in(QVariantList() << val1 << val2 << val3 << val4 << val5); + } + + QxSqlQuery &QxSqlQuery::in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6) + { + return in(QVariantList() << val1 << val2 << val3 << val4 << val5 << val6); + } + + QxSqlQuery &QxSqlQuery::in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7) + { + return in(QVariantList() << val1 << val2 << val3 << val4 << val5 << val6 << val7); + } + + QxSqlQuery &QxSqlQuery::in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7, const QVariant &val8) + { + return in(QVariantList() << val1 << val2 << val3 << val4 << val5 << val6 << val7 << val8); + } + + QxSqlQuery &QxSqlQuery::in(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7, const QVariant &val8, const QVariant &val9) + { + return in(QVariantList() << val1 << val2 << val3 << val4 << val5 << val6 << val7 << val8 << val9); + } + + QxSqlQuery &QxSqlQuery::notIn(const QVariantList &values) + { + return addSqlIn(values, qx::dao::detail::QxSqlIn::_not_in); + } + + QxSqlQuery &QxSqlQuery::notIn(const QVariant &val1) + { + return notIn(QVariantList() << val1); + } + + QxSqlQuery &QxSqlQuery::notIn(const QVariant &val1, const QVariant &val2) + { + return notIn(QVariantList() << val1 << val2); + } + + QxSqlQuery &QxSqlQuery::notIn(const QVariant &val1, const QVariant &val2, const QVariant &val3) + { + return notIn(QVariantList() << val1 << val2 << val3); + } + + QxSqlQuery &QxSqlQuery::notIn(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4) + { + return notIn(QVariantList() << val1 << val2 << val3 << val4); + } + + QxSqlQuery &QxSqlQuery::notIn(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5) + { + return notIn(QVariantList() << val1 << val2 << val3 << val4 << val5); + } + + QxSqlQuery &QxSqlQuery::notIn(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6) + { + return notIn(QVariantList() << val1 << val2 << val3 << val4 << val5 << val6); + } + + QxSqlQuery &QxSqlQuery::notIn(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7) + { + return notIn(QVariantList() << val1 << val2 << val3 << val4 << val5 << val6 << val7); + } + + QxSqlQuery &QxSqlQuery::notIn(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7, const QVariant &val8) + { + return notIn(QVariantList() << val1 << val2 << val3 << val4 << val5 << val6 << val7 << val8); + } + + QxSqlQuery &QxSqlQuery::notIn(const QVariant &val1, const QVariant &val2, const QVariant &val3, const QVariant &val4, const QVariant &val5, const QVariant &val6, const QVariant &val7, const QVariant &val8, const QVariant &val9) + { + return notIn(QVariantList() << val1 << val2 << val3 << val4 << val5 << val6 << val7 << val8 << val9); + } + + QxSqlQuery &QxSqlQuery::in_Select(const QxSqlQuery &query) + { + if (query.m_lstSqlElement.count() <= 0) + { + addSqlIn(QVariantList() << QVariant(query.queryAt(0)), qx::dao::detail::QxSqlIn::_in_select); + } + else + { + addEmbedQuery(query, qx::dao::detail::QxSqlEmbedQuery::_in, true); + } + return (*this); + } + + QxSqlQuery &QxSqlQuery::notIn_Select(const QxSqlQuery &query) + { + if (query.m_lstSqlElement.count() <= 0) + { + addSqlIn(QVariantList() << QVariant(query.queryAt(0)), qx::dao::detail::QxSqlIn::_not_in_select); + } + else + { + addEmbedQuery(query, qx::dao::detail::QxSqlEmbedQuery::_not_in, true); + } + return (*this); + } + + QxSqlQuery &QxSqlQuery::isEqualTo_Select(const QxSqlQuery &query) + { + if (query.m_lstSqlElement.count() <= 0) + { + addSqlCompare(query.queryAt(0), qx::dao::detail::QxSqlCompare::_is_equal_to_select); + } + else + { + addEmbedQuery(query, qx::dao::detail::QxSqlEmbedQuery::_is_equal_to, true); + } + return (*this); + } + + QxSqlQuery &QxSqlQuery::isNotEqualTo_Select(const QxSqlQuery &query) + { + if (query.m_lstSqlElement.count() <= 0) + { + addSqlCompare(query.queryAt(0), qx::dao::detail::QxSqlCompare::_is_not_equal_to_select); + } + else + { + addEmbedQuery(query, qx::dao::detail::QxSqlEmbedQuery::_is_not_equal_to, true); + } + return (*this); + } + + QxSqlQuery &QxSqlQuery::isNull() + { + return addSqlIsNull(qx::dao::detail::QxSqlIsNull::_is_null); + } + + QxSqlQuery &QxSqlQuery::isNotNull() + { + return addSqlIsNull(qx::dao::detail::QxSqlIsNull::_is_not_null); + } + + QxSqlQuery &QxSqlQuery::isBetween(const QVariant &val1, const QVariant &val2) + { + return addSqlIsBetween(val1, val2, qx::dao::detail::QxSqlIsBetween::_is_between); + } + + QxSqlQuery &QxSqlQuery::isNotBetween(const QVariant &val1, const QVariant &val2) + { + return addSqlIsBetween(val1, val2, qx::dao::detail::QxSqlIsBetween::_is_not_between); + } + + QxSqlQuery &QxSqlQuery::freeText(const QString &text, const QVariantList &values /* = QVariantList() */) + { + if (text.isEmpty()) + { + return (*this); + } + return addFreeText(text, values); + } + + QxSqlQuery &QxSqlQuery::addJoinQuery(const QString &relationKeyOrAlias, const QxSqlQuery &joinQuery) + { + std::shared_ptr pQuery = std::make_shared(joinQuery); + m_lstJoinQueryUser.insert(relationKeyOrAlias, pQuery); + return (*this); + } + + QxSqlQuery &QxSqlQuery::addSqlExpression(const QString &column, qx::dao::detail::QxSqlExpression::type type) + { + qx::dao::detail::QxSqlExpression_ptr p; + p = std::make_shared(m_iSqlElementIndex++, type); + m_lstSqlElement.append(p); + + m_pSqlElementTemp = std::make_shared(); + m_pSqlElementTemp->setColumn(column); + return (*this); + } + + QxSqlQuery &QxSqlQuery::addSqlCompare(const QVariant &val, qx::dao::detail::QxSqlCompare::type type, const QString &sCustomOperator /* = QString() */) + { + if (!m_pSqlElementTemp) + { + qDebug("[QxOrm] qx::QxSqlQuery::addSqlCompare : '%s'", "invalid SQL query, need a column name"); + qAssert(false); + return (*this); + } + + qx::dao::detail::QxSqlCompare_ptr p; + p = std::make_shared(m_iSqlElementIndex++, type, sCustomOperator); + p->clone(m_pSqlElementTemp.get()); + p->setValue(val); + + m_lstSqlElement.append(p); + m_pSqlElementTemp.reset(); + return (*this); + } + + QxSqlQuery &QxSqlQuery::addSqlSort(const QStringList &columns, qx::dao::detail::QxSqlSort::type type) + { + qx::dao::detail::QxSqlSort_ptr p; + p = std::make_shared(m_iSqlElementIndex++, type); + p->setColumns(columns); + m_lstSqlElement.append(p); + return (*this); + } + + QxSqlQuery &QxSqlQuery::addSqlIn(const QVariantList &values, qx::dao::detail::QxSqlIn::type type) + { + if (!m_pSqlElementTemp) + { + qDebug("[QxOrm] qx::QxSqlQuery::addSqlIn : '%s'", "invalid SQL query, need a column name"); + qAssert(false); + return (*this); + } + + qx::dao::detail::QxSqlIn_ptr p; + p = std::make_shared(m_iSqlElementIndex++, type); + p->clone(m_pSqlElementTemp.get()); + p->setValues(values); + + m_lstSqlElement.append(p); + m_pSqlElementTemp.reset(); + return (*this); + } + + QxSqlQuery &QxSqlQuery::addSqlIsNull(qx::dao::detail::QxSqlIsNull::type type) + { + if (!m_pSqlElementTemp) + { + qDebug("[QxOrm] qx::QxSqlQuery::addSqlIsNull : '%s'", "invalid SQL query, need a column name"); + qAssert(false); + return (*this); + } + + qx::dao::detail::QxSqlIsNull_ptr p; + p = std::make_shared(m_iSqlElementIndex++, type); + p->clone(m_pSqlElementTemp.get()); + + m_lstSqlElement.append(p); + m_pSqlElementTemp.reset(); + return (*this); + } + + QxSqlQuery &QxSqlQuery::addSqlIsBetween(const QVariant &val1, const QVariant &val2, qx::dao::detail::QxSqlIsBetween::type type) + { + if (!m_pSqlElementTemp) + { + qDebug("[QxOrm] qx::QxSqlQuery::addSqlIsBetween : '%s'", "invalid SQL query, need a column name"); + qAssert(false); + return (*this); + } + + qx::dao::detail::QxSqlIsBetween_ptr p; + p = std::make_shared(m_iSqlElementIndex++, type); + p->clone(m_pSqlElementTemp.get()); + p->setValues(QVariantList() << val1 << val2); + + m_lstSqlElement.append(p); + m_pSqlElementTemp.reset(); + return (*this); + } + + QxSqlQuery &QxSqlQuery::addFreeText(const QString &text, const QVariantList &values) + { + qx::dao::detail::QxSqlFreeText_ptr p; + p = std::make_shared(m_iSqlElementIndex++); + p->setText(text); + p->setValues(values); + m_lstSqlElement.append(p); + return (*this); + } + + QxSqlQuery &QxSqlQuery::addEmbedQuery(const QxSqlQuery &query, qx::dao::detail::QxSqlEmbedQuery::type type, bool requirePreviousElement) + { + if ((requirePreviousElement) && (!m_pSqlElementTemp)) + { + qDebug("[QxOrm] qx::QxSqlQuery::addEmbedQuery : '%s'", "invalid SQL query, need a column name"); + qAssert(false); + return (*this); + } + + qx::dao::detail::QxSqlEmbedQuery_ptr p; + p = std::make_shared(m_iSqlElementIndex++, type); + if (requirePreviousElement) + { + p->clone(m_pSqlElementTemp.get()); + } + p->setQuery(query); + + m_lstSqlElement.append(p); + m_pSqlElementTemp.reset(); + return (*this); + } + +} // namespace qx + +namespace qx +{ + namespace dao + { + + QSqlError call_query(qx::QxSqlQuery &query, QSqlDatabase *pDatabase /* = NULL */) { return qx::dao::helper::call_query_helper(query, pDatabase, true); } + + QSqlError call_query_without_prepare(qx::QxSqlQuery &query, QSqlDatabase *pDatabase /* = NULL */) { return qx::dao::helper::call_query_helper(query, pDatabase, false); } + + namespace helper + { + + QSqlError call_query_helper(qx::QxSqlQuery &query, QSqlDatabase *pDatabase, bool bPrepare) + { +#ifdef _QX_ENABLE_MONGODB + if (qx::QxSqlDatabase::getSingleton()->getDriverName() == "QXMONGODB") + { + return qx::dao::mongodb::QxMongoDB_Helper::executeCommand(NULL, NULL, (&query)); + } +#endif // _QX_ENABLE_MONGODB + + QSqlError dbError; + QSqlDatabase d = (pDatabase ? (*pDatabase) : qx::QxSqlDatabase::getDatabase(dbError)); + if (dbError.isValid()) + { + return dbError; + } + bool bBoundValues = qx::QxSqlDatabase::getSingleton()->getTraceSqlBoundValues(); + bool bBoundValuesOnError = qx::QxSqlDatabase::getSingleton()->getTraceSqlBoundValuesOnError(); + QElapsedTimer timer; + timer.start(); + QString sql = query.query(); + QSqlQuery q = QSqlQuery(d); + q.setForwardOnly(true); + + do + { + if (bPrepare && !q.prepare(sql)) + { + dbError = q.lastError(); + break; + } + query.resolve(q); + if (bPrepare) + { + if (!q.exec()) + { + dbError = q.lastError(); + break; + } + } + else + { + if (!q.exec(sql)) + { + dbError = q.lastError(); + break; + } + } + query.resolveOutput(q, true); + } while (0); + + qlonglong ms = static_cast(timer.elapsed()); + if (dbError.isValid()) + { + QString log = "custom sql query failed (" + QString::number(ms) + " ms) : " + sql; + qDebug("[QxOrm] %s", qPrintable(log)); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + QString serr = dbError.nativeErrorCode(); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + int ierr = dbError.number(); + QString serr = QString::number(ierr); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + QString tmp = dbError.driverText(); + qDebug("Database error number '%s' : %s", qPrintable(serr), qPrintable(tmp)); + tmp = dbError.databaseText(); + qDebug("%s", qPrintable(tmp)); + } + else if (qx::QxSqlDatabase::getSingleton()->getTraceSqlQuery()) + { + qlonglong iTraceSqlOnlySlowQueriesDatabase = static_cast(qx::QxSqlDatabase::getSingleton()->getTraceSqlOnlySlowQueriesDatabase()); + qlonglong iTraceSqlOnlySlowQueriesTotal = static_cast(qx::QxSqlDatabase::getSingleton()->getTraceSqlOnlySlowQueriesTotal()); + if ((iTraceSqlOnlySlowQueriesDatabase > 0) && (iTraceSqlOnlySlowQueriesTotal < 0)) + { + iTraceSqlOnlySlowQueriesTotal = 999999999; + } + else if ((iTraceSqlOnlySlowQueriesTotal > 0) && (iTraceSqlOnlySlowQueriesDatabase < 0)) + { + iTraceSqlOnlySlowQueriesDatabase = 999999999; + } + if ((ms >= iTraceSqlOnlySlowQueriesTotal) || (ms >= iTraceSqlOnlySlowQueriesDatabase)) + { + QString log = "custom sql query (" + QString::number(ms) + " ms) : " + sql; + qDebug("[QxOrm] %s", qPrintable(log)); + } + } + + if ((dbError.isValid() && bBoundValuesOnError) || (bBoundValues)) + { + qx::QxSqlQuery::dumpBoundValues(q); + } + return dbError; + } + + } // namespace helper + } // namespace dao +} // namespace qx + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const qx::QxSqlQuery &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QHash lstResultPosByKey; + QVector> lstResultValues; + qx::dao::detail::IxSqlElement::type_class eNoSqlType = qx::dao::detail::IxSqlElement::_no_type; + + if (t.m_pSqlResult) + { + lstResultPosByKey = t.m_pSqlResult->positionByKey; + lstResultValues = t.m_pSqlResult->values; + } + + ar << boost::serialization::make_nvp("query", t.m_sQuery); + ar << boost::serialization::make_nvp("list_values", t.m_lstValue); + ar << boost::serialization::make_nvp("sql_element_index", t.m_iSqlElementIndex); + ar << boost::serialization::make_nvp("parenthesis_count", t.m_iParenthesisCount); + ar << boost::serialization::make_nvp("distinct", t.m_bDistinct); + ar << boost::serialization::make_nvp("result_position_by_key", lstResultPosByKey); + ar << boost::serialization::make_nvp("result_values", lstResultValues); + ar << boost::serialization::make_nvp("response", t.m_vResponse); + ar << boost::serialization::make_nvp("type", t.m_sType); + ar << boost::serialization::make_nvp("list_join_query_user", t.m_lstJoinQueryUser); + ar << boost::serialization::make_nvp("list_join_query_resolve", t.m_lstJoinQueryToResolve); + + if (!t.m_pSqlElementTemp) + { + ar << boost::serialization::make_nvp("sql_element_temp_type", eNoSqlType); + } + else + { + qx::dao::detail::IxSqlElement::type_class eTypeSqlElement = t.m_pSqlElementTemp->getTypeClass(); + ar << boost::serialization::make_nvp("sql_element_temp_type", eTypeSqlElement); + t.m_pSqlElementTemp->qxSave(ar); + } + + long lSqlElementCount = t.m_lstSqlElement.count(); + ar << boost::serialization::make_nvp("sql_element_count", lSqlElementCount); + Q_FOREACH (qx::dao::detail::IxSqlElement_ptr pSqlElement, t.m_lstSqlElement) + { + if (!pSqlElement) + { + ar << boost::serialization::make_nvp("sql_element_type", eNoSqlType); + continue; + } + qx::dao::detail::IxSqlElement::type_class eTypeSqlElement = pSqlElement->getTypeClass(); + ar << boost::serialization::make_nvp("sql_element_type", eTypeSqlElement); + pSqlElement->qxSave(ar); + } + } + + template + inline void qx_load(Archive &ar, qx::QxSqlQuery &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QHash lstResultPosByKey; + QVector> lstResultValues; + + ar >> boost::serialization::make_nvp("query", t.m_sQuery); + ar >> boost::serialization::make_nvp("list_values", t.m_lstValue); + ar >> boost::serialization::make_nvp("sql_element_index", t.m_iSqlElementIndex); + ar >> boost::serialization::make_nvp("parenthesis_count", t.m_iParenthesisCount); + ar >> boost::serialization::make_nvp("distinct", t.m_bDistinct); + ar >> boost::serialization::make_nvp("result_position_by_key", lstResultPosByKey); + ar >> boost::serialization::make_nvp("result_values", lstResultValues); + ar >> boost::serialization::make_nvp("response", t.m_vResponse); + ar >> boost::serialization::make_nvp("type", t.m_sType); + ar >> boost::serialization::make_nvp("list_join_query_user", t.m_lstJoinQueryUser); + ar >> boost::serialization::make_nvp("list_join_query_resolve", t.m_lstJoinQueryToResolve); + + t.m_pSqlResult.reset(); + if ((lstResultPosByKey.count() > 0) || (lstResultValues.count() > 0)) + { + t.m_pSqlResult = std::shared_ptr(new qx::QxSqlQuery::QxSqlResult()); + t.m_pSqlResult->positionByKey = lstResultPosByKey; + t.m_pSqlResult->values = lstResultValues; + } + + t.m_pSqlElementTemp.reset(); + qx::dao::detail::IxSqlElement::type_class eTypeSqlElement = qx::dao::detail::IxSqlElement::_no_type; + ar >> boost::serialization::make_nvp("sql_element_temp_type", eTypeSqlElement); + if (eTypeSqlElement != qx::dao::detail::IxSqlElement::_no_type) + { + t.m_pSqlElementTemp = qx::dao::detail::create_sql_element(eTypeSqlElement); + qAssert(t.m_pSqlElementTemp); + if (t.m_pSqlElementTemp) + { + t.m_pSqlElementTemp->qxLoad(ar); + } + } + + t.m_lstSqlElement.clear(); + long lSqlElementCount = 0; + ar >> boost::serialization::make_nvp("sql_element_count", lSqlElementCount); + t.m_lstSqlElement.reserve(lSqlElementCount); + for (long l = 0; l < lSqlElementCount; l++) + { + qx::dao::detail::IxSqlElement_ptr pSqlElement; + eTypeSqlElement = qx::dao::detail::IxSqlElement::_no_type; + ar >> boost::serialization::make_nvp("sql_element_type", eTypeSqlElement); + if (eTypeSqlElement != qx::dao::detail::IxSqlElement::_no_type) + { + pSqlElement = qx::dao::detail::create_sql_element(eTypeSqlElement); + qAssert(pSqlElement); + if (pSqlElement) + { + pSqlElement->qxLoad(ar); + } + } + t.m_lstSqlElement.append(pSqlElement); + } + } + + } // namespace serialization +} // namespace boost + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(qx::QxSqlQuery) + +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +QDataStream &operator<<(QDataStream &stream, const qx::QxSqlQuery &t) +{ + QHash lstResultPosByKey; + QVector> lstResultValues; + qx::dao::detail::IxSqlElement::type_class eNoSqlType = qx::dao::detail::IxSqlElement::_no_type; + + if (t.m_pSqlResult) + { + lstResultPosByKey = t.m_pSqlResult->positionByKey; + lstResultValues = t.m_pSqlResult->values; + } + + stream << t.m_sQuery; + stream << t.m_lstValue; + stream << t.m_iSqlElementIndex; + stream << t.m_iParenthesisCount; + stream << t.m_bDistinct; + stream << lstResultPosByKey; + stream << lstResultValues; + stream << t.m_vResponse; + stream << t.m_sType; + stream << t.m_lstJoinQueryUser; + stream << t.m_lstJoinQueryToResolve; + + if (!t.m_pSqlElementTemp) + { + stream << (qint32)(eNoSqlType); + } + else + { + qx::dao::detail::IxSqlElement::type_class eTypeSqlElement = t.m_pSqlElementTemp->getTypeClass(); + stream << (qint32)(eTypeSqlElement); + stream << (*t.m_pSqlElementTemp); + } + + long lSqlElementCount = t.m_lstSqlElement.count(); + stream << (qint64)(lSqlElementCount); + Q_FOREACH (qx::dao::detail::IxSqlElement_ptr pSqlElement, t.m_lstSqlElement) + { + if (!pSqlElement) + { + stream << (qint32)(eNoSqlType); + continue; + } + qx::dao::detail::IxSqlElement::type_class eTypeSqlElement = pSqlElement->getTypeClass(); + stream << (qint32)(eTypeSqlElement); + stream << (*pSqlElement); + } + + return stream; +} + +QDataStream &operator>>(QDataStream &stream, qx::QxSqlQuery &t) +{ + QHash lstResultPosByKey; + QVector> lstResultValues; + qint32 i32Temp = 0; + + stream >> t.m_sQuery; + stream >> t.m_lstValue; + stream >> t.m_iSqlElementIndex; + stream >> t.m_iParenthesisCount; + stream >> t.m_bDistinct; + stream >> lstResultPosByKey; + stream >> lstResultValues; + stream >> t.m_vResponse; + stream >> t.m_sType; + stream >> t.m_lstJoinQueryUser; + stream >> t.m_lstJoinQueryToResolve; + + t.m_pSqlResult.reset(); + if ((lstResultPosByKey.count() > 0) || (lstResultValues.count() > 0)) + { + t.m_pSqlResult = std::shared_ptr(new qx::QxSqlQuery::QxSqlResult()); + t.m_pSqlResult->positionByKey = lstResultPosByKey; + t.m_pSqlResult->values = lstResultValues; + } + + t.m_pSqlElementTemp.reset(); + qx::dao::detail::IxSqlElement::type_class eTypeSqlElement = qx::dao::detail::IxSqlElement::_no_type; + stream >> i32Temp; + eTypeSqlElement = static_cast(i32Temp); + if (eTypeSqlElement != qx::dao::detail::IxSqlElement::_no_type) + { + t.m_pSqlElementTemp = qx::dao::detail::create_sql_element(eTypeSqlElement); + qAssert(t.m_pSqlElementTemp); + if (t.m_pSqlElementTemp) + { + stream >> (*t.m_pSqlElementTemp); + } + } + + t.m_lstSqlElement.clear(); + qint64 lSqlElementCount = 0; + stream >> lSqlElementCount; + t.m_lstSqlElement.reserve(static_cast(lSqlElementCount)); + for (long l = 0; l < static_cast(lSqlElementCount); l++) + { + qx::dao::detail::IxSqlElement_ptr pSqlElement; + eTypeSqlElement = qx::dao::detail::IxSqlElement::_no_type; + stream >> i32Temp; + eTypeSqlElement = static_cast(i32Temp); + if (eTypeSqlElement != qx::dao::detail::IxSqlElement::_no_type) + { + pSqlElement = qx::dao::detail::create_sql_element(eTypeSqlElement); + qAssert(pSqlElement); + if (pSqlElement) + { + stream >> (*pSqlElement); + } + } + t.m_lstSqlElement.append(pSqlElement); + } + + return stream; +} diff --git a/src/QxDao/QxSqlRelationLinked.cpp b/src/QxDao/QxSqlRelationLinked.cpp new file mode 100644 index 0000000..91aec20 --- /dev/null +++ b/src/QxDao/QxSqlRelationLinked.cpp @@ -0,0 +1,864 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include +#include +#include + +#include + +#include + +#include + +#include + +namespace qx +{ + + struct Q_DECL_HIDDEN QxSqlRelationLinked::QxSqlRelationLinkedImpl + { + + enum e_hierarchy_action + { + e_hierarchy_select, + e_hierarchy_from, + e_hierarchy_join, + e_hierarchy_where_soft_delete + }; + + /* + - qx::dao::sql_join::join_type eJoinType : LEFT OUTER JOIN / INNER JOIN / etc... + - IxSqlRelation * pRelation : relation instance + - QPair, long> lstColumns : list of columns ordered by index + - QString customAlias : custom SQL alias provided by user using syntax + - bool isNullColumn : NULL column (using {NULL} syntax) means : fetch relationships only in LEFT OUTER/INNER JOIN and WHERE clauses (so no columns in SELECT part) + */ + typedef std::tuple, long>, QString, bool> type_relation; + + typedef std::shared_ptr type_ptr; + typedef qx::QxCollection type_lst_relation; + typedef QHash type_lst_relation_linked; + + type_lst_relation m_relationX; //!< List of relationships for current level + type_lst_relation_linked m_relationLinkedX; //!< List of child to build the hierarchy + IxSqlRelationX *m_allRelationX; //!< List of all relationships per level + IxSqlRelationX_ptr m_allRelationXOrdered; //!< List of all relationships per level (order defined by user, used only when necessary, for example with custom join sub-queries) + bool m_bRoot; //!< Root of the hierarchy + QSet m_lstRootColumns; //!< Root columns to fetch (using syntax { col_1, col_2, etc... } ), if empty then fetch all root columns + long m_lRootColumnsOffset; //!< Root columns offset to resolve SQL query output + bool m_bRootColumnsModeRemove; //!< Special syntax to remove root columns instead of adding root columns : -{ column1, column2, etc... } + QString m_sRootCustomAlias; //!< Root custom alias using syntax + + static QMutex m_mutex; //!< Mutex => qx::QxSqlRelationLinked is thread-safe + static QHash, type_ptr> m_relationLinkedSaved; //!< Keep relations linked in memory for optimization (to avoid too many parsing) + + QxSqlRelationLinkedImpl(bool bRoot) : m_allRelationX(NULL), m_bRoot(bRoot), m_lRootColumnsOffset(0), m_bRootColumnsModeRemove(false) { ; } + ~QxSqlRelationLinkedImpl() { ; } + + qx_bool buildHierarchy(IxSqlRelationX *pRelationX, const QStringList &sRelationX, qx::dao::detail::IxDao_Helper *pDaoHelper); + qx_bool insertRelationToHierarchy(const QStringList &sRelationX, const QString &sKey, qx::dao::sql_join::join_type eJoinType, qx::dao::detail::IxDao_Helper *pDaoHelper); + QStringList removeColumns(const QStringList &columnsToRemove, IxSqlRelation *pRelation) const; + + bool checkRootColumns(const QString &s) const { return (m_lstRootColumns.isEmpty() || (m_bRootColumnsModeRemove ? (!m_lstRootColumns.contains(s)) : m_lstRootColumns.contains(s))); } + + void hierarchyAction(QxSqlRelationParams ¶ms, e_hierarchy_action action) + { + if (m_bRoot) + { + params.setIndex(0); + params.setIndexOwner(0); + params.setCustomAlias(m_sRootCustomAlias); + params.setCustomAliasOwner(m_sRootCustomAlias); + } + IxSqlRelationX *pAllRelationX = (m_allRelationXOrdered ? m_allRelationXOrdered.get() : m_allRelationX); + if (!pAllRelationX) + { + qAssert(false); + return; + } + for (auto itr = pAllRelationX->begin(); itr != pAllRelationX->end(); ++itr) + { + IxSqlRelation *p = itr->second; + if (!p) + { + continue; + } + params.setIndex(params.index() + 1); + params.setRelationX(&m_relationLinkedX); + params.setJoinType(qx::dao::sql_join::no_join); + + if (!m_relationX.exist(p->getKey())) + { + if (m_bRoot && checkRootColumns(p->getKey())) + { + switch (action) + { + case e_hierarchy_select: + p->lazySelect(params); + break; + case e_hierarchy_from: + p->lazyFrom(params); + break; + case e_hierarchy_join: + p->lazyJoin(params); + break; + case e_hierarchy_where_soft_delete: + p->lazyWhereSoftDelete(params); + break; + default: + qAssert(false); + break; + } + } + continue; + } + + QxSqlRelationLinkedImpl::type_relation &temp = const_cast(m_relationX.getByKey(p->getKey())); + params.setJoinType(std::get<0>(temp)); + params.setColumns(&std::get<2>(temp)); + params.setCustomAlias(std::get<3>(temp)); + + switch (action) + { + case e_hierarchy_select: + if (!std::get<4>(temp)) + { + p->eagerSelect(params); + } + break; + case e_hierarchy_from: + p->eagerFrom(params); + break; + case e_hierarchy_join: + p->eagerJoin(params); + break; + case e_hierarchy_where_soft_delete: + p->eagerWhereSoftDelete(params); + break; + default: + qAssert(false); + break; + } + + QxSqlRelationLinked_ptr pRelationLinked = m_relationLinkedX.value(p->getKey()); + if (!pRelationLinked) + { + continue; + } + long lOldIndexOwner = params.indexOwner(); + params.setIndexOwner(params.index()); + QString sOldCustomAliasOwner = params.getCustomAliasOwner(); + params.setCustomAliasOwner(std::get<3>(temp)); + + switch (action) + { + case e_hierarchy_select: + pRelationLinked->hierarchySelect(params); + break; + case e_hierarchy_from: + pRelationLinked->hierarchyFrom(params); + break; + case e_hierarchy_join: + pRelationLinked->hierarchyJoin(params); + break; + case e_hierarchy_where_soft_delete: + pRelationLinked->hierarchyWhereSoftDelete(params); + break; + default: + qAssert(false); + break; + } + + params.setIndexOwner(lOldIndexOwner); + params.setCustomAliasOwner(sOldCustomAliasOwner); + } + } + + static std::shared_ptr getRelationLinkedSaved(const QPair &key) + { + QMutexLocker locker(&m_mutex); + return m_relationLinkedSaved.value(key); + } + static void insertRelationLinkedSaved(const QPair &key, std::shared_ptr ptr) + { + QMutexLocker locker(&m_mutex); + m_relationLinkedSaved.insert(key, ptr); + } + }; + + QMutex QxSqlRelationLinked::QxSqlRelationLinkedImpl::m_mutex; + QHash, std::shared_ptr> QxSqlRelationLinked::QxSqlRelationLinkedImpl::m_relationLinkedSaved; + + QxSqlRelationLinked::QxSqlRelationLinked() : m_pImpl(new QxSqlRelationLinkedImpl(true)) { ; } + + QxSqlRelationLinked::QxSqlRelationLinked(bool bRoot) : m_pImpl(new QxSqlRelationLinkedImpl(bRoot)) { ; } + + QxSqlRelationLinked::~QxSqlRelationLinked() { ; } + + bool QxSqlRelationLinked::isRoot() const { return m_pImpl->m_bRoot; } + + bool QxSqlRelationLinked::checkRootColumns(const QString &s) const { return m_pImpl->checkRootColumns(s); } + + long QxSqlRelationLinked::getRootColumnsCount() const { return m_pImpl->m_lstRootColumns.count(); } + + long QxSqlRelationLinked::getRootColumnsOffset() const { return m_pImpl->m_lRootColumnsOffset; } + + void QxSqlRelationLinked::setRootColumnsOffset(long l) { m_pImpl->m_lRootColumnsOffset = l; } + + QString QxSqlRelationLinked::getRootCustomAlias() const { return m_pImpl->m_sRootCustomAlias; } + + QxSqlRelationLinked::type_lst_relation_linked QxSqlRelationLinked::getRelationLinkedX() const { return m_pImpl->m_relationLinkedX; } + + QxSqlRelationLinked::type_lst_relation QxSqlRelationLinked::getRelationX() const { return m_pImpl->m_relationX; } + + std::shared_ptr QxSqlRelationLinked::getHierarchy(IxClass *pClass, const QStringList &sRelationX, qx_bool &bOk, qx::dao::detail::IxDao_Helper *pDaoHelper /* = NULL */) + { + if (!pClass) + { + qAssert(false); + bOk = qx_bool(false, "class is empty"); + return std::shared_ptr(); + } + QByteArray hash = sRelationX.join("|").toLatin1(); + QPair key(pClass, hash); + bool bHasJoinSubQuery = (pDaoHelper ? (!pDaoHelper->qxQuery().getJoinQueryHash().isEmpty()) : false); + std::shared_ptr ptr = (bHasJoinSubQuery ? std::shared_ptr() : QxSqlRelationLinked::QxSqlRelationLinkedImpl::getRelationLinkedSaved(key)); + std::shared_ptr result = std::make_shared(); + if (ptr) + { + (*result->m_pImpl) = (*ptr->m_pImpl); + return result; + } + + ptr = std::make_shared(); + bOk = ptr->m_pImpl->buildHierarchy(pClass->getSqlRelationX().get(), sRelationX, pDaoHelper); + if (!bOk) + { + return std::shared_ptr(); + } + if (!bHasJoinSubQuery) + { + QxSqlRelationLinked::QxSqlRelationLinkedImpl::insertRelationLinkedSaved(key, ptr); + } + (*result->m_pImpl) = (*ptr->m_pImpl); + return result; + } + + qx_bool QxSqlRelationLinked::QxSqlRelationLinkedImpl::buildHierarchy(IxSqlRelationX *pRelationX, const QStringList &sRelationX, qx::dao::detail::IxDao_Helper *pDaoHelper) + { + if (!pRelationX) + { + qAssert(false); + return qx_bool(false); + } + if (m_bRoot) + { + m_relationLinkedX.clear(); + m_relationX.clear(); + } + m_allRelationX = pRelationX; + + Q_FOREACH (QString sRelation, sRelationX) + { + QString sKey; + sRelation = sRelation.trimmed(); + int iJoin = QString(QX_LEFT_OUTER_JOIN).size(); + qAssert(iJoin == QString(QX_INNER_JOIN).size()); + qx::dao::sql_join::join_type eJoinType = qx::dao::sql_join::no_join; + + if (sRelation.left(iJoin) == QX_LEFT_OUTER_JOIN) + { + eJoinType = qx::dao::sql_join::left_outer_join; + } + else if (sRelation.left(iJoin) == QX_INNER_JOIN) + { + eJoinType = qx::dao::sql_join::inner_join; + } + int iOffset = ((eJoinType == qx::dao::sql_join::no_join) ? 0 : iJoin); + int iPosLeftOuter = sRelation.indexOf(QX_LEFT_OUTER_JOIN, 1); + int iPosInner = sRelation.indexOf(QX_INNER_JOIN, 1); + int iPos = -1; + + if (iPosLeftOuter < 0) + { + iPos = iPosInner; + } + else if (iPosInner < 0) + { + iPos = iPosLeftOuter; + } + else + { + iPos = ((iPosLeftOuter < iPosInner) ? iPosLeftOuter : iPosInner); + } + + if (iPos >= 0) + { + sKey = sRelation.mid(iOffset, (iPos - iOffset)); + } + else + { + sKey = sRelation.mid(iOffset); + } + sRelation.remove(0, (sKey.size() + iOffset)); + sKey = sKey.trimmed(); + sRelation = sRelation.trimmed(); + if (sKey.isEmpty()) + { + continue; + } + QStringList sNewRelationX = (sRelation.isEmpty() ? QStringList() : (QStringList() << sRelation)); + + if (sKey.startsWith("*")) + { + QString suffix = sKey.right(sKey.size() - 1); + QxCollectionIterator itr(*m_allRelationX); + while (itr.next()) + { + qx_bool bResult = insertRelationToHierarchy(sNewRelationX, (itr.key() + suffix), eJoinType, pDaoHelper); + if (!bResult) + { + return bResult; + } + } + } + else + { + qx_bool bResult = insertRelationToHierarchy(sNewRelationX, sKey, eJoinType, pDaoHelper); + if (!bResult) + { + return bResult; + } + } + } + + bool bRelationXOrdered = (pDaoHelper ? (!pDaoHelper->qxQuery().getJoinQueryHash().isEmpty()) : false); + if (bRelationXOrdered && m_allRelationXOrdered) + { + QxCollectionIterator itr(*m_allRelationX); + while (itr.next()) + { + QString sKey = itr.key(); + if (sKey.isEmpty()) + { + qAssert(false); + continue; + } + IxSqlRelation *pRelation = itr.value(); + if (!pRelation) + { + qAssert(false); + continue; + } + if (!m_allRelationXOrdered->exist(sKey)) + { + m_allRelationXOrdered->insert(sKey, pRelation); + } + } + } + + if (m_allRelationXOrdered) + { + qAssert(m_allRelationXOrdered->count() == m_allRelationX->count()); + } + return qx_bool(true); + } + + qx_bool QxSqlRelationLinked::QxSqlRelationLinkedImpl::insertRelationToHierarchy(const QStringList &sRelationX, const QString &sKey, qx::dao::sql_join::join_type eJoinType, qx::dao::detail::IxDao_Helper *pDaoHelper) + { + bool bModeRemoveColumns = false; + QStringList columns; + QString sKeyTemp = sKey; + if (sKey.contains("{") && sKey.contains("}")) + { + int iPos1 = sKey.indexOf("{"); + int iPos2 = sKey.indexOf("}"); + if (iPos1 >= iPos2) + { + return qx_bool(false, QString("invalid relation : character '}' before than character '{' (") + sKey + ")"); + } + if (iPos1 > 0) + { + bModeRemoveColumns = (sKey.at(iPos1 - 1) == QChar('-')); + } // syntax to remove columns instead of adding columns : -{ column1, column2, etc... } + if (iPos1 > 1) + { + bModeRemoveColumns = (bModeRemoveColumns || ((sKey.at(iPos1 - 2) == QChar('-')) && (sKey.at(iPos1 - 1) == QChar(' ')))); + } // syntax to remove columns instead of adding columns : - { column1, column2, etc... } + sKeyTemp = sKey.left(iPos1); + sKeyTemp = sKeyTemp.trimmed(); + if (sKeyTemp.endsWith("-")) + { + sKeyTemp = sKeyTemp.left(sKeyTemp.length() - 1).trimmed(); + } + QString sColumns = sKey.mid((iPos1 + 1), (iPos2 - iPos1 - 1)); + columns = sColumns.split(","); + for (long l = 0; l < columns.count(); l++) + { + columns[l] = columns.at(l).trimmed(); + } + if ((columns.count() == 1) && ((columns.at(0) == "*") || (columns.at(0) == ""))) + { + columns = QStringList(); + } + } + + QString customAlias, customAliasSuffix, customAliasPrefix; + if (sKeyTemp.contains("<") && sKeyTemp.contains(">")) + { + int iPos1 = sKeyTemp.indexOf("<"); + int iPos2 = sKeyTemp.indexOf(">"); + if (iPos1 >= iPos2) + { + return qx_bool(false, QString("invalid custom alias : character '>' before than character '<' (") + sKeyTemp + ")"); + } + customAlias = sKeyTemp.mid((iPos1 + 1), (iPos2 - iPos1 - 1)).trimmed(); + if (customAlias.startsWith("...")) + { + customAliasSuffix = customAlias.right(customAlias.size() - 3); + customAlias = ""; + } + if (customAlias.endsWith("...")) + { + customAliasPrefix = customAlias.left(customAlias.size() - 3); + customAlias = ""; + } + if (customAlias.contains(" ") || customAliasSuffix.contains(" ") || customAliasPrefix.contains(" ")) + { + return qx_bool(false, QString("invalid custom alias : custom alias cannot contain a space (") + sKeyTemp + ")"); + } + if (customAlias.contains(".") || customAliasSuffix.contains(".") || customAliasPrefix.contains(".")) + { + return qx_bool(false, QString("invalid custom alias : custom alias cannot contain a dot (") + sKeyTemp + ")"); + } + sKeyTemp = sKeyTemp.left(iPos1); + sKeyTemp = sKeyTemp.trimmed(); + } + + if (m_bRoot && sKeyTemp.isEmpty() && (columns.count() > 0)) +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + { + m_lstRootColumns = QSet(columns.begin(), columns.end()); + m_bRootColumnsModeRemove = bModeRemoveColumns; + m_sRootCustomAlias = customAlias; + return qx_bool(true); + } +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + { + m_lstRootColumns = columns.toSet(); + m_bRootColumnsModeRemove = bModeRemoveColumns; + m_sRootCustomAlias = customAlias; + return qx_bool(true); + } +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + else if (m_bRoot && sKeyTemp.isEmpty() && (columns.count() == 0)) + { + m_sRootCustomAlias = customAlias; + return qx_bool(true); + } + + if (!m_allRelationX || !m_allRelationX->exist(sKeyTemp)) + { + qAssert(false); + return qx_bool(false, QString("invalid relation key : '") + sKeyTemp + "' (" + sKey + ")"); + } + IxSqlRelation *pRelation = m_allRelationX->getByKey(sKeyTemp); + if (!pRelation) + { + qAssert(false); + return qx_bool(false, QString("invalid relation pointer : 'NULL pointer'")); + } + if (bModeRemoveColumns && (columns.count() > 0)) + { + columns = removeColumns(columns, pRelation); + } + + bool isNullColumn = ((columns.count() == 1) && (columns.at(0).toUpper() == QString("NULL"))); + if (!isNullColumn) + { + QString sDataIdKey = (pRelation->getDataId() ? pRelation->getDataId()->getKey() : QString()); + Q_FOREACH (QString sColumn, columns) + { + if ((!pRelation->getDataByKey(sColumn)) && (sDataIdKey != sColumn)) + { + return qx_bool(false, QString("invalid relation column : '" + sKeyTemp + "." + sColumn + "' (" + sKey + ")")); + } + } + } + + bool bRelationXOrdered = (pDaoHelper ? (!pDaoHelper->qxQuery().getJoinQueryHash().isEmpty()) : false); + if (bRelationXOrdered && !m_allRelationXOrdered) + { + m_allRelationXOrdered = std::make_shared(); + } + if (bRelationXOrdered && !m_allRelationXOrdered->exist(sKeyTemp)) + { + m_allRelationXOrdered->insert(sKeyTemp, pRelation); + } + + if (!customAliasSuffix.isEmpty() && pRelation->getClass()) + { + customAlias = (pRelation->getClass()->getKey() + customAliasSuffix); + } + if (!customAliasPrefix.isEmpty() && pRelation->getClass()) + { + customAlias = (customAliasPrefix + pRelation->getClass()->getKey()); + } +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + if (!m_relationX.exist(sKeyTemp)) + { + m_relationX.insert(sKeyTemp, QxSqlRelationLinkedImpl::type_relation(eJoinType, pRelation, qMakePair(QSet(columns.begin(), columns.end()), static_cast(0)), customAlias, isNullColumn)); + } +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + if (!m_relationX.exist(sKeyTemp)) + { + m_relationX.insert(sKeyTemp, QxSqlRelationLinkedImpl::type_relation(eJoinType, pRelation, qMakePair(columns.toSet(), static_cast(0)), customAlias, isNullColumn)); + } +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + if (sRelationX.count() <= 0) + { + return qx_bool(true); + } + + QxSqlRelationLinked_ptr pRelationLinked = m_relationLinkedX.value(sKeyTemp); + if (!pRelationLinked) + { + pRelationLinked = QxSqlRelationLinked_ptr(new QxSqlRelationLinked(false)); + m_relationLinkedX.insert(sKeyTemp, pRelationLinked); + } + + return pRelationLinked->m_pImpl->buildHierarchy(pRelation->getLstRelation(), sRelationX, pDaoHelper); + } + + QStringList QxSqlRelationLinked::QxSqlRelationLinkedImpl::removeColumns(const QStringList &columnsToRemove, IxSqlRelation *pRelation) const + { + if (!pRelation) + { + return QStringList(); + } + IxDataMember *pCurrData = NULL; + IxSqlRelation *pCurrRelation = NULL; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QSet columnsToRemoveSet(columnsToRemove.begin(), columnsToRemove.end()); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QSet columnsToRemoveSet = columnsToRemove.toSet(); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QStringList columns; + + long lCurrIndex = 0; + while ((pCurrData = pRelation->nextData(lCurrIndex))) + { + if (!columnsToRemoveSet.contains(pCurrData->getKey())) + { + columns.append(pCurrData->getKey()); + } + } + + lCurrIndex = 0; + while ((pCurrRelation = pRelation->nextRelation(lCurrIndex))) + { + if (!columnsToRemoveSet.contains(pCurrRelation->getKey())) + { + columns.append(pCurrRelation->getKey()); + } + } + + return columns; + } + + void QxSqlRelationLinked::hierarchySelect(QxSqlRelationParams ¶ms) { m_pImpl->hierarchyAction(params, QxSqlRelationLinkedImpl::e_hierarchy_select); } + + void QxSqlRelationLinked::hierarchyFrom(QxSqlRelationParams ¶ms) { m_pImpl->hierarchyAction(params, QxSqlRelationLinkedImpl::e_hierarchy_from); } + + void QxSqlRelationLinked::hierarchyJoin(QxSqlRelationParams ¶ms) { m_pImpl->hierarchyAction(params, QxSqlRelationLinkedImpl::e_hierarchy_join); } + + void QxSqlRelationLinked::hierarchyWhereSoftDelete(QxSqlRelationParams ¶ms) { m_pImpl->hierarchyAction(params, QxSqlRelationLinkedImpl::e_hierarchy_where_soft_delete); } + + void QxSqlRelationLinked::hierarchyResolveOutput(QxSqlRelationParams ¶ms) + { + if (m_pImpl->m_bRoot) + { + params.setIndex(0); + params.setIndexOwner(0); + params.setCustomAlias(m_pImpl->m_sRootCustomAlias); + params.setCustomAliasOwner(m_pImpl->m_sRootCustomAlias); + } + IxSqlRelationX *pAllRelationX = (m_pImpl->m_allRelationXOrdered ? m_pImpl->m_allRelationXOrdered.get() : m_pImpl->m_allRelationX); + if (!pAllRelationX) + { + qAssert(false); + return; + } + QVariant vId, vIdRelation; + bool bByPass(false), bComplex(params.builder().getCartesianProduct()); + for (auto itr = pAllRelationX->begin(); itr != pAllRelationX->end(); ++itr) + { + IxSqlRelation *p = itr->second; + if (!p) + { + continue; + } + if (!isValidDaoHelper(params)) + { + return; + } + params.setIndex(params.index() + 1); + params.setRelationX(&m_pImpl->m_relationLinkedX); + params.setJoinType(qx::dao::sql_join::no_join); + bool bEager = m_pImpl->m_relationX.exist(p->getKey()); + bool bCheckRootColumn = (m_pImpl->m_bRoot && checkRootColumns(p->getKey())); + bool bIsNullColumn = false; + if (bEager) + { + QxSqlRelationLinkedImpl::type_relation &temp = const_cast(m_pImpl->m_relationX.getByKey(p->getKey())); + params.setJoinType(std::get<0>(temp)); + params.setColumns(&std::get<2>(temp)); + params.setCustomAlias(std::get<3>(temp)); + bIsNullColumn = std::get<4>(temp); + } + if (bComplex) + { + vIdRelation = ((bCheckRootColumn || (bEager && (!bIsNullColumn))) ? p->getIdFromQuery(bEager, params, -1, -1) : QVariant()); + } + bool bValidId = (bComplex && qx::trait::is_valid_primary_key(vIdRelation)); + void *pFetched = (bValidId ? params.builder().existIdX(params.index(), params.id(), vIdRelation) : NULL); + bByPass = (bValidId && (pFetched != NULL)); + if (bByPass) + { + p->updateOffset(bEager, params); + } + else + { + if (bEager) + { + if (!bIsNullColumn) + { + pFetched = p->eagerFetch_ResolveOutput(params); + } + } + else if (bCheckRootColumn) + { + p->lazyFetch_ResolveOutput(params); + } + if (bValidId && pFetched) + { + params.builder().insertIdX(params.index(), params.id(), vIdRelation, pFetched); + } + if (!isValidDaoHelper(params)) + { + return; + } + } + if (bEager) + { + QxSqlRelationLinkedImpl::type_relation &temp = const_cast(m_pImpl->m_relationX.getByKey(p->getKey())); + QxSqlRelationLinked_ptr pRelationLinked = m_pImpl->m_relationLinkedX.value(p->getKey()); + if (!pRelationLinked) + { + continue; + } + if (!pFetched) + { + params.setRelationX(NULL); + params.setIndex(params.index() + pRelationLinked->getAllRelationCount()); + pRelationLinked->updateOffset(params); + continue; + } + void *pOldOwner = params.owner(); + params.setOwner(pFetched); + long lOldIndexOwner = params.indexOwner(); + params.setIndexOwner(params.index()); + QString sOldCustomAliasOwner = params.getCustomAliasOwner(); + params.setCustomAliasOwner(std::get<3>(temp)); + QVariant vOldId = params.id(); + params.setId(QVariant(vOldId.toString() + "|" + vIdRelation.toString())); + pRelationLinked->hierarchyResolveOutput(params); + params.setIndexOwner(lOldIndexOwner); + params.setCustomAliasOwner(sOldCustomAliasOwner); + params.setOwner(pOldOwner); + params.setId(vOldId); + } + } + } + + QSqlError QxSqlRelationLinked::hierarchyOnBeforeSave(QxSqlRelationParams ¶ms) + { + for (auto itr = m_pImpl->m_relationX.begin(); itr != m_pImpl->m_relationX.end(); ++itr) + { + const QxSqlRelationLinkedImpl::type_relation &item = itr->second; + if (!std::get<1>(item)) + { + continue; + } + QSqlError err = std::get<1>(item)->onBeforeSave(params); + if (err.isValid()) + { + return err; + } + } + return QSqlError(); + } + + QSqlError QxSqlRelationLinked::hierarchyOnAfterSave(QxSqlRelationParams ¶ms) + { + for (auto itr = m_pImpl->m_relationX.begin(); itr != m_pImpl->m_relationX.end(); ++itr) + { + const QxSqlRelationLinkedImpl::type_relation &item = itr->second; + if (!std::get<1>(item)) + { + continue; + } + QSqlError err = std::get<1>(item)->onAfterSave(params); + if (err.isValid()) + { + return err; + } + } + return QSqlError(); + } + + bool QxSqlRelationLinked::getCartesianProduct() const + { + for (auto itr = m_pImpl->m_relationX.begin(); itr != m_pImpl->m_relationX.end(); ++itr) + { + const QxSqlRelationLinkedImpl::type_relation &item = itr->second; + if (!std::get<1>(item)) + { + continue; + } + if (std::get<1>(item)->getCartesianProduct()) + { + return true; + } + } + + Q_FOREACH (QxSqlRelationLinked_ptr pRelationLinked, m_pImpl->m_relationLinkedX) + { + if (pRelationLinked && pRelationLinked->getCartesianProduct()) + { + return true; + } + } + return false; + } + + long QxSqlRelationLinked::getAllRelationCount() const + { + long lCount = 0; + if (!m_pImpl->m_allRelationX) + { + return 0; + } + Q_FOREACH (QxSqlRelationLinked_ptr pRelationLinked, m_pImpl->m_relationLinkedX) + { + lCount += pRelationLinked->getAllRelationCount(); + } + return (lCount + m_pImpl->m_allRelationX->count()); + } + + long QxSqlRelationLinked::getRelationCount() const + { + long lCount = 0; + Q_FOREACH (QxSqlRelationLinked_ptr pRelationLinked, m_pImpl->m_relationLinkedX) + { + lCount += pRelationLinked->getRelationCount(); + } + return (lCount + m_pImpl->m_relationX.count()); + } + + void QxSqlRelationLinked::updateOffset(QxSqlRelationParams ¶ms) + { + IxSqlRelationX *pAllRelationX = (m_pImpl->m_allRelationXOrdered ? m_pImpl->m_allRelationXOrdered.get() : m_pImpl->m_allRelationX); + if (!pAllRelationX) + { + qAssert(false); + return; + } + for (auto itr = pAllRelationX->begin(); itr != pAllRelationX->end(); ++itr) + { + IxSqlRelation *p = itr->second; + if (!p) + { + continue; + } + params.setRelationX(&m_pImpl->m_relationLinkedX); + bool bEager = m_pImpl->m_relationX.exist(p->getKey()); + bool bIsNullColumn = false; + if (bEager) + { + QxSqlRelationLinkedImpl::type_relation &temp = const_cast(m_pImpl->m_relationX.getByKey(p->getKey())); + params.setColumns(&std::get<2>(temp)); + params.setCustomAlias(std::get<3>(temp)); + bIsNullColumn = std::get<4>(temp); + } + else + { + params.setColumns(NULL); + params.setCustomAlias(""); + } + if (bEager) + { + if (!bIsNullColumn) + { + p->updateOffset(true, params); + } + } + else if (m_pImpl->m_bRoot) + { + p->updateOffset(false, params); + } + QxSqlRelationLinked_ptr pRelationLinked = m_pImpl->m_relationLinkedX.value(p->getKey()); + if (!pRelationLinked) + { + continue; + } + pRelationLinked->updateOffset(params); + } + } + + bool QxSqlRelationLinked::existRelation(const QString &sKey) const + { + return m_pImpl->m_relationX.exist(sKey); + } + + bool QxSqlRelationLinked::isValidDaoHelper(QxSqlRelationParams ¶ms) const + { + if (!params.builder().getDaoHelper()) + { + return true; + } + return params.builder().getDaoHelper()->isValid(); + } + +} // namespace qx diff --git a/src/QxDao/QxSqlRelationParams.cpp b/src/QxDao/QxSqlRelationParams.cpp new file mode 100644 index 0000000..9ef5f34 --- /dev/null +++ b/src/QxDao/QxSqlRelationParams.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include + +namespace qx +{ + + QxSqlRelationParams::QxSqlRelationParams() : m_lIndex(0), m_lIndexOwner(0), m_lOffset(0), m_sql(NULL), m_builder(NULL), m_query(NULL), m_database(NULL), m_pOwner(NULL), m_eJoinType(qx::dao::sql_join::no_join), m_pRelationX(NULL), m_eSaveMode(qx::dao::save_mode::e_check_insert_or_update), m_bRecursiveMode(false), m_pColumns(NULL), m_pLstExecBatch(NULL), m_bIsDistinct(false) { ; } + + QxSqlRelationParams::QxSqlRelationParams(long lIndex, long lOffset, QString *sql, IxSqlQueryBuilder *builder, QSqlQuery *query, void *pOwner, const QVariant &vId /* = QVariant() */, qx::QxCollection *pLstExecBatch /* = NULL */) : m_vId(vId), m_lIndex(lIndex), m_lIndexOwner(0), m_lOffset(lOffset), m_sql(sql), m_builder(builder), m_query(query), m_database(NULL), m_pOwner(pOwner), m_eJoinType(qx::dao::sql_join::no_join), m_pRelationX(NULL), m_eSaveMode(qx::dao::save_mode::e_check_insert_or_update), m_bRecursiveMode(false), m_pColumns(NULL), m_pLstExecBatch(pLstExecBatch), m_bIsDistinct(false) + { + qx::dao::detail::IxDao_Helper *pDaoHelper = (builder ? builder->getDaoHelper() : NULL); + m_bIsDistinct = (pDaoHelper ? pDaoHelper->isDistinct() : false); + } + + QxSqlRelationParams::~QxSqlRelationParams() { ; } + + void QxSqlRelationParams::setBuilder(IxSqlQueryBuilder *builder) + { + m_builder = builder; + m_bIsDistinct = ((builder && builder->getDaoHelper()) ? builder->getDaoHelper()->isDistinct() : false); + } + +} // namespace qx diff --git a/src/QxDao/QxTimeNeutral.cpp b/src/QxDao/QxTimeNeutral.cpp new file mode 100644 index 0000000..ad6477c --- /dev/null +++ b/src/QxDao/QxTimeNeutral.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +QDataStream &operator<<(QDataStream &stream, const qx::QxTimeNeutral &t) +{ + stream << t.m_time; + stream << t.m_neutral; + return stream; +} + +QDataStream &operator>>(QDataStream &stream, qx::QxTimeNeutral &t) +{ + stream >> t.m_time; + stream >> t.m_neutral; + return stream; +} diff --git a/src/QxDataMember/IxDataMember.cpp b/src/QxDataMember/IxDataMember.cpp new file mode 100644 index 0000000..294c2fa --- /dev/null +++ b/src/QxDataMember/IxDataMember.cpp @@ -0,0 +1,963 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include + +#define QX_CONSTRUCT_IX_DATA_MEMBER() \ + m_iPrecision(6), m_iMinLength(-1), m_iMaxLength(-1), m_bRequired(false), \ + m_bReadOnly(false), m_bAutoIncrement(false), m_bNotNull(false), \ + m_bIsPrimaryKey(false), m_bAccessDataPointer(false), m_bIndex(false), \ + m_bUnique(false), m_pName(NULL), m_pParent(NULL), m_pImpl(NULL) + +namespace qx +{ + + struct Q_DECL_HIDDEN IxDataMember::IxDataMemberImpl + { + + typedef QMap> type_fk_part_of_pk; + typedef std::unique_ptr type_fk_part_of_pk_ptr; //!< Type used by primary key to manage the case where a foreign key (relationship) is also a part of primary key (which can contain several columns) + typedef QMap> type_part_of_pk; + typedef std::unique_ptr type_part_of_pk_ptr; //!< Type used by relationship to manage the case where a foreign key (relationship) is also a part of primary key (which can contain several columns) + + QString m_sKey; //!< Data key + QString m_sName; //!< Data name <=> database record name (if empty => data key) + QString m_sNameParent; //!< Data parent name <=> database table name + QString m_sDescription; //!< Data description + QString m_sFormat; //!< Data format ('%04d' for example) + QString m_sSqlType; //!< Data sql type + QString m_sSqlAlias; //!< Data sql alias + long m_lVersion; //!< Data version creation + bool m_bSerialize; //!< Data must be serialized + bool m_bDao; //!< Data is associated with a data source + QVariant m_vDefaultValue; //!< Data default value under QVariant format + QVariant m_vMinValue; //!< Data minimum value under QVariant format + QVariant m_vMaxValue; //!< Data maximum value under QVariant format + int m_iPrecision; //!< Data precision for numerics values (double, float, etc...) + int m_iMinLength; //!< Data minimum length (-1 <=> no min length) + int m_iMaxLength; //!< Data maximum length (-1 <=> no max length) + bool m_bRequired; //!< Data is required or optional + bool m_bReadOnly; //!< Data is read-only + bool m_bAutoIncrement; //!< Data value is auto-generated (auto-increment) + bool m_bNotNull; //!< Data can be null or not + bool m_bIsPrimaryKey; //!< Data is a primary key + bool m_bAccessDataPointer; //!< Can access to the data-member pointer + bool m_bIndex; //!< Data is an index to optimize SQL queries + bool m_bUnique; //!< Data is unique : 2 rows cannot have the same value for this column + + QByteArray m_byteName; //!< Optimization to retrieve name under "const char *" format + const char *m_pName; //!< Optimization to retrieve name under "const char *" format + QStringList m_lstNames; //!< Particular case of "boost::tuple<>" data member (multi-column primary key, composite key) + + std::unique_ptr m_pSqlRelation; //!< Sql relation to build/resolve sql query + IxDataMemberX *m_pParent; //!< 'IxDataMemberX' parent + IxDataMember *m_pImpl; //!< If not NULL then this data member is owned by a private implementation idiom instance + + type_fk_part_of_pk_ptr m_pListRelationPartOfPrimaryKey; //!< Used by primary key to manage the case where a foreign key (relationship) is also a part of primary key (which can contain several columns) + type_part_of_pk_ptr m_pListPartOfPrimaryKey; //!< Used by relationship to manage the case where a foreign key (relationship) is also a part of primary key (which can contain several columns) + + IxDataMember::type_fct_sql_callback m_fctCustomGetSqlName; //!< Custom callback function called building SQL queries (GetSqlName method) + IxDataMember::type_fct_sql_callback m_fctCustomGetSqlTablePointNameAsAlias; //!< Custom callback function called building SQL queries (GetSqlTablePointNameAsAlias method) + IxDataMember::type_fct_sql_callback m_fctCustomGetSqlNameEqualToPlaceHolder; //!< Custom callback function called building SQL queries (GetSqlNameEqualToPlaceHolder method) + IxDataMember::type_fct_sql_callback m_fctCustomGetSqlAliasEqualToPlaceHolder; //!< Custom callback function called building SQL queries (GetSqlAliasEqualToPlaceHolder method) + IxDataMember::type_fct_sql_callback m_fctCustomGetSqlAlias; //!< Custom callback function called building SQL queries (GetSqlAlias method) + + IxDataMemberImpl(const QString &sKey, long lVersion, bool bSerialize, bool bDao, IxDataMember *pImpl) : m_sKey(sKey), m_lVersion(lVersion), m_bSerialize(bSerialize), m_bDao(bDao), QX_CONSTRUCT_IX_DATA_MEMBER() + { + qAssert(!m_sKey.isEmpty()); + m_pImpl = pImpl; + updateNamePtr(); + } + ~IxDataMemberImpl() { ; } + + void updateNamePtr() + { + QString sNamePtr = (m_sName.isEmpty() ? m_sKey : m_sName); + sNamePtr.replace("|", "-"); // valid xml tag + m_byteName = sNamePtr.toLatin1(); + m_pName = m_byteName.constData(); + m_lstNames = (m_sName.isEmpty() ? m_sKey.split("|") : m_sName.split("|")); + } + }; + + QMutex IxDataMember::m_mutex; + + IxDataMember::IxDataMember(const QString &sKey, long lVersion, bool bSerialize, bool bDao, IxDataMember *pImpl) : qx::QxPropertyBag(), m_pImpl(new IxDataMemberImpl(sKey, lVersion, bSerialize, bDao, pImpl)) { ; } + + IxDataMember::~IxDataMember() { ; } + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + template + void IxDataMember::serialize(Archive &ar, const unsigned int version) + { + Q_UNUSED(version); + ar &boost::serialization::make_nvp("key", m_pImpl->m_sKey); + ar &boost::serialization::make_nvp("name", m_pImpl->m_sName); + ar &boost::serialization::make_nvp("nameParent", m_pImpl->m_sNameParent); + ar &boost::serialization::make_nvp("desc", m_pImpl->m_sDescription); + ar &boost::serialization::make_nvp("format", m_pImpl->m_sFormat); + ar &boost::serialization::make_nvp("sqlType", m_pImpl->m_sSqlType); + ar &boost::serialization::make_nvp("sqlAlias", m_pImpl->m_sSqlAlias); + ar &boost::serialization::make_nvp("version", m_pImpl->m_lVersion); + ar &boost::serialization::make_nvp("serialize", m_pImpl->m_bSerialize); + ar &boost::serialization::make_nvp("dao", m_pImpl->m_bDao); + ar &boost::serialization::make_nvp("defaultValue", m_pImpl->m_vDefaultValue); + ar &boost::serialization::make_nvp("minValue", m_pImpl->m_vMinValue); + ar &boost::serialization::make_nvp("maxValue", m_pImpl->m_vMaxValue); + ar &boost::serialization::make_nvp("precision", m_pImpl->m_iPrecision); + ar &boost::serialization::make_nvp("minLength", m_pImpl->m_iMinLength); + ar &boost::serialization::make_nvp("maxLength", m_pImpl->m_iMaxLength); + ar &boost::serialization::make_nvp("required", m_pImpl->m_bRequired); + ar &boost::serialization::make_nvp("readOnly", m_pImpl->m_bReadOnly); + ar &boost::serialization::make_nvp("autoIncrement", m_pImpl->m_bAutoIncrement); + ar &boost::serialization::make_nvp("notNull", m_pImpl->m_bNotNull); + ar &boost::serialization::make_nvp("isPrimaryKey", m_pImpl->m_bIsPrimaryKey); + ar &boost::serialization::make_nvp("isIndex", m_pImpl->m_bIndex); + ar &boost::serialization::make_nvp("isUnique", m_pImpl->m_bUnique); + } +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + QString IxDataMember::getKey() const { return m_pImpl->m_sKey; } + + QString IxDataMember::getName() const { return (m_pImpl->m_sName.isEmpty() ? m_pImpl->m_sKey : m_pImpl->m_sName); } + + int IxDataMember::getNameCount() const { return m_pImpl->m_lstNames.count(); } + + QString IxDataMember::getNameParent() const { return m_pImpl->m_sNameParent; } + + const char *IxDataMember::getNamePtr() const { return m_pImpl->m_pName; } + + QString IxDataMember::getDescription() const { return m_pImpl->m_sDescription; } + + QString IxDataMember::getFormat() const { return m_pImpl->m_sFormat; } + + long IxDataMember::getVersion() const { return m_pImpl->m_lVersion; } + + bool IxDataMember::getSerialize() const { return m_pImpl->m_bSerialize; } + + bool IxDataMember::getDao() const { return m_pImpl->m_bDao; } + + QVariant IxDataMember::getDefaultValue() const { return m_pImpl->m_vDefaultValue; } + + QVariant IxDataMember::getMinValue() const { return m_pImpl->m_vMinValue; } + + QVariant IxDataMember::getMaxValue() const { return m_pImpl->m_vMaxValue; } + + int IxDataMember::getPrecision() const { return m_pImpl->m_iPrecision; } + + int IxDataMember::getMinLength() const { return m_pImpl->m_iMinLength; } + + int IxDataMember::getMaxLength() const { return m_pImpl->m_iMaxLength; } + + bool IxDataMember::getRequired() const { return m_pImpl->m_bRequired; } + + bool IxDataMember::getReadOnly() const { return m_pImpl->m_bReadOnly; } + + bool IxDataMember::getAutoIncrement() const { return m_pImpl->m_bAutoIncrement; } + + bool IxDataMember::getNotNull() const { return m_pImpl->m_bNotNull; } + + bool IxDataMember::getIsPrimaryKey() const { return m_pImpl->m_bIsPrimaryKey; } + + bool IxDataMember::getIsIndex() const { return m_pImpl->m_bIndex; } + + bool IxDataMember::getIsUnique() const { return m_pImpl->m_bUnique; } + + IxDataMemberX *IxDataMember::getParent() const { return m_pImpl->m_pParent; } + + IxSqlRelation *IxDataMember::getSqlRelation() const { return m_pImpl->m_pSqlRelation.get(); } + + bool IxDataMember::hasSqlRelation() const { return (m_pImpl->m_pSqlRelation.get() != NULL); } + + bool IxDataMember::getAccessDataPointer() const { return m_pImpl->m_bAccessDataPointer; } + + QString IxDataMember::getType() const { return ""; } + + QString IxDataMember::getTypeParent() const + { + IxClass *pClass = (m_pImpl->m_pParent ? m_pImpl->m_pParent->getClass() : NULL); + return (pClass ? pClass->getKey() : QString()); + } + + IxDataMember *IxDataMember::getPImpl() const { return m_pImpl->m_pImpl; } + + void IxDataMember::setName(const QString &sName) + { + m_pImpl->m_sName = sName; + m_pImpl->updateNamePtr(); + } + + void IxDataMember::setNameParent(const QString &sName) { m_pImpl->m_sNameParent = sName; } + + void IxDataMember::setDescription(const QString &sDesc) { m_pImpl->m_sDescription = sDesc; } + + void IxDataMember::setFormat(const QString &sFormat) { m_pImpl->m_sFormat = sFormat; } + + void IxDataMember::setSqlType(const QString &sSqlType) { m_pImpl->m_sSqlType = sSqlType; } + + void IxDataMember::setSqlAlias(const QString &sSqlAlias) { m_pImpl->m_sSqlAlias = sSqlAlias; } + + void IxDataMember::setVersion(long lVersion) { m_pImpl->m_lVersion = lVersion; } + + void IxDataMember::setSerialize(bool bSerialize) { m_pImpl->m_bSerialize = bSerialize; } + + void IxDataMember::setDao(bool bDao) { m_pImpl->m_bDao = bDao; } + + void IxDataMember::setDefaultValue(const QVariant &var) { m_pImpl->m_vDefaultValue = var; } + + void IxDataMember::setPrecision(int iPrecision) { m_pImpl->m_iPrecision = iPrecision; } + + void IxDataMember::setRequired(bool bRequired) { m_pImpl->m_bRequired = bRequired; } + + void IxDataMember::setReadOnly(bool bReadOnly) { m_pImpl->m_bReadOnly = bReadOnly; } + + void IxDataMember::setAutoIncrement(bool bAutoIncrement) { m_pImpl->m_bAutoIncrement = bAutoIncrement; } + + void IxDataMember::setIsPrimaryKey(bool bIsPrimaryKey) { m_pImpl->m_bIsPrimaryKey = bIsPrimaryKey; } + + void IxDataMember::setIsIndex(bool bIsIndex) { m_pImpl->m_bIndex = bIsIndex; } + + void IxDataMember::setIsUnique(bool bIsUnique) { m_pImpl->m_bUnique = bIsUnique; } + + void IxDataMember::setParent(IxDataMemberX *pParent) { m_pImpl->m_pParent = pParent; } + + void IxDataMember::setSqlRelation(IxSqlRelation *pSqlRelation) { m_pImpl->m_pSqlRelation.reset(pSqlRelation); } + + void IxDataMember::setAccessDataPointer(bool b) { m_pImpl->m_bAccessDataPointer = b; } + + QVariant IxDataMember::toVariant(const void *pOwner, int iIndexName /* = -1 */, qx::cvt::context::ctx_type ctx /* = qx::cvt::context::e_no_context */) const { return this->toVariant(pOwner, m_pImpl->m_sFormat, iIndexName, ctx); } + + qx_bool IxDataMember::fromVariant(void *pOwner, const QVariant &v, int iIndexName /* = -1 */, qx::cvt::context::ctx_type ctx /* = qx::cvt::context::e_no_context */) { return this->fromVariant(pOwner, v, m_pImpl->m_sFormat, iIndexName, ctx); } + +#ifndef _QX_NO_JSON + + QJsonValue IxDataMember::toJson(const void *pOwner) const { return this->toJson(pOwner, m_pImpl->m_sFormat); } + + qx_bool IxDataMember::fromJson(void *pOwner, const QJsonValue &j) { return this->fromJson(pOwner, j, m_pImpl->m_sFormat); } + +#endif // _QX_NO_JSON + + void IxDataMember::setMinValue(long lMinValue, const QString &sMessage /* = QString() */) + { + m_pImpl->m_vMinValue = (qlonglong)lMinValue; + IxClass *pClass = (m_pImpl->m_pParent ? m_pImpl->m_pParent->getClass() : NULL); + if (!pClass) + { + return; + } + IxValidatorX *pAllValidator = pClass->getAllValidator(); + if (!pAllValidator) + { + return; + } + pAllValidator->add_MinValue(m_pImpl->m_sKey, lMinValue, sMessage); + } + + void IxDataMember::setMinValue(double dMinValue, const QString &sMessage /* = QString() */) + { + m_pImpl->m_vMinValue = dMinValue; + IxClass *pClass = (m_pImpl->m_pParent ? m_pImpl->m_pParent->getClass() : NULL); + if (!pClass) + { + return; + } + IxValidatorX *pAllValidator = pClass->getAllValidator(); + if (!pAllValidator) + { + return; + } + pAllValidator->add_MinDecimal(m_pImpl->m_sKey, dMinValue, sMessage); + } + + void IxDataMember::setMaxValue(long lMaxValue, const QString &sMessage /* = QString() */) + { + m_pImpl->m_vMaxValue = (qlonglong)lMaxValue; + IxClass *pClass = (m_pImpl->m_pParent ? m_pImpl->m_pParent->getClass() : NULL); + if (!pClass) + { + return; + } + IxValidatorX *pAllValidator = pClass->getAllValidator(); + if (!pAllValidator) + { + return; + } + pAllValidator->add_MaxValue(m_pImpl->m_sKey, lMaxValue, sMessage); + } + + void IxDataMember::setMaxValue(double dMaxValue, const QString &sMessage /* = QString() */) + { + m_pImpl->m_vMaxValue = dMaxValue; + IxClass *pClass = (m_pImpl->m_pParent ? m_pImpl->m_pParent->getClass() : NULL); + if (!pClass) + { + return; + } + IxValidatorX *pAllValidator = pClass->getAllValidator(); + if (!pAllValidator) + { + return; + } + pAllValidator->add_MaxDecimal(m_pImpl->m_sKey, dMaxValue, sMessage); + } + + void IxDataMember::setMinLength(int iMinLength, const QString &sMessage /* = QString() */) + { + m_pImpl->m_iMinLength = iMinLength; + IxClass *pClass = (m_pImpl->m_pParent ? m_pImpl->m_pParent->getClass() : NULL); + if (!pClass) + { + return; + } + IxValidatorX *pAllValidator = pClass->getAllValidator(); + if (!pAllValidator) + { + return; + } + pAllValidator->add_MinLength(m_pImpl->m_sKey, (long)m_pImpl->m_iMinLength, sMessage); + } + + void IxDataMember::setMaxLength(int iMaxLength, const QString &sMessage /* = QString() */) + { + m_pImpl->m_iMaxLength = iMaxLength; + IxClass *pClass = (m_pImpl->m_pParent ? m_pImpl->m_pParent->getClass() : NULL); + if (!pClass) + { + return; + } + IxValidatorX *pAllValidator = pClass->getAllValidator(); + if (!pAllValidator) + { + return; + } + pAllValidator->add_MaxLength(m_pImpl->m_sKey, (long)m_pImpl->m_iMaxLength, sMessage); + } + + void IxDataMember::setNotNull(bool bNotNull, const QString &sMessage /* = QString() */) + { + m_pImpl->m_bNotNull = bNotNull; + IxClass *pClass = (m_pImpl->m_pParent ? m_pImpl->m_pParent->getClass() : NULL); + if (!pClass) + { + return; + } + IxValidatorX *pAllValidator = pClass->getAllValidator(); + if (!pAllValidator) + { + return; + } + if (m_pImpl->m_bNotNull) + { + pAllValidator->add_NotNull(m_pImpl->m_sKey, sMessage); + } + } + + bool IxDataMember::isThereRelationPartOfPrimaryKey(int iIndexNamePK, IxSqlRelation *&pRelation, int &iIndexNameFK) const + { + pRelation = NULL; + iIndexNameFK = -1; + if (!m_pImpl->m_pListRelationPartOfPrimaryKey) + { + return false; + } + if (!m_pImpl->m_pListRelationPartOfPrimaryKey->contains(iIndexNamePK)) + { + return false; + } + QPair pair = m_pImpl->m_pListRelationPartOfPrimaryKey->value(iIndexNamePK); + pRelation = pair.first; + iIndexNameFK = pair.second; + return true; + } + + bool IxDataMember::isPartOfPrimaryKey(int iIndexNameFK, IxDataMember *&pPrimaryKey, int &iIndexNamePK) const + { + pPrimaryKey = NULL; + iIndexNamePK = -1; + if (!m_pImpl->m_pListPartOfPrimaryKey) + { + return false; + } + if (!m_pImpl->m_pListPartOfPrimaryKey->contains(iIndexNameFK)) + { + return false; + } + QPair pair = m_pImpl->m_pListPartOfPrimaryKey->value(iIndexNameFK); + pPrimaryKey = pair.first; + iIndexNamePK = pair.second; + return true; + } + + void IxDataMember::setRelationPartOfPrimaryKey(int iIndexNamePK, IxSqlRelation *pRelation, int iIndexNameFK) + { + if (!m_pImpl->m_pListRelationPartOfPrimaryKey) + { + m_pImpl->m_pListRelationPartOfPrimaryKey.reset(new IxDataMemberImpl::type_fk_part_of_pk()); + } + if (m_pImpl->m_pListRelationPartOfPrimaryKey->contains(iIndexNamePK)) + { + return; + } + QPair pair; + pair.first = pRelation; + pair.second = iIndexNameFK; + m_pImpl->m_pListRelationPartOfPrimaryKey->insert(iIndexNamePK, pair); + } + + void IxDataMember::setPartOfPrimaryKey(int iIndexNameFK, IxDataMember *pPrimaryKey, int iIndexNamePK) + { + if (!m_pImpl->m_pListPartOfPrimaryKey) + { + m_pImpl->m_pListPartOfPrimaryKey.reset(new IxDataMemberImpl::type_part_of_pk()); + } + if (m_pImpl->m_pListPartOfPrimaryKey->contains(iIndexNameFK)) + { + return; + } + QPair pair; + pair.first = pPrimaryKey; + pair.second = iIndexNamePK; + m_pImpl->m_pListPartOfPrimaryKey->insert(iIndexNameFK, pair); + } + + QString IxDataMember::getName(int iIndex, const QString &sOtherName /* = QString() */) const + { + if (!sOtherName.isEmpty()) + { + QStringList lstOtherNames = sOtherName.split("|"); + qAssert(m_pImpl->m_lstNames.count() == lstOtherNames.count()); + return (((iIndex >= 0) && (iIndex < lstOtherNames.count())) ? lstOtherNames.at(iIndex) : QString()); + } + + return (((iIndex >= 0) && (iIndex < m_pImpl->m_lstNames.count())) ? m_pImpl->m_lstNames.at(iIndex) : QString()); + } + + QString IxDataMember::getSqlAlias(const QString &sTable /* = QString() */, bool bClauseWhere /* = false */, int iIndexName /* = 0 */, qx::IxSqlQueryBuilder *pSqlQueryBuilder /* = NULL */) const + { + QString sSqlAlias; + QString sTableAlias = sTable; + sTableAlias.replace(".", "_"); + + do + { + // Standard SQL disallows references to column aliases in a WHERE clause + // cf. + if (bClauseWhere && !sTableAlias.isEmpty()) + { + sSqlAlias = (getSqlTableName(sTableAlias) + "." + getSqlColumnName(getName(iIndexName))); + break; + } + + sSqlAlias = m_pImpl->m_sSqlAlias; + if (!sSqlAlias.isEmpty()) + { + break; + } + if (!sTableAlias.isEmpty()) + { + sSqlAlias = (sTableAlias + "_" + getName(iIndexName) + "_0"); + } + else + { + sSqlAlias = (m_pImpl->m_sNameParent + "_" + getName(iIndexName) + "_0"); + } + + // Special database keywords using '[', ']' or '"' characters + sSqlAlias.replace("[", ""); + sSqlAlias.replace("]", ""); + sSqlAlias.replace("\"", ""); + sSqlAlias.replace(".", "_"); + } while (0); + + if (m_pImpl->m_fctCustomGetSqlAlias) + { + IxDataMemberSqlCallbackParams callbackParams(this, sSqlAlias); + callbackParams.sTable = sTable; + callbackParams.bClauseWhere = bClauseWhere; + callbackParams.iIndexName = iIndexName; + callbackParams.pDaoHelper = (pSqlQueryBuilder ? pSqlQueryBuilder->getDaoHelper() : NULL); + callbackParams.pSqlQueryBuilder = pSqlQueryBuilder; + m_pImpl->m_fctCustomGetSqlAlias(callbackParams); + } + + return sSqlAlias; + } + + QString IxDataMember::getSqlType(int iIndexName /* = -1 */) const + { + if ((iIndexName == -1) || (getNameCount() <= 1)) + { + return m_pImpl->m_sSqlType; + } + QStringList lst = m_pImpl->m_sSqlType.split("|"); + return (((iIndexName >= 0) && (iIndexName < lst.count())) ? lst.at(iIndexName) : QString()); + } + + QString IxDataMember::getSqlTypeAndParams(int iIndexName /* = -1 */) const + { + QString sResult = getSqlType(iIndexName); + sResult += (m_pImpl->m_bNotNull ? " NOT NULL" : ""); + sResult += ((m_pImpl->m_bIsPrimaryKey && (getNameCount() <= 1)) ? " PRIMARY KEY" : ""); + + if (m_pImpl->m_bAutoIncrement) + { + qx::dao::detail::IxSqlGenerator *pSqlGenerator = QxSqlDatabase::getSingleton()->getSqlGenerator(); + if (!pSqlGenerator) + { + qAssert(false); + sResult += " AUTOINCREMENT"; + } + else + { + sResult += " " + pSqlGenerator->getAutoIncrement(); + } + } + + return sResult; + } + + QString IxDataMember::getSqlPlaceHolder(const QString &sAppend /* = QString() */, int iIndexName /* = 0 */, const QString &sSep /* = QString(", ") */, const QString &sOtherName /* = QString() */, bool bCheckFKPartOfPK /* = false */) const + { + QString sResult; + int iIndexNameFK = 0; + IxSqlRelation *pRelation = NULL; + + if (iIndexName == -1) + { + for (int i = 0; i < m_pImpl->m_lstNames.count(); i++) + { + if (bCheckFKPartOfPK && m_pImpl->m_bIsPrimaryKey && isThereRelationPartOfPrimaryKey(i, pRelation, iIndexNameFK)) + { + continue; + } + sResult += getSqlPlaceHolder(sAppend, i, sSep, sOtherName) + sSep; + } + + if (!sResult.isEmpty()) + { + sResult = sResult.left(sResult.length() - sSep.length()); + } // Remove last separator + return sResult; + } + + // Special database keywords using '[', ']' or '"' characters + if (bCheckFKPartOfPK && m_pImpl->m_bIsPrimaryKey && isThereRelationPartOfPrimaryKey(iIndexName, pRelation, iIndexNameFK)) + { + return ""; + } + QString sSqlPlaceHolder = getName(iIndexName, sOtherName) + sAppend; + sSqlPlaceHolder.replace("[", ""); + sSqlPlaceHolder.replace("]", ""); + sSqlPlaceHolder.replace("\"", ""); + + switch (QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle()) + { + case QxSqlDatabase::ph_style_question_mark: + sResult = "?"; + break; + case QxSqlDatabase::ph_style_2_point_name: + sResult = ":" + sSqlPlaceHolder; + break; + case QxSqlDatabase::ph_style_at_name: + sResult = "@" + sSqlPlaceHolder; + break; + default: + sResult = ":" + sSqlPlaceHolder; + break; + } + + return sResult; + } + + void IxDataMember::setSqlPlaceHolder(QSqlQuery &query, void *pOwner, const QString &sAppend /* = QString() */, const QString &sOtherName /* = QString() */, bool bCheckFKPartOfPK /* = false */, qx::QxCollection *pLstExecBatch /* = NULL */) const + { + int iIndexNameFK = 0; + IxSqlRelation *pRelation = NULL; + bool bQuestionMark = (qx::QxSqlDatabase::getSingleton()->getSqlPlaceHolderStyle() == qx::QxSqlDatabase::ph_style_question_mark); + + for (int i = 0; i < m_pImpl->m_lstNames.count(); i++) + { + if (bCheckFKPartOfPK && m_pImpl->m_bIsPrimaryKey && isThereRelationPartOfPrimaryKey(i, pRelation, iIndexNameFK)) + { + continue; + } + QString key = (bQuestionMark ? QString() : getSqlPlaceHolder(sAppend, i, "", sOtherName)); + QVariant val = toVariant(pOwner, i, qx::cvt::context::e_database); + if (pLstExecBatch) + { + if (bQuestionMark) + { + key = getName(i, sOtherName); + } + if (!pLstExecBatch->exist(key)) + { + QVariantList empty; + pLstExecBatch->insert(key, empty); + } + QVariantList &values = const_cast(pLstExecBatch->getByKey(key)); + values.append(val); + } + else + { + if (bQuestionMark) + { + query.addBindValue(val); + } + else + { + query.bindValue(key, val); + } + } + } + } + + QString IxDataMember::getSqlAliasEqualToPlaceHolder(const QString &sTable /* = QString() */, bool bClauseWhere /* = false */, const QString &sAppend /* = QString() */, const QString &sSep /* = QString(" AND ") */, bool bCheckFKPartOfPK /* = false */, qx::IxSqlQueryBuilder *pSqlQueryBuilder /* = NULL */) const + { + QString sResult; + int iIndexNameFK = 0; + IxSqlRelation *pRelation = NULL; + + for (int i = 0; i < m_pImpl->m_lstNames.count(); i++) + { + if (bCheckFKPartOfPK && m_pImpl->m_bIsPrimaryKey && isThereRelationPartOfPrimaryKey(i, pRelation, iIndexNameFK)) + { + continue; + } + sResult += getSqlAlias(sTable, bClauseWhere, i, pSqlQueryBuilder) + " = " + getSqlPlaceHolder(sAppend, i) + sSep; + } + + if (!sResult.isEmpty()) + { + sResult = sResult.left(sResult.length() - sSep.length()); + } // Remove last separator + + if (m_pImpl->m_fctCustomGetSqlAliasEqualToPlaceHolder) + { + IxDataMemberSqlCallbackParams callbackParams(this, sResult); + callbackParams.sTable = sTable; + callbackParams.bClauseWhere = bClauseWhere; + callbackParams.sAppend = sAppend; + callbackParams.sSep = sSep; + callbackParams.bCheckFKPartOfPK = bCheckFKPartOfPK; + callbackParams.pDaoHelper = (pSqlQueryBuilder ? pSqlQueryBuilder->getDaoHelper() : NULL); + callbackParams.pSqlQueryBuilder = pSqlQueryBuilder; + m_pImpl->m_fctCustomGetSqlAliasEqualToPlaceHolder(callbackParams); + } + + return sResult; + } + + QString IxDataMember::getSqlNameEqualToPlaceHolder(const QString &sAppend /* = QString() */, const QString &sSep /* = QString(" AND ") */, bool bCheckFKPartOfPK /* = false */, qx::IxSqlQueryBuilder *pSqlQueryBuilder /* = NULL */) const + { + QString sResult; + int iIndexNameFK = 0; + IxSqlRelation *pRelation = NULL; + + for (int i = 0; i < m_pImpl->m_lstNames.count(); i++) + { + if (bCheckFKPartOfPK && m_pImpl->m_bIsPrimaryKey && isThereRelationPartOfPrimaryKey(i, pRelation, iIndexNameFK)) + { + continue; + } + sResult += getSqlColumnName(getName(i)) + " = " + getSqlPlaceHolder(sAppend, i) + sSep; + } + + if (!sResult.isEmpty()) + { + sResult = sResult.left(sResult.length() - sSep.length()); + } // Remove last separator + + if (m_pImpl->m_fctCustomGetSqlNameEqualToPlaceHolder) + { + IxDataMemberSqlCallbackParams callbackParams(this, sResult); + callbackParams.sAppend = sAppend; + callbackParams.sSep = sSep; + callbackParams.bCheckFKPartOfPK = bCheckFKPartOfPK; + callbackParams.pDaoHelper = (pSqlQueryBuilder ? pSqlQueryBuilder->getDaoHelper() : NULL); + callbackParams.pSqlQueryBuilder = pSqlQueryBuilder; + m_pImpl->m_fctCustomGetSqlNameEqualToPlaceHolder(callbackParams); + } + + return sResult; + } + + QString IxDataMember::getSqlTablePointNameAsAlias(const QString &sTable, const QString &sSep /* = QString(", ") */, const QString &sSuffixAlias /* = QString() */, bool bCheckFKPartOfPK /* = false */, const QString &sCustomAlias /* = QString() */, qx::IxSqlQueryBuilder *pSqlQueryBuilder /* = NULL */) const + { + QString sResult; + int iIndexNameFK = 0; + IxSqlRelation *pRelation = NULL; + QString sTableAlias = (sCustomAlias.isEmpty() ? sTable : sCustomAlias); + sTableAlias.replace(".", "_"); + sTableAlias = getSqlTableName(sTableAlias); + + QString sTableAliasSep = " AS "; + qx::dao::detail::IxSqlGenerator *pSqlGenerator = QxSqlDatabase::getSingleton()->getSqlGenerator(); + if (pSqlGenerator) + { + sTableAliasSep = pSqlGenerator->getTableAliasSep(); + } + + for (int i = 0; i < m_pImpl->m_lstNames.count(); i++) + { + if (bCheckFKPartOfPK && m_pImpl->m_bIsPrimaryKey && isThereRelationPartOfPrimaryKey(i, pRelation, iIndexNameFK)) + { + continue; + } + sResult += sTableAlias + "." + getSqlColumnName(getName(i)) + sTableAliasSep + getSqlColumnNameAlias(getSqlAlias((sCustomAlias.isEmpty() ? sTable : sCustomAlias), false, i, pSqlQueryBuilder) + sSuffixAlias) + sSep; + } + + if (!sResult.isEmpty()) + { + sResult = sResult.left(sResult.length() - sSep.length()); + } // Remove last separator + + if (m_pImpl->m_fctCustomGetSqlTablePointNameAsAlias) + { + IxDataMemberSqlCallbackParams callbackParams(this, sResult); + callbackParams.sTable = sTable; + callbackParams.sSep = sSep; + callbackParams.sSuffixAlias = sSuffixAlias; + callbackParams.bCheckFKPartOfPK = bCheckFKPartOfPK; + callbackParams.sCustomAlias = sCustomAlias; + callbackParams.pDaoHelper = (pSqlQueryBuilder ? pSqlQueryBuilder->getDaoHelper() : NULL); + callbackParams.pSqlQueryBuilder = pSqlQueryBuilder; + m_pImpl->m_fctCustomGetSqlTablePointNameAsAlias(callbackParams); + } + + return sResult; + } + + QString IxDataMember::getSqlName(const QString &sSep /* = QString(", ") */, const QString &sOtherName /* = QString() */, bool bCheckFKPartOfPK /* = false */, qx::IxSqlQueryBuilder *pSqlQueryBuilder /* = NULL */) const + { + QString sResult; + int iIndexNameFK = 0; + IxSqlRelation *pRelation = NULL; + + for (int i = 0; i < m_pImpl->m_lstNames.count(); i++) + { + if (bCheckFKPartOfPK && m_pImpl->m_bIsPrimaryKey && isThereRelationPartOfPrimaryKey(i, pRelation, iIndexNameFK)) + { + continue; + } + sResult += getSqlColumnName(getName(i, sOtherName)) + sSep; + } + + if (!sResult.isEmpty()) + { + sResult = sResult.left(sResult.length() - sSep.length()); + } // Remove last separator + + if (m_pImpl->m_fctCustomGetSqlName) + { + IxDataMemberSqlCallbackParams callbackParams(this, sResult); + callbackParams.sSep = sSep; + callbackParams.sOtherName = sOtherName; + callbackParams.bCheckFKPartOfPK = bCheckFKPartOfPK; + callbackParams.pDaoHelper = (pSqlQueryBuilder ? pSqlQueryBuilder->getDaoHelper() : NULL); + callbackParams.pSqlQueryBuilder = pSqlQueryBuilder; + m_pImpl->m_fctCustomGetSqlName(callbackParams); + } + + return sResult; + } + + QString IxDataMember::getSqlNameAndTypeAndParams(const QString &sSep /* = QString(", ") */, const QString &sOtherName /* = QString() */, bool bCheckFKPartOfPK /* = false */) const + { + QString sResult; + int iIndexNameFK = 0; + IxSqlRelation *pRelation = NULL; + + for (int i = 0; i < m_pImpl->m_lstNames.count(); i++) + { + if (bCheckFKPartOfPK && m_pImpl->m_bIsPrimaryKey && isThereRelationPartOfPrimaryKey(i, pRelation, iIndexNameFK)) + { + continue; + } + sResult += getSqlColumnName(getName(i, sOtherName)) + " " + getSqlTypeAndParams(i) + sSep; + } + + if (!sResult.isEmpty()) + { + sResult = sResult.left(sResult.length() - sSep.length()); + } // Remove last separator + return sResult; + } + + void IxDataMember::customGetSqlName(IxDataMember::type_fct_sql_callback fct) { m_pImpl->m_fctCustomGetSqlName = fct; } + + void IxDataMember::customGetSqlTablePointNameAsAlias(IxDataMember::type_fct_sql_callback fct) { m_pImpl->m_fctCustomGetSqlTablePointNameAsAlias = fct; } + + void IxDataMember::customGetSqlNameEqualToPlaceHolder(IxDataMember::type_fct_sql_callback fct) { m_pImpl->m_fctCustomGetSqlNameEqualToPlaceHolder = fct; } + + void IxDataMember::customGetSqlAliasEqualToPlaceHolder(IxDataMember::type_fct_sql_callback fct) { m_pImpl->m_fctCustomGetSqlAliasEqualToPlaceHolder = fct; } + + void IxDataMember::customGetSqlAlias(IxDataMember::type_fct_sql_callback fct) { m_pImpl->m_fctCustomGetSqlAlias = fct; } + + QString IxDataMember::getSqlFromTable(const QString &sTable, const QString &sCustomAlias /* = QString() */) + { + if (sCustomAlias.isEmpty() && !sTable.contains(".")) + { + return getSqlTableName(sTable); + } + QString sTableAlias = (sCustomAlias.isEmpty() ? sTable : sCustomAlias); + sTableAlias.replace(".", "_"); + + QString sTableAliasSep = " AS "; + qx::dao::detail::IxSqlGenerator *pSqlGenerator = QxSqlDatabase::getSingleton()->getSqlGenerator(); + if (pSqlGenerator) + { + sTableAliasSep = pSqlGenerator->getTableAliasSep(); + } + + return (getSqlTableName(sTable) + sTableAliasSep + sTableAlias); + } + + QString IxDataMember::getSqlTableName(const QString &sTable) + { + QStringList lstDelimiter = QxSqlDatabase::getSingleton()->getSqlDelimiterForTableName(); + bool bAddSquareBracketsForTableName = QxSqlDatabase::getSingleton()->getAddSqlSquareBracketsForTableName(); + QString sStart = (bAddSquareBracketsForTableName ? QString("[") : QString()); + QString sEnd = (bAddSquareBracketsForTableName ? QString("]") : QString()); + if (lstDelimiter.count() > 0) + { + sStart = lstDelimiter.at(0); + sEnd = lstDelimiter.at(0); + } + if (lstDelimiter.count() > 1) + { + sEnd = lstDelimiter.at(1); + } + if (sStart.isEmpty() || sEnd.isEmpty() || sTable.contains(sStart) || sTable.contains(sEnd)) + { + return sTable; + } + QString sResult = (sStart + sTable + sEnd); + sResult.replace(".", (sEnd + "." + sStart)); + return sResult; + } + + QString IxDataMember::getSqlColumnName(const QString &sColumn) + { + QStringList lstDelimiter = QxSqlDatabase::getSingleton()->getSqlDelimiterForColumnName(); + bool bAddSquareBracketsForColumnName = QxSqlDatabase::getSingleton()->getAddSqlSquareBracketsForColumnName(); + QString sStart = (bAddSquareBracketsForColumnName ? QString("[") : QString()); + QString sEnd = (bAddSquareBracketsForColumnName ? QString("]") : QString()); + if (lstDelimiter.count() > 0) + { + sStart = lstDelimiter.at(0); + sEnd = lstDelimiter.at(0); + } + if (lstDelimiter.count() > 1) + { + sEnd = lstDelimiter.at(1); + } + if (sStart.isEmpty() || sEnd.isEmpty() || sColumn.contains(sStart) || sColumn.contains(sEnd)) + { + return sColumn; + } + QString sResult = (sStart + sColumn + sEnd); + sResult.replace(".", (sEnd + "." + sStart)); + return sResult; + } + + QString IxDataMember::getSqlTableNameAlias(const QString &sTable) + { + QStringList lstDelimiter = QxSqlDatabase::getSingleton()->getSqlDelimiterForTableNameAlias(); + QString sStart = QString(); + QString sEnd = QString(); + if (lstDelimiter.count() > 0) + { + sStart = lstDelimiter.at(0); + sEnd = lstDelimiter.at(0); + } + if (lstDelimiter.count() > 1) + { + sEnd = lstDelimiter.at(1); + } + if (sStart.isEmpty() || sEnd.isEmpty() || sTable.contains(sStart) || sTable.contains(sEnd)) + { + return sTable; + } + QString sResult = (sStart + sTable + sEnd); + sResult.replace(".", (sEnd + "." + sStart)); + return sResult; + } + + QString IxDataMember::getSqlColumnNameAlias(const QString &sColumn) + { + QStringList lstDelimiter = QxSqlDatabase::getSingleton()->getSqlDelimiterForColumnNameAlias(); + QString sStart = QString(); + QString sEnd = QString(); + if (lstDelimiter.count() > 0) + { + sStart = lstDelimiter.at(0); + sEnd = lstDelimiter.at(0); + } + if (lstDelimiter.count() > 1) + { + sEnd = lstDelimiter.at(1); + } + if (sStart.isEmpty() || sEnd.isEmpty() || sColumn.contains(sStart) || sColumn.contains(sEnd)) + { + return sColumn; + } + QString sResult = (sStart + sColumn + sEnd); + sResult.replace(".", (sEnd + "." + sStart)); + return sResult; + } + + IxDataMemberSqlCallbackParams::IxDataMemberSqlCallbackParams(const IxDataMember *p, QString &sql) : pDataMember(p), sSQL(sql), bClauseWhere(false), bCheckFKPartOfPK(false), iIndexName(0), pDaoHelper(NULL), pSqlQueryBuilder(NULL) { qAssert(pDataMember); } + + IxDataMemberSqlCallbackParams::~IxDataMemberSqlCallbackParams() { ; } + +} // namespace qx + +inline bool operator<(const qx::IxDataMember &i1, const qx::IxDataMember &i2) { return (i1.getKey() < i2.getKey()); } +inline bool operator>(const qx::IxDataMember &i1, const qx::IxDataMember &i2) { return (i1.getKey() > i2.getKey()); } diff --git a/src/QxDataMember/IxDataMemberX.cpp b/src/QxDataMember/IxDataMemberX.cpp new file mode 100644 index 0000000..435b9c2 --- /dev/null +++ b/src/QxDataMember/IxDataMemberX.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +#include + +namespace qx +{ + + struct Q_DECL_HIDDEN IxDataMemberX::IxDataMemberXImpl + { + + QxCollection m_lstDataMember; //!< Collection of IxDataMember + QxCollection m_lstPImpl; //!< Collection of private implementation idiom instances + IxClass *m_pClass; //!< Class definition + IxDataMember *m_pDataMemberId; //!< Data member id with primary key type + + IxDataMemberXImpl() : m_pClass(NULL), m_pDataMemberId(NULL) { ; } + ~IxDataMemberXImpl() { ; } + + void deleteAllIxDataMember() + { + for (auto itr = m_lstDataMember.begin(); itr != m_lstDataMember.end(); ++itr) + { + delete itr->second; + } + for (auto itr = m_lstPImpl.begin(); itr != m_lstPImpl.end(); ++itr) + { + delete itr->second; + } + } + }; + + IxDataMemberX::IxDataMemberX() : m_pImpl(new IxDataMemberXImpl()) { ; } + + IxDataMemberX::~IxDataMemberX() { m_pImpl->deleteAllIxDataMember(); } + + IxClass *IxDataMemberX::getClass() const { return m_pImpl->m_pClass; } + + void IxDataMemberX::setClass(IxClass *p) { m_pImpl->m_pClass = p; } + + QString IxDataMemberX::getName() const { return (m_pImpl->m_pClass ? m_pImpl->m_pClass->getName() : ""); } + + const char *IxDataMemberX::getNamePtr() const { return (m_pImpl->m_pClass ? m_pImpl->m_pClass->getNamePtr() : NULL); } + + QString IxDataMemberX::getDescription() const { return (m_pImpl->m_pClass ? m_pImpl->m_pClass->getDescription() : ""); } + + long IxDataMemberX::getVersion() const { return (m_pImpl->m_pClass ? m_pImpl->m_pClass->getVersion() : -1); } + + qx::dao::strategy::inheritance IxDataMemberX::getDaoStrategy() const { return (m_pImpl->m_pClass ? m_pImpl->m_pClass->getDaoStrategy() : qx::dao::strategy::concrete_table_inheritance); } + + long IxDataMemberX::count() const { return m_pImpl->m_lstDataMember.count(); } + + long IxDataMemberX::size() const { return this->count(); } + + bool IxDataMemberX::exist(const QString &sKey) const { return m_pImpl->m_lstDataMember.exist(sKey); } + + IxDataMember *IxDataMemberX::get(long l) const { return m_pImpl->m_lstDataMember.getByIndex(l); } + + IxDataMember *IxDataMemberX::get(const QString &s) const { return m_pImpl->m_lstDataMember.getByKey(s); } + + IxDataMember *IxDataMemberX::getId() const { return m_pImpl->m_pDataMemberId; } + + void IxDataMemberX::setId(IxDataMember *p) { m_pImpl->m_pDataMemberId = p; } + + QxCollection &IxDataMemberX::getListDataMemberRef() { return m_pImpl->m_lstDataMember; } + + const QxCollection &IxDataMemberX::getListDataMemberRef() const { return m_pImpl->m_lstDataMember; } + + QxCollection &IxDataMemberX::getListPImplRef() { return m_pImpl->m_lstPImpl; } + + const QxCollection &IxDataMemberX::getListPImplRef() const { return m_pImpl->m_lstPImpl; } + +} // namespace qx diff --git a/src/QxDataMember/QxDataMember_QObject.cpp b/src/QxDataMember/QxDataMember_QObject.cpp new file mode 100644 index 0000000..94d6506 --- /dev/null +++ b/src/QxDataMember/QxDataMember_QObject.cpp @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +#include +#include + +#include + +#define QX_DATA_MEMBER_QOBJECT_IMPL_VIRTUAL_ARCHIVE_CPP(ArchiveInput, ArchiveOutput) \ + namespace qx \ + { \ + void QxDataMember_QObject::toArchive(const void *pOwner, ArchiveOutput &ar) const \ + { \ + QVariant v = m_metaProperty.read(static_cast(pOwner)); \ + ar << boost::serialization::make_nvp(getNamePtr(), v); \ + } \ + void QxDataMember_QObject::fromArchive(void *pOwner, ArchiveInput &ar) \ + { \ + QVariant v; \ + ar >> boost::serialization::make_nvp(getNamePtr(), v); \ + m_metaProperty.write(static_cast(pOwner), v); \ + } \ + } + +namespace qx +{ + + QxDataMember_QObject::QxDataMember_QObject(const QMetaObject *pMetaObject, const QString &sKey) : IxDataMember(sKey, 0, true, true, NULL), m_metaObject(pMetaObject) + { + setAccessDataPointer(false); + if (!m_metaObject) + { + qAssert(false); + return; + } + int index = m_metaObject->indexOfProperty(qPrintable(sKey)); + if (index == -1) + { + qAssert(false); + return; + } + m_metaProperty = m_metaObject->property(index); + setSqlType(QxClassX::getSqlTypeByClassName(m_metaProperty.typeName())); + } + + bool QxDataMember_QObject::isEqual(const void *pOwner1, const void *pOwner2) const + { + if ((pOwner1 == NULL) || (pOwner2 == NULL)) + { + return false; + } + if (pOwner1 == pOwner2) + { + return true; + } + QVariant var1 = m_metaProperty.read(static_cast(pOwner1)); + QVariant var2 = m_metaProperty.read(static_cast(pOwner2)); + return (var1 == var2); + } + + QVariant QxDataMember_QObject::toVariant(const void *pOwner, const QString &sFormat, int iIndexName /* = -1 */, qx::cvt::context::ctx_type ctx /* = qx::cvt::context::e_no_context */) const + { + Q_UNUSED(sFormat); + Q_UNUSED(iIndexName); + Q_UNUSED(ctx); + return m_metaProperty.read(static_cast(pOwner)); + } + + qx_bool QxDataMember_QObject::fromVariant(void *pOwner, const QVariant &v, const QString &sFormat, int iIndexName /* = -1 */, qx::cvt::context::ctx_type ctx /* = qx::cvt::context::e_no_context */) + { + Q_UNUSED(sFormat); + Q_UNUSED(iIndexName); + Q_UNUSED(ctx); + return m_metaProperty.write(static_cast(pOwner), v); + } + + QString QxDataMember_QObject::getType() const + { + return QString(m_metaProperty.typeName()); + } + +#ifndef _QX_NO_JSON + + QJsonValue QxDataMember_QObject::toJson(const void *pOwner, const QString &sFormat) const + { + Q_UNUSED(sFormat); + QVariant val = m_metaProperty.read(static_cast(pOwner)); + +#ifdef _QX_ENABLE_MONGODB + if (getIsPrimaryKey() && sFormat.startsWith("mongodb")) + { + QString tmp = val.toString(); + if (tmp.startsWith("qx_oid:")) + { + QJsonObject obj; + obj.insert("$oid", QJsonValue(tmp.right(tmp.size() - 7))); + return QJsonValue(obj); + } + } +#endif // _QX_ENABLE_MONGODB + + return QJsonValue::fromVariant(val); + } + + qx_bool QxDataMember_QObject::fromJson(void *pOwner, const QJsonValue &j, const QString &sFormat) + { + Q_UNUSED(sFormat); + QVariant val = j.toVariant(); + +#ifdef _QX_ENABLE_MONGODB + if (getIsPrimaryKey() && val.toString().isEmpty() && j.isObject() && sFormat.startsWith("mongodb")) + { + QJsonObject obj = j.toObject(); + QString tmp; + if (obj.contains("$oid")) + { + tmp = obj.value("$oid").toString(); + } + if (!tmp.isEmpty()) + { + tmp = ("qx_oid:" + tmp); + val = tmp; + } + } +#endif // _QX_ENABLE_MONGODB + + return m_metaProperty.write(static_cast(pOwner), val); + } + +#endif // _QX_NO_JSON + + qx::any QxDataMember_QObject::getDataPtr(const void *pOwner) const + { + Q_UNUSED(pOwner); + qDebug("[QxOrm] qx::QxDataMember_QObject::getDataPtr() : '%s'", "cannot access to the data-member pointer with Qt introspection engine"); + qAssert(false); + return qx::any(); + } + + qx::any QxDataMember_QObject::getDataPtr(void *pOwner) + { + Q_UNUSED(pOwner); + qDebug("[QxOrm] qx::QxDataMember_QObject::getDataPtr() : '%s'", "cannot access to the data-member pointer with Qt introspection engine"); + qAssert(false); + return qx::any(); + } + + void *QxDataMember_QObject::getDataVoidPtr(const void *pOwner) const + { + Q_UNUSED(pOwner); + qDebug("[QxOrm] qx::QxDataMember_QObject::getDataVoidPtr() : '%s'", "cannot access to the data-member pointer with Qt introspection engine"); + qAssert(false); + return NULL; + } + + void *QxDataMember_QObject::getDataVoidPtr(void *pOwner) + { + Q_UNUSED(pOwner); + qDebug("[QxOrm] qx::QxDataMember_QObject::getDataVoidPtr() : '%s'", "cannot access to the data-member pointer with Qt introspection engine"); + qAssert(false); + return NULL; + } + +} // namespace qx + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#if _QX_SERIALIZE_POLYMORPHIC +QX_DATA_MEMBER_QOBJECT_IMPL_VIRTUAL_ARCHIVE_CPP(boost::archive::polymorphic_iarchive, boost::archive::polymorphic_oarchive) +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY +QX_DATA_MEMBER_QOBJECT_IMPL_VIRTUAL_ARCHIVE_CPP(boost::archive::binary_iarchive, boost::archive::binary_oarchive) +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT +QX_DATA_MEMBER_QOBJECT_IMPL_VIRTUAL_ARCHIVE_CPP(boost::archive::text_iarchive, boost::archive::text_oarchive) +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML +QX_DATA_MEMBER_QOBJECT_IMPL_VIRTUAL_ARCHIVE_CPP(boost::archive::xml_iarchive, boost::archive::xml_oarchive) +#endif // _QX_SERIALIZE_XML + +#if _QX_SERIALIZE_PORTABLE_BINARY +QX_DATA_MEMBER_QOBJECT_IMPL_VIRTUAL_ARCHIVE_CPP(eos::portable_iarchive, eos::portable_oarchive) +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#if _QX_SERIALIZE_WIDE_BINARY +QX_DATA_MEMBER_QOBJECT_IMPL_VIRTUAL_ARCHIVE_CPP(boost::archive::binary_wiarchive, boost::archive::binary_woarchive) +#endif // _QX_SERIALIZE_WIDE_BINARY + +#if _QX_SERIALIZE_WIDE_TEXT +QX_DATA_MEMBER_QOBJECT_IMPL_VIRTUAL_ARCHIVE_CPP(boost::archive::text_wiarchive, boost::archive::text_woarchive) +#endif // _QX_SERIALIZE_WIDE_TEXT + +#if _QX_SERIALIZE_WIDE_XML +QX_DATA_MEMBER_QOBJECT_IMPL_VIRTUAL_ARCHIVE_CPP(boost::archive::xml_wiarchive, boost::archive::xml_woarchive) +#endif // _QX_SERIALIZE_WIDE_XML + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxFactory/IxFactory.cpp b/src/QxFactory/IxFactory.cpp new file mode 100644 index 0000000..cef575f --- /dev/null +++ b/src/QxFactory/IxFactory.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include +#include + +#include + +namespace qx +{ + + IxFactory::IxFactory(const QString &sKey) : m_sKeyFactory(sKey) + { +#ifdef _QX_TRACE_CONSTRUCTOR_DESTRUCTOR + qDebug("[QxOrm] qx::IxFactory constructor '%s'", qPrintable(m_sKeyFactory)); +#endif // _QX_TRACE_CONSTRUCTOR_DESTRUCTOR + + QxFactoryX::getSingleton()->registerFactory(m_sKeyFactory, this); + } + + IxFactory::~IxFactory() + { +#ifdef _QX_TRACE_CONSTRUCTOR_DESTRUCTOR + qDebug("[QxOrm] qx::IxFactory destructor '%s'", qPrintable(m_sKeyFactory)); +#endif // _QX_TRACE_CONSTRUCTOR_DESTRUCTOR + + if (!QxFactoryX::isSingletonNull()) + { + QxFactoryX::getSingleton()->unregisterFactory(m_sKeyFactory); + } + } + +} // namespace qx diff --git a/src/QxFactory/QxFactoryX.cpp b/src/QxFactory/QxFactoryX.cpp new file mode 100644 index 0000000..35bb660 --- /dev/null +++ b/src/QxFactory/QxFactoryX.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::QxFactoryX) + +namespace qx +{ + + void QxFactoryX::registerFactory(const QString &sKey, IxFactory *pFactory) + { + QMutexLocker locker(&m_oMutexFactoryX); + qAssert(!sKey.isEmpty() && !m_mapFactoryX.contains(sKey)); + + if (!pFactory || sKey.isEmpty() || m_mapFactoryX.contains(sKey)) + return; + + m_mapFactoryX.insert(sKey, pFactory); + } + + void QxFactoryX::unregisterFactory(const QString &sKey) + { + QMutexLocker locker(&m_oMutexFactoryX); + m_mapFactoryX.remove(sKey); + } + + qx::any QxFactoryX::createObject(const QString &sKey, bool bRawPointer /* = false */) const + { + IxFactory *pFactory = (m_mapFactoryX.contains(sKey) ? m_mapFactoryX.value(sKey) : NULL); + if (!pFactory) + { + qDebug("[QxOrm] cannot create an instance of type '%s'", qPrintable(sKey)); + } + + return (pFactory ? pFactory->createObject(bRawPointer) : qx::any()); + } + + void *QxFactoryX::createObjectNudePtr(const QString &sKey) const + { + IxFactory *pFactory = (m_mapFactoryX.contains(sKey) ? m_mapFactoryX.value(sKey) : NULL); + if (!pFactory) + { + qDebug("[QxOrm] cannot create an instance of type '%s'", qPrintable(sKey)); + } + + return (pFactory ? pFactory->createObjectNudePtr() : NULL); + } + +#ifndef _QX_NO_RTTI + const std::type_info &QxFactoryX::typeInfo(const QString &sKey) const + { + IxFactory *pFactory = (m_mapFactoryX.contains(sKey) ? m_mapFactoryX.value(sKey) : NULL); + if (!pFactory) + { + qDebug("[QxOrm] cannot get informations about type '%s'", qPrintable(sKey)); + } + + return (pFactory ? pFactory->typeInfo() : typeid(void)); + } +#endif // _QX_NO_RTTI + +} // namespace qx diff --git a/src/QxHttpServer/QxHttpCookie.cpp b/src/QxHttpServer/QxHttpCookie.cpp new file mode 100644 index 0000000..d0c66a2 --- /dev/null +++ b/src/QxHttpServer/QxHttpCookie.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK + +#include + +#include + +#include + +namespace qx +{ + + QxHttpCookie::QxHttpCookie() : path("/"), maxAge(0), secure(false), httpOnly(false) { ; } + + QxHttpCookie::QxHttpCookie(const QByteArray &name_, const QByteArray &value_, const QByteArray &domain_ /* = QByteArray() */, const QByteArray &path_ /* = QByteArray("/") */, qlonglong maxAge_ /* = 0 */, bool secure_ /* = false */, bool httpOnly_ /* = false */) : name(name_), value(value_), domain(domain_), path(path_), maxAge(maxAge_), secure(secure_), httpOnly(httpOnly_) { ; } + + QxHttpCookie::~QxHttpCookie() { ; } + + QByteArray QxHttpCookie::toString() const + { + QByteArray result = name; + if (!value.isEmpty()) + { + result += "=" + value; + } + if (!domain.isEmpty()) + { + result += "; Domain=" + domain; + } + if (!path.isEmpty()) + { + result += "; Path=" + path; + } + if (maxAge != 0) + { + result += "; Max-Age=" + QByteArray::number(maxAge); + } + if (secure) + { + result += "; Secure"; + } + if (httpOnly) + { + result += "; HttpOnly"; + } + return result; + } + + QHash QxHttpCookie::parse(const QByteArray &cookies) + { + QHash result; + QList lst = cookies.split(';'); + Q_FOREACH (QByteArray data, lst) + { + QByteArray name, value; + int pos = data.indexOf('='); + if (pos <= 0) + { + name = data.trimmed(); + } + else + { + name = data.left(pos).trimmed(); + value = data.mid(pos + 1).trimmed(); + } + if (name.isEmpty()) + { + continue; + } + + QxHttpCookie cookie(name, value); + result.insert(cookie.name, cookie); + } + return result; + } + +} // namespace qx + +#endif // _QX_ENABLE_QT_NETWORK diff --git a/src/QxHttpServer/QxHttpRequest.cpp b/src/QxHttpServer/QxHttpRequest.cpp new file mode 100644 index 0000000..71bd855 --- /dev/null +++ b/src/QxHttpServer/QxHttpRequest.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK + +#include + +#include + +#include +#include + +#include + +namespace qx +{ + + struct Q_DECL_HIDDEN QxHttpRequest::QxHttpRequestImpl + { + + QUrl m_url; //!< HTTP request path + QString m_command; //!< HTTP request method : GET, POST, HEAD, PUT, DELETE, etc... + QString m_version; //!< HTTP request version + QByteArray m_data; //!< HTTP request body content + QHash m_headers; //!< HTTP request headers + QHash m_cookies; //!< HTTP request cookies + QHash m_dispatchParams; //!< HTTP request dynamic URL parameters from server dispatcher + QHash m_params; //!< HTTP request parameters (from URL and from body if content-type is 'application/x-www-form-urlencoded', which means web-form submit) + QString m_address; //!< HTTP client IP address + long m_port; //!< HTTP client port + QxHttpTransaction *m_transaction; //!< HTTP transaction + QString m_guid; //!< HTTP request internal GUID (can be used for logging for example) + + QxHttpRequestImpl(QxHttpTransaction *transaction) : m_port(0), m_transaction(transaction) + { + qAssert(m_transaction != NULL); + m_guid = QUuid::createUuid().toString(); + } + ~QxHttpRequestImpl() { ; } + }; + + QxHttpRequest::QxHttpRequest(QxHttpTransaction *transaction) : m_pImpl(new QxHttpRequestImpl(transaction)) { ; } + + QxHttpRequest::~QxHttpRequest() { ; } + + QUrl &QxHttpRequest::url() { return m_pImpl->m_url; } + + QString &QxHttpRequest::command() { return m_pImpl->m_command; } + + QString &QxHttpRequest::version() { return m_pImpl->m_version; } + + QByteArray &QxHttpRequest::data() { return m_pImpl->m_data; } + + QHash &QxHttpRequest::headers() { return m_pImpl->m_headers; } + + QByteArray QxHttpRequest::header(const QByteArray &key) + { + QHashIterator itr(m_pImpl->m_headers); + while (itr.hasNext()) + { + itr.next(); + if (itr.key().trimmed().toLower() == key.trimmed().toLower()) + { + return itr.value(); + } + } + return QByteArray(); + } + + QHash &QxHttpRequest::cookies() { return m_pImpl->m_cookies; } + + QxHttpCookie QxHttpRequest::cookie(const QByteArray &name) { return m_pImpl->m_cookies.value(name); } + + QHash &QxHttpRequest::dispatchParams() { return m_pImpl->m_dispatchParams; } + + QHash &QxHttpRequest::params() { return m_pImpl->m_params; } + + QString QxHttpRequest::param(const QString &key) { return m_pImpl->m_params.value(key); } + + QString &QxHttpRequest::sourceAddress() { return m_pImpl->m_address; } + + long &QxHttpRequest::sourcePort() { return m_pImpl->m_port; } + + QString QxHttpRequest::guid() const { return m_pImpl->m_guid; } + +} // namespace qx + +#endif // _QX_ENABLE_QT_NETWORK diff --git a/src/QxHttpServer/QxHttpResponse.cpp b/src/QxHttpServer/QxHttpResponse.cpp new file mode 100644 index 0000000..748dc3a --- /dev/null +++ b/src/QxHttpServer/QxHttpResponse.cpp @@ -0,0 +1,478 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK + +#include + +#include +#include + +#include + +#include + +#ifndef QByteArrayLiteral +#define QByteArrayLiteral QByteArray +#endif // QByteArrayLiteral + +namespace qx +{ + + struct Q_DECL_HIDDEN QxHttpResponse::QxHttpResponseImpl + { + + int m_status; //!< HTTP response status (200 OK, 400 Bad Request, 401 Unauthorized, 500 Internal Server Error, etc...) + QByteArray m_data; //!< HTTP response body content + QHash m_headers; //!< HTTP response headers + QHash m_cookies; //!< HTTP response cookies + QxHttpTransaction *m_transaction; //!< HTTP transaction + bool m_isChunked; //!< HTTP response is chunked (useful for streaming for example) + + QxHttpResponseImpl(QxHttpTransaction *transaction) : m_status(200), m_transaction(transaction), m_isChunked(false) + { + qAssert(m_transaction != NULL); + initHeaders(); + } + ~QxHttpResponseImpl() { ; } + + void initHeaders() + { + m_headers.insert("Server", "QxOrm HTTP server"); + m_headers.insert("Date", currentDateTimeGMT()); + m_headers.insert("Content-Type", "text/plain; charset=iso-8859-1"); + if (qx::service::QxConnect::getSingleton()->getKeepAlive() != 0) + { + m_headers.insert("Connection", "keep-alive"); + } + else + { + m_headers.insert("Connection", "close"); + } + } + + qx_bool writeChunked(const QByteArray &data) + { + if (data.isEmpty()) + { + return qx_bool(true); + } + if (!m_transaction) + { + qAssert(false); + return qx_bool(false, "No HTTP transaction associated to the response"); + } + qx_bool bWriteChunked = m_transaction->writeChunked(data); + if (bWriteChunked) + { + m_isChunked = true; + } + return bWriteChunked; + } + + QByteArray currentDateTimeGMT() + { + QByteArray day, month; + QDateTime dt = QDateTime::currentDateTimeUtc(); + switch (dt.date().dayOfWeek()) + { + case 1: + day = QByteArrayLiteral("Mon"); + break; + case 2: + day = QByteArrayLiteral("Tue"); + break; + case 3: + day = QByteArrayLiteral("Wed"); + break; + case 4: + day = QByteArrayLiteral("Thu"); + break; + case 5: + day = QByteArrayLiteral("Fri"); + break; + case 6: + day = QByteArrayLiteral("Sat"); + break; + case 7: + day = QByteArrayLiteral("Sun"); + break; + default: + qAssert(false); + break; + } + switch (dt.date().month()) + { + case 1: + month = QByteArrayLiteral("Jan"); + break; + case 2: + month = QByteArrayLiteral("Feb"); + break; + case 3: + month = QByteArrayLiteral("Mar"); + break; + case 4: + month = QByteArrayLiteral("Apr"); + break; + case 5: + month = QByteArrayLiteral("May"); + break; + case 6: + month = QByteArrayLiteral("Jun"); + break; + case 7: + month = QByteArrayLiteral("Jul"); + break; + case 8: + month = QByteArrayLiteral("Aug"); + break; + case 9: + month = QByteArrayLiteral("Sep"); + break; + case 10: + month = QByteArrayLiteral("Oct"); + break; + case 11: + month = QByteArrayLiteral("Nov"); + break; + case 12: + month = QByteArrayLiteral("Dec"); + break; + default: + qAssert(false); + break; + } + QByteArray day_ = QString("%1").arg(dt.date().day(), 2, 10, QChar('0')).toLatin1(); + QByteArray year = QString("%1").arg(dt.date().year(), 4, 10, QChar('0')).toLatin1(); + QByteArray hour = QString("%1").arg(dt.time().hour(), 2, 10, QChar('0')).toLatin1(); + QByteArray minute = QString("%1").arg(dt.time().minute(), 2, 10, QChar('0')).toLatin1(); + QByteArray second = QString("%1").arg(dt.time().second(), 2, 10, QChar('0')).toLatin1(); + return (day + ", " + day_ + " " + month + " " + year + " " + hour + ":" + minute + ":" + second + " GMT"); + } + }; + + QxHttpResponse::QxHttpResponse(QxHttpTransaction *transaction) : m_pImpl(new QxHttpResponseImpl(transaction)) { ; } + + QxHttpResponse::~QxHttpResponse() { ; } + + int &QxHttpResponse::status() { return m_pImpl->m_status; } + + QByteArray &QxHttpResponse::data() { return m_pImpl->m_data; } + + QHash &QxHttpResponse::headers() { return m_pImpl->m_headers; } + + QByteArray QxHttpResponse::header(const QByteArray &key) + { + QHashIterator itr(m_pImpl->m_headers); + while (itr.hasNext()) + { + itr.next(); + if (itr.key().trimmed().toLower() == key.trimmed().toLower()) + { + return itr.value(); + } + } + return QByteArray(); + } + + QHash &QxHttpResponse::cookies() { return m_pImpl->m_cookies; } + + QxHttpCookie QxHttpResponse::cookie(const QByteArray &name) { return m_pImpl->m_cookies.value(name); } + + qx_bool QxHttpResponse::writeChunked(const QByteArray &data) { return m_pImpl->writeChunked(data); } + + bool QxHttpResponse::isChunked() const { return m_pImpl->m_isChunked; } + + QByteArray QxHttpResponse::statusDesc() + { + QByteArray desc; + switch (m_pImpl->m_status) + { + case 100: + desc = QByteArrayLiteral("Continue"); + break; + case 101: + desc = QByteArrayLiteral("Switching Protocols"); + break; + case 102: + desc = QByteArrayLiteral("Processing"); + break; + case 103: + desc = QByteArrayLiteral("Early Hints"); + break; + case 200: + desc = QByteArrayLiteral("OK"); + break; + case 201: + desc = QByteArrayLiteral("Created"); + break; + case 202: + desc = QByteArrayLiteral("Accepted"); + break; + case 203: + desc = QByteArrayLiteral("Non-Authoritative Information"); + break; + case 204: + desc = QByteArrayLiteral("No Content"); + break; + case 205: + desc = QByteArrayLiteral("Reset Content"); + break; + case 206: + desc = QByteArrayLiteral("Partial Content"); + break; + case 207: + desc = QByteArrayLiteral("Multi-Status"); + break; + case 208: + desc = QByteArrayLiteral("Already Reported"); + break; + case 210: + desc = QByteArrayLiteral("Content Different"); + break; + case 226: + desc = QByteArrayLiteral("IM Used"); + break; + case 300: + desc = QByteArrayLiteral("Multiple Choices"); + break; + case 301: + desc = QByteArrayLiteral("Moved Permanently"); + break; + case 302: + desc = QByteArrayLiteral("Found"); + break; + case 303: + desc = QByteArrayLiteral("See Other"); + break; + case 304: + desc = QByteArrayLiteral("Not Modified"); + break; + case 305: + desc = QByteArrayLiteral("Use Proxy"); + break; + case 306: + desc = QByteArrayLiteral("Switch Proxy"); + break; + case 307: + desc = QByteArrayLiteral("Temporary Redirect"); + break; + case 308: + desc = QByteArrayLiteral("Permanent Redirect"); + break; + case 310: + desc = QByteArrayLiteral("Too many Redirects"); + break; + case 400: + desc = QByteArrayLiteral("Bad Request"); + break; + case 401: + desc = QByteArrayLiteral("Unauthorized"); + break; + case 402: + desc = QByteArrayLiteral("Payment Required"); + break; + case 403: + desc = QByteArrayLiteral("Forbidden"); + break; + case 404: + desc = QByteArrayLiteral("Not Found"); + break; + case 405: + desc = QByteArrayLiteral("Method Not Allowed"); + break; + case 406: + desc = QByteArrayLiteral("Not Acceptable"); + break; + case 407: + desc = QByteArrayLiteral("Proxy Authentication Required"); + break; + case 408: + desc = QByteArrayLiteral("Request Time-out"); + break; + case 409: + desc = QByteArrayLiteral("Conflict"); + break; + case 410: + desc = QByteArrayLiteral("Gone"); + break; + case 411: + desc = QByteArrayLiteral("Length Required"); + break; + case 412: + desc = QByteArrayLiteral("Precondition Failed"); + break; + case 413: + desc = QByteArrayLiteral("Request Entity Too Large"); + break; + case 414: + desc = QByteArrayLiteral("Request-URI Too Long"); + break; + case 415: + desc = QByteArrayLiteral("Unsupported Media Type"); + break; + case 416: + desc = QByteArrayLiteral("Requested range unsatisfiable"); + break; + case 417: + desc = QByteArrayLiteral("Expectation failed"); + break; + case 418: + desc = QByteArrayLiteral("I’m a teapot"); + break; + case 421: + desc = QByteArrayLiteral("Bad mapping / Misdirected Request"); + break; + case 422: + desc = QByteArrayLiteral("Unprocessable entity"); + break; + case 423: + desc = QByteArrayLiteral("Locked"); + break; + case 424: + desc = QByteArrayLiteral("Method failure"); + break; + case 425: + desc = QByteArrayLiteral("Unordered Collection"); + break; + case 426: + desc = QByteArrayLiteral("Upgrade Required"); + break; + case 428: + desc = QByteArrayLiteral("Precondition Required"); + break; + case 429: + desc = QByteArrayLiteral("Too Many Requests"); + break; + case 431: + desc = QByteArrayLiteral("Request Header Fields Too Large"); + break; + case 449: + desc = QByteArrayLiteral("Retry With"); + break; + case 450: + desc = QByteArrayLiteral("Blocked by Windows Parental Controls"); + break; + case 451: + desc = QByteArrayLiteral("Unavailable For Legal Reasons"); + break; + case 456: + desc = QByteArrayLiteral("Unrecoverable Error"); + break; + case 444: + desc = QByteArrayLiteral("No Response"); + break; + case 495: + desc = QByteArrayLiteral("SSL Certificate Error"); + break; + case 496: + desc = QByteArrayLiteral("SSL Certificate Required"); + break; + case 497: + desc = QByteArrayLiteral("HTTP Request Sent to HTTPS Port"); + break; + case 498: + desc = QByteArrayLiteral("Token expired/invalid"); + break; + case 499: + desc = QByteArrayLiteral("Client Closed Request"); + break; + case 500: + desc = QByteArrayLiteral("Internal Server Error"); + break; + case 501: + desc = QByteArrayLiteral("Not Implemented"); + break; + case 502: + desc = QByteArrayLiteral("Bad Gateway ou Proxy Error"); + break; + case 503: + desc = QByteArrayLiteral("Service Unavailable"); + break; + case 504: + desc = QByteArrayLiteral("Gateway Time-out"); + break; + case 505: + desc = QByteArrayLiteral("HTTP Version not supported"); + break; + case 506: + desc = QByteArrayLiteral("Variant Also Negotiates"); + break; + case 507: + desc = QByteArrayLiteral("Insufficient storage"); + break; + case 508: + desc = QByteArrayLiteral("Loop detected"); + break; + case 509: + desc = QByteArrayLiteral("Bandwidth Limit Exceeded"); + break; + case 510: + desc = QByteArrayLiteral("Not extended"); + break; + case 511: + desc = QByteArrayLiteral("Network authentication required"); + break; + case 520: + desc = QByteArrayLiteral("Unknown Error"); + break; + case 521: + desc = QByteArrayLiteral("Web Server Is Down"); + break; + case 522: + desc = QByteArrayLiteral("Connection Timed Out"); + break; + case 523: + desc = QByteArrayLiteral("Origin Is Unreachable"); + break; + case 524: + desc = QByteArrayLiteral("A Timeout Occurred"); + break; + case 525: + desc = QByteArrayLiteral("SSL Handshake Failed"); + break; + case 526: + desc = QByteArrayLiteral("Invalid SSL Certificate"); + break; + case 527: + desc = QByteArrayLiteral("Railgun Error"); + break; + default: + desc = QByteArrayLiteral("Unknown status code"); + break; + } + return desc; + } + +} // namespace qx + +#endif // _QX_ENABLE_QT_NETWORK diff --git a/src/QxHttpServer/QxHttpServer.cpp b/src/QxHttpServer/QxHttpServer.cpp new file mode 100644 index 0000000..5def479 --- /dev/null +++ b/src/QxHttpServer/QxHttpServer.cpp @@ -0,0 +1,769 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK + +#include + +#include +#include +#include +#include + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#include +#include +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + +#include + +#include +#include + +#include +#include + +#include +#include + +#include + +#include + +namespace qx +{ + + struct QxHttpServerDispatchSegment + { + + enum type_segment + { + _static, + _variable, + _variable_and_type, + _variable_and_regexp, + _wildcard + }; + + type_segment m_type; //!< HTTP server dispatcher segment type + QString m_path; //!< HTTP server dispatcher segment path (part of item path splitted by '/' character) + qx_hash_result m_pathHash; //!< HTTP server dispatcher segment path hash (for optimization) + QString m_varName; //!< HTTP server dispatcher segment variable name + QString m_varType; //!< HTTP server dispatcher segment variable type (or regular expression) + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + QRegularExpression m_regExp; //!< HTTP server dispatcher segment regular expression +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + QRegExp m_regExp; //!< HTTP server dispatcher segment regular expression (Qt4 version) +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + + QxHttpServerDispatchSegment() : m_type(_static), m_pathHash(0) { ; } + ~QxHttpServerDispatchSegment() { ; } + + bool parse(); + bool check(const QString &path, qx_hash_result hash) const; + void fillParam(const QString &path, QHash ¶meters) const; + }; + + struct QxHttpServerDispatchItem + { + + QString m_command; //!< HTTP server dispatcher item command (GET, POST, DELETE, PUT, etc...) + qx_hash_result m_commandHash; //!< HTTP server dispatcher item command hash (for optimization) + bool m_commandWildcard; //!< HTTP server dispatcher item wildcard command (means accept all commands) + QString m_path; //!< HTTP server dispatcher item path (example : /foo/bar///*) + QList> m_segments; //!< HTTP server dispatcher item list of segments (path splitted by '/' character) + QxHttpServer::type_fct_custom_request_handler m_fct; //!< HTTP server dispatcher item callback (executed in its dedicated thread) + + QxHttpServerDispatchItem() : m_commandHash(0), m_commandWildcard(false) { ; } + ~QxHttpServerDispatchItem() { ; } + + bool parse(); + }; + + struct QxHttpServerDispatcher + { + + qx::QxCollection> m_lstDispatchItems; //!< HTTP server dispatcher items (associate url to callback) + + QxHttpServerDispatcher() { ; } + ~QxHttpServerDispatcher() { ; } + + void dispatch(const QString &command, const QString &path, QxHttpServer::type_fct_custom_request_handler fct, long position); + std::shared_ptr find(qx::QxHttpRequest &request) const; + }; + + struct Q_DECL_HIDDEN QxHttpServer::QxHttpServerImpl + { + + typedef std::shared_ptr type_fct_ptr; + + QMutex m_mutex; //!< Mutex => 'QxHttpServer' is thread-safe (callbacks can be changed even if server is running) + qx::service::QxThreadPool_ptr m_pThreadPool; //!< HTTP server is managed by a thread pool + type_fct_ptr m_fctCustomRequestHandler; //!< Callback to manage requests and build responses (executed in its dedicated thread) + std::unique_ptr m_pDispatcher; //!< HTTP server dispatcher (each callback is executed in its dedicated thread) + type_fct_ptr m_fctBeforeDispatching; //!< Callback function called before handling the request (executed in its dedicated thread), can be used for logging for example + type_fct_ptr m_fctAfterDispatching; //!< Callback function called after handling the request and generating the response (executed in its dedicated thread), can be used for logging for example + QAbstractEventDispatcher *m_pEventDispatcher; //!< Can be used to optimize socket implementation (using epoll on Linux for example), for example : https://github.com/sjinks/qt_eventdispatcher_epoll or https://github.com/connectedtable/qeventdispatcher_epoll + + QxHttpServerImpl() : m_pDispatcher(new QxHttpServerDispatcher()), m_pEventDispatcher(NULL) { ; } + ~QxHttpServerImpl() { ; } + }; + + QxHttpServer::QxHttpServer(QObject *parent /* = NULL */) : QObject(parent), m_pImpl(new QxHttpServerImpl()) + { + qRegisterMetaType("qx::QxHttpTransaction_ptr"); + qRegisterMetaType("QxHttpTransaction_ptr"); + } + + QxHttpServer::~QxHttpServer() { stopServer(); } + + void QxHttpServer::startServer() + { + if (m_pImpl->m_pThreadPool) + { + stopServer(); + } + qx::service::QxConnect::getSingleton()->setModeHTTP(true); + m_pImpl->m_pThreadPool.reset(new qx::service::QxThreadPool()); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + if (m_pImpl->m_pEventDispatcher) + { + m_pImpl->m_pThreadPool->setEventDispatcher(m_pImpl->m_pEventDispatcher); + } +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + + QObject::connect(m_pImpl->m_pThreadPool.get(), SIGNAL(error(const QString &, qx::service::QxTransaction_ptr)), this, SLOT(onError(const QString &, qx::service::QxTransaction_ptr))); + QObject::connect(m_pImpl->m_pThreadPool.get(), SIGNAL(serverIsRunning(bool, qx::service::QxServer *)), this, SLOT(onServerIsRunning(bool, qx::service::QxServer *))); + QObject::connect(m_pImpl->m_pThreadPool.get(), SIGNAL(transactionStarted(qx::service::QxTransaction_ptr)), this, SLOT(onTransactionStarted(qx::service::QxTransaction_ptr))); + QObject::connect(m_pImpl->m_pThreadPool.get(), SIGNAL(transactionFinished(qx::service::QxTransaction_ptr)), this, SLOT(onTransactionFinished(qx::service::QxTransaction_ptr))); + QObject::connect(m_pImpl->m_pThreadPool.get(), SIGNAL(customRequestHandler(qx::service::QxTransaction_ptr)), this, SLOT(onCustomRequestHandler(qx::service::QxTransaction_ptr)), Qt::DirectConnection); + m_pImpl->m_pThreadPool->start(); + } + + void QxHttpServer::stopServer() + { + if (!m_pImpl->m_pThreadPool) + { + return; + } + m_pImpl->m_pThreadPool.reset(); + Q_EMIT serverStatusChanged(false); + } + + void QxHttpServer::setCustomRequestHandler(QxHttpServer::type_fct_custom_request_handler fct) + { + QMutexLocker locker(&m_pImpl->m_mutex); + if (!fct) + { + m_pImpl->m_fctCustomRequestHandler.reset(); + return; + } + m_pImpl->m_fctCustomRequestHandler = std::make_shared(); + (*m_pImpl->m_fctCustomRequestHandler) = fct; + } + + QxHttpServer &QxHttpServer::dispatch(const QString &command, const QString &path, QxHttpServer::type_fct_custom_request_handler fct, long position /* = -1 */) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_pDispatcher->dispatch(command.toUpper(), path, fct, position); + return (*this); + } + + void QxHttpServer::beforeDispatching(QxHttpServer::type_fct_custom_request_handler fct) + { + QMutexLocker locker(&m_pImpl->m_mutex); + if (!fct) + { + m_pImpl->m_fctBeforeDispatching.reset(); + return; + } + m_pImpl->m_fctBeforeDispatching = std::make_shared(); + (*m_pImpl->m_fctBeforeDispatching) = fct; + } + + void QxHttpServer::afterDispatching(QxHttpServer::type_fct_custom_request_handler fct) + { + QMutexLocker locker(&m_pImpl->m_mutex); + if (!fct) + { + m_pImpl->m_fctAfterDispatching.reset(); + return; + } + m_pImpl->m_fctAfterDispatching = std::make_shared(); + (*m_pImpl->m_fctAfterDispatching) = fct; + } + + void QxHttpServer::clearDispatcher() + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_pDispatcher.reset(new QxHttpServerDispatcher()); + } + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + void QxHttpServer::setEventDispatcher(QAbstractEventDispatcher *pEventDispatcher) + { + m_pImpl->m_pEventDispatcher = pEventDispatcher; + } +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + + void QxHttpServer::onError(const QString &err, qx::service::QxTransaction_ptr transaction) + { + qx::QxHttpTransaction_ptr httpTransaction = std::dynamic_pointer_cast(transaction); + if (transaction && (!httpTransaction)) + { + qAssertMsg(false, "[QxOrm] qx::QxHttpServer::onError", "unable to get HTTP transaction using std::dynamic_pointer_cast"); + } + Q_EMIT error(err, httpTransaction); + } + + void QxHttpServer::onServerIsRunning(bool bIsRunning, qx::service::QxServer *pServer) + { + Q_UNUSED(pServer); + if (bIsRunning) + { + qx::service::QxConnect *serverSettings = qx::service::QxConnect::getSingleton(); + QString msg = "HTTP server is running ("; + msg += "port: " + QString::number(serverSettings->getPort()); + msg += ", threads: " + QString::number(serverSettings->getThreadCount()); +#ifndef QT_NO_SSL + msg += ", SSL: " + (serverSettings->getSSLEnabled() ? QString("enabled") : QString("disabled")); +#else // QT_NO_SSL + msg += ", SSL: disabled"; +#endif // QT_NO_SSL + msg += ")"; + qDebug("[QxOrm] %s", qPrintable(msg)); + } + else + { + qDebug("[QxOrm] %s", "HTTP server has been stopped"); + } + Q_EMIT serverStatusChanged(bIsRunning); + } + + void QxHttpServer::onTransactionStarted(qx::service::QxTransaction_ptr transaction) + { + qx::QxHttpTransaction_ptr httpTransaction = std::dynamic_pointer_cast(transaction); + if (transaction && (!httpTransaction)) + { + qAssertMsg(false, "[QxOrm] qx::QxHttpServer::onTransactionStarted", "unable to get HTTP transaction using std::dynamic_pointer_cast"); + } + Q_EMIT transactionStarted(httpTransaction); + } + + void QxHttpServer::onTransactionFinished(qx::service::QxTransaction_ptr transaction) + { + qx::QxHttpTransaction_ptr httpTransaction = std::dynamic_pointer_cast(transaction); + if (transaction && (!httpTransaction)) + { + qAssertMsg(false, "[QxOrm] qx::QxHttpServer::onTransactionFinished", "unable to get HTTP transaction using std::dynamic_pointer_cast"); + } + Q_EMIT transactionFinished(httpTransaction); + } + + void QxHttpServer::onCustomRequestHandler(qx::service::QxTransaction_ptr transaction) + { + // Check if transaction is valid + qx::QxHttpTransaction_ptr httpTransaction = std::dynamic_pointer_cast(transaction); + if (transaction && (!httpTransaction)) + { + qAssertMsg(false, "[QxOrm] qx::QxHttpServer::onCustomRequestHandler", "unable to get HTTP transaction using std::dynamic_pointer_cast"); + return; + } + if (!httpTransaction) + { + qAssertMsg(false, "[QxOrm] qx::QxHttpServer::onCustomRequestHandler", "HTTP transaction is empty"); + return; + } + + // Get request and response instances + qx::QxHttpRequest &request = httpTransaction->request(); + qx::QxHttpResponse &response = httpTransaction->response(); + + // Get custom request handler + before/after dispatching callbacks (thread-safe) + std::shared_ptr fctCustomHandler; + std::shared_ptr fctBeforeDispatching; + std::shared_ptr fctAfterDispatching; + { + QMutexLocker locker(&m_pImpl->m_mutex); + fctCustomHandler = m_pImpl->m_fctCustomRequestHandler; + fctBeforeDispatching = m_pImpl->m_fctBeforeDispatching; + fctAfterDispatching = m_pImpl->m_fctAfterDispatching; + } + + // Execute before dispatching callback + if (fctBeforeDispatching && (*fctBeforeDispatching)) + { + try + { + (*fctBeforeDispatching)(request, response); + } + catch (const qx::exception &x) + { + qx_bool xb = x.toQxBool(); + httpTransaction->setMessageReturn(xb); + } + catch (const std::exception &e) + { + httpTransaction->setMessageReturn(qx_bool(500, e.what())); + } + catch (...) + { + httpTransaction->setMessageReturn(qx_bool(500, "[QxOrm] Unknown server error (before dispatching)")); + } + } + + // Get dispatcher item callback (thread-safe) + std::shared_ptr itemDispatch; + { + QMutexLocker locker(&m_pImpl->m_mutex); + itemDispatch = m_pImpl->m_pDispatcher->find(request); + } + + try + { + // Execute dispatcher item callback or custom request handler + if (itemDispatch && itemDispatch->m_fct) + { + itemDispatch->m_fct(request, response); + } + else if (fctCustomHandler && (*fctCustomHandler)) + { + (*fctCustomHandler)(request, response); + } + else + { + httpTransaction->setMessageReturn(qx_bool(501, "[QxOrm] Not implemented : no route found for URL (" + request.command() + " - " + request.url().path() + ") : you must provide a request handler (callback) using qx::QxHttpServer::dispatch() or qx::QxHttpServer::setCustomRequestHandler() method")); + } + } + catch (const qx::exception &x) + { + qx_bool xb = x.toQxBool(); + httpTransaction->setMessageReturn(xb); + } + catch (const std::exception &e) + { + httpTransaction->setMessageReturn(qx_bool(500, e.what())); + } + catch (...) + { + httpTransaction->setMessageReturn(qx_bool(500, "[QxOrm] Unknown server error")); + } + + // Execute after dispatching callback + if (fctAfterDispatching && (*fctAfterDispatching)) + { + try + { + (*fctAfterDispatching)(request, response); + } + catch (const qx::exception &x) + { + qx_bool xb = x.toQxBool(); + httpTransaction->setMessageReturn(xb); + } + catch (const std::exception &e) + { + httpTransaction->setMessageReturn(qx_bool(500, e.what())); + } + catch (...) + { + httpTransaction->setMessageReturn(qx_bool(500, "[QxOrm] Unknown server error (after dispatching)")); + } + } + } + + void QxHttpServerDispatcher::dispatch(const QString &command, const QString &path, QxHttpServer::type_fct_custom_request_handler fct, long position) + { + QString key = command + "|" + path; + if (m_lstDispatchItems.exist(key)) + { + m_lstDispatchItems.removeByKey(key); + } + if ((!fct) || (path.isEmpty()) || (command.isEmpty())) + { + return; + } + + std::shared_ptr item = std::make_shared(); + item->m_command = command; + item->m_path = ((path == "/") ? QString("/*") : path); + item->m_fct = fct; + if (!item->parse()) + { + qAssert(false); + return; + } + if (item->m_segments.count() <= 0) + { + return; + } + if (position < 0) + { + m_lstDispatchItems.insert(key, item); + } + else + { + m_lstDispatchItems.insert(position, key, item); + } + } + + std::shared_ptr QxHttpServerDispatcher::find(qx::QxHttpRequest &request) const + { + // Check and prepare input data + if (m_lstDispatchItems.count() <= 0) + { + return std::shared_ptr(); + } + QString requestCommand = request.command().toUpper(); + qx_hash_result requestCommandHash = qHash(requestCommand); + QString requestPath = request.url().path(); + if (requestPath == "/") + { + requestPath = "/*"; + } +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QStringList requestSegments = requestPath.split("/", Qt::SkipEmptyParts); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QStringList requestSegments = requestPath.split("/", QString::SkipEmptyParts); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QList requestSegmentsHash; + requestSegmentsHash.reserve(requestSegments.count()); + Q_FOREACH (QString data, requestSegments) + { + requestSegmentsHash.append(qHash(data)); + } + if (requestSegments.count() <= 0) + { + return std::shared_ptr(); + } + + // Iterate over all dispatcher items + qx::QxCollectionIterator> itr(m_lstDispatchItems); + while (itr.next()) + { + // Check dispatcher item command and segments count + const std::shared_ptr &item = itr.value(); + if (!item) + { + qAssert(false); + continue; + } + if ((item->m_commandHash != requestCommandHash) && (!item->m_commandWildcard)) + { + continue; + } + if (item->m_segments.count() > requestSegments.count()) + { + continue; + } + if (!item->m_fct) + { + continue; + } + + // Iterate over all dispatcher item segments to check if everything matched + bool bAllSegmentsOk = true; + for (int i = 0; i < item->m_segments.count(); i++) + { + const std::shared_ptr &segment = item->m_segments.at(i); + if (!segment) + { + qAssert(false); + continue; + } + if (!segment->check(requestSegments.at(i), requestSegmentsHash.at(i))) + { + bAllSegmentsOk = false; + break; + } + } + if (!bAllSegmentsOk) + { + continue; + } + + // Iterate over all dispatcher item segments to fill URL parameters + QHash parameters; + for (int i = 0; i < item->m_segments.count(); i++) + { + const std::shared_ptr &segment = item->m_segments.at(i); + if (!segment) + { + qAssert(false); + continue; + } + segment->fillParam(requestSegments.at(i), parameters); + } + + // We have found our dispatcher item + request.dispatchParams() = parameters; + return item; + } + return std::shared_ptr(); + } + + bool QxHttpServerDispatchItem::parse() + { + m_segments.clear(); + m_commandHash = qHash(m_command); + m_commandWildcard = (m_command == "*"); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QStringList segments = m_path.split("/", Qt::SkipEmptyParts); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QStringList segments = m_path.split("/", QString::SkipEmptyParts); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + Q_FOREACH (QString data, segments) + { + data = data.trimmed(); + if (data.isEmpty()) + { + continue; + } + std::shared_ptr segment = std::make_shared(); + segment->m_path = data; + if (!segment->parse()) + { + qAssert(false); + return false; + } + m_segments.append(segment); + } + return true; + } + + bool QxHttpServerDispatchSegment::parse() + { + if (m_path.isEmpty()) + { + return true; + } + if (m_path.startsWith("<") && m_path.endsWith(">")) + { + QString path = m_path; + path.remove(0, 1); + path.chop(1); + int pos = path.indexOf(":"); + if (pos >= 0) + { + m_varName = path.left(pos).trimmed(); + m_varType = path.mid(pos + 1).trimmed(); + m_type = QxHttpServerDispatchSegment::_variable_and_type; + if (m_varType.startsWith("{") && m_varType.endsWith("}")) + { + m_varType.remove(0, 1); + m_varType.chop(1); + m_regExp.setPattern(m_varType); + m_type = QxHttpServerDispatchSegment::_variable_and_regexp; + } + } + else + { + m_varName = path.trimmed(); + m_type = QxHttpServerDispatchSegment::_variable; + } + } + else if (m_path == "*") + { + m_type = QxHttpServerDispatchSegment::_wildcard; + } + else + { + m_type = QxHttpServerDispatchSegment::_static; + m_pathHash = qHash(m_path); + } + return true; + } + + bool QxHttpServerDispatchSegment::check(const QString &path, qx_hash_result hash) const + { + bool result = false; + switch (m_type) + { + case QxHttpServerDispatchSegment::_static: + qAssert((hash != 0) && (m_pathHash != 0)); + result = (hash == m_pathHash); + break; + case QxHttpServerDispatchSegment::_variable: + qAssert(!m_varName.isEmpty()); + result = (!path.isEmpty()); + break; + case QxHttpServerDispatchSegment::_variable_and_type: + qAssert(!m_varName.isEmpty() && !m_varType.isEmpty()); + if ((m_varType == "int") || (m_varType == "long")) + { + qlonglong val = path.toLongLong(&result, 10); + Q_UNUSED(val); + } + if ((m_varType == "float") || (m_varType == "double")) + { + double val = path.toDouble(&result); + Q_UNUSED(val); + } + else if (m_varType == "string") + { + result = (!path.isEmpty()); + } + break; + case QxHttpServerDispatchSegment::_variable_and_regexp: + qAssert(!m_varName.isEmpty() && !m_varType.isEmpty()); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + result = ((!path.isEmpty()) && (m_regExp.match(path).hasMatch())); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + result = ((!path.isEmpty()) && (m_regExp.exactMatch(path))); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + break; + case QxHttpServerDispatchSegment::_wildcard: + result = true; + break; + default: + break; + } + return result; + } + + void QxHttpServerDispatchSegment::fillParam(const QString &path, QHash ¶meters) const + { + QVariant vParamValue; + bool bAddParam = true; + switch (m_type) + { + case QxHttpServerDispatchSegment::_variable: + vParamValue = QVariant(path); + break; + case QxHttpServerDispatchSegment::_variable_and_type: + vParamValue = QVariant(path); + break; + case QxHttpServerDispatchSegment::_variable_and_regexp: + vParamValue = QVariant(path); + break; + default: + bAddParam = false; + break; + } + if ((bAddParam) && (!m_varName.isEmpty())) + { + parameters.insert(m_varName, vParamValue); + } + } + + void QxHttpServer::buildResponseStaticFile(qx::QxHttpRequest &request, qx::QxHttpResponse &response, const QString &serverPath, qlonglong chunkedSize /* = 0 */) + { + // Check HTTP method GET + QByteArray requestPath = request.url().path().toLatin1(); + if (request.command() != "GET") + { + response.status() = 400; + response.data() = "Unknown command '" + request.command().toLatin1() + "' to get server static file '" + requestPath + "'"; + return; + } + + // Check root server path to deliver static files + QDir dirServerPath(serverPath); + if (!dirServerPath.exists()) + { + response.status() = 404; + response.data() = "Server path for static files not found : " + serverPath.toLatin1(); + return; + } + + // Check if requested static file exists on server + QString filePath = dirServerPath.filePath(requestPath.right(requestPath.size() - 1)); + if (!QFile::exists(filePath)) + { + response.status() = 404; + response.data() = "Server static file not found : " + requestPath; + return; + } + + // Check if requested static file is inside root server path (to avoid ../) + QDir dirFilePath(filePath); + QString canonicalServerPath = dirServerPath.canonicalPath(); + QString canonicalFilePath = dirFilePath.canonicalPath(); + if (!canonicalFilePath.startsWith(canonicalServerPath)) + { + response.status() = 403; + response.data() = "Cannot access to server static file : " + requestPath; + return; + } + + // Try to open static file in read-only mode + QFile file(filePath); + if (!file.open(QFile::ReadOnly)) + { + response.status() = 403; + response.data() = "Cannot open server static file : " + requestPath; + return; + } + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + QMimeDatabase mimeDatabase; + QMimeType mimeType = mimeDatabase.mimeTypeForFile(filePath); + response.headers().insert("Content-Type", mimeType.name().toLatin1()); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + + // Read file content + if (chunkedSize > 0) + { + while (!file.atEnd()) + { + if (!response.writeChunked(file.read(chunkedSize))) + { + return; + } + } + } + else + { + response.data() = file.readAll(); + } + file.close(); + } + + void QxHttpServer::buildResponseQxRestApi(qx::QxHttpRequest &request, qx::QxHttpResponse &response) + { + qx::QxRestApi api; + QString result = api.processRequest(request.data()); + response.headers().insert("Content-Type", "application/json; charset=utf-8"); + response.data() = result.toUtf8(); + } + +} // namespace qx + +#endif // _QX_ENABLE_QT_NETWORK diff --git a/src/QxHttpServer/QxHttpSession.cpp b/src/QxHttpServer/QxHttpSession.cpp new file mode 100644 index 0000000..9924955 --- /dev/null +++ b/src/QxHttpServer/QxHttpSession.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK + +#include + +#include + +#include + +#include + +namespace qx +{ + + struct Q_DECL_HIDDEN QxHttpSession::QxHttpSessionImpl + { + + QMutex m_mutex; //!< Mutex => 'QxHttpSession' is thread-safe + QHash m_storage; //!< Session storage based on key/values + QByteArray m_id; //!< Session unique identifier + QDateTime m_lastAccess; //!< Session last access date-time to check if expired + + QxHttpSessionImpl() + { + m_id = QUuid::createUuid().toString().toLatin1(); + m_lastAccess = QDateTime::currentDateTime(); + } + ~QxHttpSessionImpl() { ; } + }; + + QxHttpSession::QxHttpSession() : m_pImpl(new QxHttpSessionImpl()) { ; } + + QxHttpSession::~QxHttpSession() { ; } + + QByteArray QxHttpSession::id() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_id; + } + + QDateTime QxHttpSession::lastAccess() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_lastAccess; + } + + void QxHttpSession::lastAccess(const QDateTime &dt) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_lastAccess = dt; + } + + QVariant QxHttpSession::get(const QByteArray &key) + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_storage.value(key); + } + + void QxHttpSession::set(const QByteArray &key, const QVariant &value) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_storage.insert(key, value); + } + + QHash QxHttpSession::getAll() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_storage; + } + + void QxHttpSession::remove(const QByteArray &key) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_storage.remove(key); + } + + void QxHttpSession::clear() + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_storage.clear(); + } + +} // namespace qx + +#endif // _QX_ENABLE_QT_NETWORK diff --git a/src/QxHttpServer/QxHttpSessionManager.cpp b/src/QxHttpServer/QxHttpSessionManager.cpp new file mode 100644 index 0000000..e49ebd5 --- /dev/null +++ b/src/QxHttpServer/QxHttpSessionManager.cpp @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK + +#include + +#include +#include + +#include + +#include + +#include + +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::QxHttpSessionManager) + +namespace qx +{ + + struct Q_DECL_HIDDEN QxHttpSessionManager::QxHttpSessionManagerImpl + { + + QMutex m_mutex; //!< Mutex => 'QxHttpSessionManager' is thread-safe + QTimer m_timer; //!< Timer to remove automatically expired sessions + QHash m_sessions; //!< List of sessions + + QxHttpSessionManagerImpl() { ; } + ~QxHttpSessionManagerImpl() { ; } + + QByteArray getSessionId(qx::QxHttpRequest &request, qx::QxHttpResponse &response, const QByteArray &cookieName) + { + QByteArray id = response.cookie(cookieName).value; + if (id.isEmpty()) + { + id = request.cookie(cookieName).value; + } + if ((!id.isEmpty()) && (!m_sessions.contains(id))) + { + id = QByteArray(); + } + return id; + } + + qx::QxHttpSession_ptr getSession(qx::QxHttpRequest &request, qx::QxHttpResponse &response, const QByteArray &cookieName, bool autoCreateSession) + { + do + { + QMutexLocker locker(&m_mutex); + QByteArray id = getSessionId(request, response, cookieName); + if (id.isEmpty() || (!m_sessions.contains(id))) + { + break; + } + qx::QxHttpSession_ptr session = m_sessions.value(id); + if (session) + { + session->lastAccess(QDateTime::currentDateTime()); + } + return session; + } while (false); + if (autoCreateSession) + { + return createSession(request, response, cookieName); + } + return qx::QxHttpSession_ptr(); + } + + qx::QxHttpSession_ptr createSession(qx::QxHttpRequest &request, qx::QxHttpResponse &response, const QByteArray &cookieName) + { + removeSession(request, response, cookieName); + QMutexLocker locker(&m_mutex); + qx::QxHttpSession_ptr session(new qx::QxHttpSession(), (&QxHttpSessionManager::deleteSession)); + QByteArray id = session->id(); + m_sessions.insert(id, session); + + qx::QxHttpCookie cookie; + cookie.name = cookieName; + cookie.value = id; + response.cookies().insert(cookieName, cookie); + return session; + } + + void removeSession(qx::QxHttpRequest &request, qx::QxHttpResponse &response, const QByteArray &cookieName) + { + QMutexLocker locker(&m_mutex); + QByteArray id = getSessionId(request, response, cookieName); + if (id.isEmpty() || (!m_sessions.contains(id))) + { + return; + } + m_sessions.remove(id); + } + }; + + QxHttpSessionManager::QxHttpSessionManager() : QObject(), qx::QxSingleton("qx::QxHttpSessionManager"), m_pImpl(new QxHttpSessionManagerImpl()) + { + QObject::connect((&m_pImpl->m_timer), SIGNAL(timeout()), this, SLOT(onCheckSessionTimeOut())); + m_pImpl->m_timer.start(60000); + } + + QxHttpSessionManager::~QxHttpSessionManager() { m_pImpl->m_timer.stop(); } + + void QxHttpSessionManager::deleteSession(qx::QxHttpSession *p) { delete p; } + + qx::QxHttpSession_ptr QxHttpSessionManager::getSession(qx::QxHttpRequest &request, qx::QxHttpResponse &response, const QByteArray &cookieName /* = QByteArray("qx_session_id") */, bool autoCreateSession /* = true */) + { + return QxHttpSessionManager::getSingleton()->m_pImpl->getSession(request, response, cookieName, autoCreateSession); + } + + qx::QxHttpSession_ptr QxHttpSessionManager::createSession(qx::QxHttpRequest &request, qx::QxHttpResponse &response, const QByteArray &cookieName /* = QByteArray("qx_session_id") */) + { + return QxHttpSessionManager::getSingleton()->m_pImpl->createSession(request, response, cookieName); + } + + void QxHttpSessionManager::removeSession(qx::QxHttpRequest &request, qx::QxHttpResponse &response, const QByteArray &cookieName /* = QByteArray("qx_session_id") */) + { + QxHttpSessionManager::getSingleton()->m_pImpl->removeSession(request, response, cookieName); + } + + void QxHttpSessionManager::onCheckSessionTimeOut() + { + QMutexLocker locker(&m_pImpl->m_mutex); + qlonglong lTimeOut = qx::service::QxConnect::getSingleton()->getSessionTimeOut(); + QMutableHashIterator itr(m_pImpl->m_sessions); + QDateTime dt = QDateTime::currentDateTime(); + + while (itr.hasNext()) + { + itr.next(); + qx::QxHttpSession_ptr session = itr.value(); + if (!session) + { + itr.remove(); + continue; + } + QDateTime lastAccess = session->lastAccess(); + if (!lastAccess.isValid()) + { + qAssert(false); + itr.remove(); + continue; + } + if (lastAccess.addMSecs(lTimeOut) < dt) + { + itr.remove(); + continue; + } + } + } + +} // namespace qx + +#endif // _QX_ENABLE_QT_NETWORK diff --git a/src/QxHttpServer/QxHttpTransaction.cpp b/src/QxHttpServer/QxHttpTransaction.cpp new file mode 100644 index 0000000..a3cf074 --- /dev/null +++ b/src/QxHttpServer/QxHttpTransaction.cpp @@ -0,0 +1,510 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK + +#include + +#include + +#include + +#include + +#include + +namespace qx +{ + + namespace compress + { + QByteArray to_gzip(const QByteArray &data); + quint32 crc32(const QByteArray &data); + } // namespace compress + + struct Q_DECL_HIDDEN QxHttpTransaction::QxHttpTransactionImpl + { + + qx::QxHttpRequest m_request; //!< HTTP transaction request + qx::QxHttpResponse m_response; //!< HTTP transaction response + QTcpSocket *m_socket; //!< HTTP transaction socket + bool m_headersWritten; //!< HTTP response headers already written (used to write chunked data) + bool m_chunkAllowed; //!< If we receive a HTTP 1.0 request, then chunked responses are not supported by client + + QxHttpTransactionImpl(QxHttpTransaction *parent) : m_request(parent), m_response(parent), m_socket(NULL), m_headersWritten(false), m_chunkAllowed(true) { qAssert(parent != NULL); } + ~QxHttpTransactionImpl() { ; } + + bool waitForReadSocket(QTcpSocket &socket) + { + if (socket.bytesAvailable() > 0) + { + return true; + } + long lMaxWait = qx::service::QxConnect::getSingleton()->getMaxWait(); + long lCurrRetry = 0; + do + { + if (socket.waitForReadyRead(1)) + { + return true; + } + if (socket.bytesAvailable() > 0) + { + return true; + } + QCoreApplication::processEvents(); + lCurrRetry++; + } while ((lMaxWait == -1) || (lCurrRetry < lMaxWait)); + return false; + } + + bool writeToSocket(QTcpSocket &socket, const QByteArray &data) + { + if (data.isEmpty()) + { + return true; + } + const char *pData = data.constData(); + qint64 iTotalWritten = 0; + qint64 iTotalToWrite = static_cast(data.count()); + while (iTotalWritten < iTotalToWrite) + { + qint64 iWritten = socket.write((pData + iTotalWritten), (iTotalToWrite - iTotalWritten)); + if (iWritten == -1) + { + break; + } + iTotalWritten += iWritten; + } + return (iTotalWritten == iTotalToWrite); + } + + qx_bool writeHeaders(QTcpSocket &socket) + { + // Check if headers/cookies has already been written + if (m_headersWritten) + { + return qx_bool(true); + } + m_headersWritten = true; + + // HTTP response first line + QByteArray line = "HTTP/1.1 " + QByteArray::number(m_response.status()) + " " + m_response.statusDesc() + "\r\n"; + if (!writeToSocket(socket, line)) + { + return qx_bool(500, "Internal server error : cannot write to socket HTTP response first line '" + line + "' (" + socket.errorString() + ")"); + } + + // HTTP response headers + QHashIterator itrHeaders(m_response.headers()); + while (itrHeaders.hasNext()) + { + itrHeaders.next(); + if (itrHeaders.key().trimmed().isEmpty()) + { + continue; + } + line = itrHeaders.key() + ": " + itrHeaders.value() + "\r\n"; + if (!writeToSocket(socket, line)) + { + return qx_bool(500, "Internal server error : cannot write to socket HTTP response header '" + line + "' (" + socket.errorString() + ")"); + } + } + + // HTTP response cookies + QHashIterator itrCookies(m_response.cookies()); + while (itrCookies.hasNext()) + { + itrCookies.next(); + if (itrCookies.value().name.trimmed().isEmpty()) + { + continue; + } + line = "Set-Cookie: " + itrCookies.value().toString() + "\r\n"; + if (!writeToSocket(socket, line)) + { + return qx_bool(500, "Internal server error : cannot write to socket HTTP response cookie '" + line + "' (" + socket.errorString() + ")"); + } + } + + // Empty line : means end of headers/cookies + if (!writeToSocket(socket, "\r\n")) + { + return qx_bool(500, "Internal server error : cannot write to socket HTTP response header end line (" + socket.errorString() + ")"); + } + return qx_bool(true); + } + }; + + QxHttpTransaction::QxHttpTransaction() : qx::service::QxTransaction(), m_pImpl(new QxHttpTransactionImpl(this)) { ; } + + QxHttpTransaction::~QxHttpTransaction() { ; } + + void QxHttpTransaction::clear() + { + qx::service::QxTransaction::clear(); + m_pImpl.reset(new QxHttpTransactionImpl(this)); + } + + qx::QxHttpRequest &QxHttpTransaction::request() { return m_pImpl->m_request; } + + qx::QxHttpResponse &QxHttpTransaction::response() { return m_pImpl->m_response; } + + void QxHttpTransaction::executeServer() + { + if (getMessageReturn()) + { + Q_EMIT onCustomRequestHandler(); + } + } + + qx_bool QxHttpTransaction::writeSocketServer(QTcpSocket &socket) + { + // Check HTTP transaction message return + qx_bool bTransactionMsg = getMessageReturn(); + if (!bTransactionMsg) + { + m_pImpl->m_response.headers().insert("Content-Type", "text/plain; charset=utf-8"); + m_pImpl->m_response.status() = static_cast(bTransactionMsg.getCode()); + m_pImpl->m_response.data() = bTransactionMsg.getDesc().toUtf8(); + } + + // Check if we can compress response data + bool chunked = (m_pImpl->m_response.isChunked() && m_pImpl->m_chunkAllowed); + bool compress = qx::service::QxConnect::getSingleton()->getCompressData(); + QString contentType = m_pImpl->m_response.header("Content-Type").toLower(); + compress = (compress && (contentType.startsWith("text/") || contentType.startsWith("application/json") || contentType.startsWith("application/javascript"))); + compress = (compress && (m_pImpl->m_request.header("Accept-Encoding").toLower().contains("gzip"))); + compress = (compress && (m_pImpl->m_response.data().size() > 99)); + compress = (compress && (m_pImpl->m_response.status() == 200)); + compress = (compress && (!chunked)); + + // Compress response data + if (compress) + { + m_pImpl->m_response.headers().insert("Content-Encoding", "gzip"); + m_pImpl->m_response.data() = qx::compress::to_gzip(m_pImpl->m_response.data()); + } + + // Insert 'Content-Length' header + QByteArray &body = m_pImpl->m_response.data(); + QByteArray contentLength = QByteArray::number(body.count()); + if (!chunked) + { + m_pImpl->m_response.headers().insert("Content-Length", contentLength); + } + + // Check if we have to force to close connection + if (m_pImpl->m_request.header("Connection").toLower() == "close") + { + setForceConnectionStatus(qx::service::QxTransaction::conn_close); + } + + // HTTP response headers and cookies + qx_bool bWriteHeaders = m_pImpl->writeHeaders(socket); + if (!bWriteHeaders) + { + return bWriteHeaders; + } + + // HTTP response body content + if (chunked) + { + if (!m_pImpl->writeToSocket(socket, "0\r\n\r\n")) + { + return qx_bool(500, "Internal server error : cannot write to socket HTTP last empty chunked data (" + socket.errorString() + ")"); + } + } + else if (!m_pImpl->writeToSocket(socket, body)) + { + return qx_bool(500, "Internal server error : cannot write to socket HTTP response body content of " + QString::number(body.count()) + " bytes (" + socket.errorString() + ")"); + } + return qx_bool(true); + } + + qx_bool QxHttpTransaction::readSocketServer(QTcpSocket &socket) + { + // Fetch source client details + setIpSource(socket.peerAddress().toString()); + setPortSource(static_cast(socket.peerPort())); + m_pImpl->m_request.sourceAddress() = socket.peerAddress().toString(); + m_pImpl->m_request.sourcePort() = static_cast(socket.peerPort()); + m_pImpl->m_socket = (&socket); + + // HTTP request first line + setMessageReturn(qx_bool(true)); + if (!m_pImpl->waitForReadSocket(socket)) + { + setMessageReturn(qx_bool(500, "Internal server error : cannot read socket to parse HTTP request first line (" + socket.errorString() + ")")); + return qx_bool(true); + } + QByteArray line = socket.readLine().trimmed(); + QStringList lst = QString::fromUtf8(line).split(" "); + if (lst.count() < 3) + { + QString errMsg = ("Bad request : invalid HTTP request first line : " + line); + setMessageReturn(qx_bool(400, errMsg)); + return qx_bool(true); + } + if (!lst.at(2).contains("HTTP")) + { + QString errMsg = ("Bad request : invalid HTTP request first line, third parameter must contain 'HTTP' : " + line); + setMessageReturn(qx_bool(400, errMsg)); + return qx_bool(true); + } + m_pImpl->m_request.command() = lst.at(0).toUpper(); + m_pImpl->m_request.url() = QUrl(lst.at(1)); + m_pImpl->m_request.version() = lst.at(2); + + // HTTP request headers and cookies + int iContentLength = 0; + do + { + if (!m_pImpl->waitForReadSocket(socket)) + { + setMessageReturn(qx_bool(500, "Internal server error : cannot read socket to parse HTTP headers (" + socket.errorString() + ")")); + return qx_bool(true); + } + line = socket.readLine().trimmed(); + if (line.isEmpty()) + { + break; + } + int pos = line.indexOf(':'); + if (pos <= 0) + { + QString errMsg = ("Bad request : invalid HTTP header : " + line); + setMessageReturn(qx_bool(400, errMsg)); + return qx_bool(true); + } + QByteArray key = line.left(pos).trimmed(); + QByteArray value = line.mid(pos + 1).trimmed(); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + if (key.toLower() == "cookie") + { + m_pImpl->m_request.cookies().insert(QxHttpCookie::parse(value)); + } +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + if (key.toLower() == "cookie") + { + m_pImpl->m_request.cookies().unite(QxHttpCookie::parse(value)); + } +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + else + { + m_pImpl->m_request.headers().insert(key, value); + } + if (key.toLower() == "content-length") + { + iContentLength = value.toInt(); + } + } while (1); + + // Check HTTP 1.0 compatibility + if (m_pImpl->m_request.version().contains("1.0")) + { + m_pImpl->m_chunkAllowed = false; + if (m_pImpl->m_request.header("Connection").toLower() != "keep-alive") + { + setForceConnectionStatus(qx::service::QxTransaction::conn_close); + } + } + + QByteArray body; + if (iContentLength > 0) + { + // HTTP request body content + body.reserve(iContentLength); + do + { + if (!m_pImpl->waitForReadSocket(socket)) + { + setMessageReturn(qx_bool(500, "Internal server error : cannot read socket to get HTTP body content (" + socket.errorString() + ")")); + return qx_bool(true); + } + body += socket.readAll(); + } while (body.count() < iContentLength); + m_pImpl->m_request.data() = body; + } + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + QByteArray params = m_pImpl->m_request.url().query(QUrl::FullyEncoded).toLatin1(); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + QByteArray params = m_pImpl->m_request.url().encodedQuery(); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + + // HTTP request parameters (from URL and from body if content-type is 'application/x-www-form-urlencoded', which means web-form submit) + if ((!body.isEmpty()) && (m_pImpl->m_request.header("content-type").toLower() == "application/x-www-form-urlencoded")) + { + params += (params.isEmpty() ? QByteArray() : QByteArray("&")) + body; + } + QList listParams = params.split('&'); + Q_FOREACH (QByteArray param, listParams) + { + int pos = param.indexOf('='); + if (pos > 0) + { + QByteArray key = param.left(pos).trimmed(); + QByteArray value = param.mid(pos + 1).trimmed(); + m_pImpl->m_request.params().insert(QUrl::fromPercentEncoding(key), QUrl::fromPercentEncoding(value)); + } + else if (!param.isEmpty()) + { + m_pImpl->m_request.params().insert(QUrl::fromPercentEncoding(param), ""); + } + } + + return qx_bool(true); + } + + qx_bool QxHttpTransaction::writeChunked(const QByteArray &data) + { + // Check input data + if (data.isEmpty()) + { + return qx_bool(true); + } + if (!m_pImpl->m_socket) + { + return qx_bool(false, "No socket to write HTTP chunked data"); + } + if (!m_pImpl->m_chunkAllowed) + { + m_pImpl->m_response.data() += data; + return qx_bool(true); + } + + // HTTP response headers and cookies + if (!m_pImpl->m_headersWritten) + { + m_pImpl->m_response.headers().insert("Transfer-Encoding", "chunked"); + qx_bool bWriteHeaders = m_pImpl->writeHeaders(*m_pImpl->m_socket); + if (!bWriteHeaders) + { + return bWriteHeaders; + } + } + + // Write chunked data + QByteArray chunked = (QByteArray::number(data.count(), 16) + "\r\n" + data + "\r\n"); + if (!m_pImpl->writeToSocket((*m_pImpl->m_socket), chunked)) + { + return qx_bool(500, "Internal server error : cannot write to socket HTTP chunked data of " + QString::number(data.count()) + " bytes (" + m_pImpl->m_socket->errorString() + ")"); + } + return qx_bool(true); + } + + namespace compress + { + + // Code from : https://stackoverflow.com/questions/20734831/compress-string-with-gzip-using-qcompress + QByteArray to_gzip(const QByteArray &data) + { + QByteArray compressedData = qCompress(data); + compressedData.remove(0, 6); + compressedData.chop(4); + + QByteArray header; + QDataStream ds1(&header, QIODevice::WriteOnly); + ds1 << quint16(0x1f8b) << quint16(0x0800) << quint16(0x0000) << quint16(0x0000) << quint16(0x000b); + + QByteArray footer; + QDataStream ds2(&footer, QIODevice::WriteOnly); + ds2.setByteOrder(QDataStream::LittleEndian); + ds2 << qx::compress::crc32(data) << quint32(data.size()); + + return (header + compressedData + footer); + } + + static const quint32 crc32_tab[] = {/* CRC polynomial 0xedb88320 */ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}; + + quint32 update_crc32(unsigned char ch, quint32 crc) + { + return (crc32_tab[((crc) ^ ((quint8)ch)) & 0xff] ^ ((crc) >> 8)); + } + + quint32 crc32(const QByteArray &data) + { + quint32 crc = 0xFFFFFFFFL; + for (int i = 0; i < data.size(); i++) + { + crc = update_crc32(data[i], crc); + } + return ~crc; + } + + } // namespace compress +} // namespace qx + +#endif // _QX_ENABLE_QT_NETWORK diff --git a/src/QxMemLeak/bool_array.cpp b/src/QxMemLeak/bool_array.cpp new file mode 100644 index 0000000..b1120b5 --- /dev/null +++ b/src/QxMemLeak/bool_array.cpp @@ -0,0 +1,228 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2008 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/** + * @file bool_array.cpp + * + * Code for class bool_array (packed boolean array). + * + * @version 3.1, 2005/08/25 + * @author Wu Yongwei + * + */ + +#include + +#ifndef QT_NO_DEBUG +#ifndef _QX_MODE_RELEASE +#if _QX_USE_MEM_LEAK_DETECTION + +#include +#include + +#include + +namespace qx { +namespace memory { + +BYTE bool_array::_S_bit_count[256] = +{ + 0, /* 0 */ 1, /* 1 */ 1, /* 2 */ 2, /* 3 */ 1, /* 4 */ + 2, /* 5 */ 2, /* 6 */ 3, /* 7 */ 1, /* 8 */ 2, /* 9 */ + 2, /* 10 */ 3, /* 11 */ 2, /* 12 */ 3, /* 13 */ 3, /* 14 */ + 4, /* 15 */ 1, /* 16 */ 2, /* 17 */ 2, /* 18 */ 3, /* 19 */ + 2, /* 20 */ 3, /* 21 */ 3, /* 22 */ 4, /* 23 */ 2, /* 24 */ + 3, /* 25 */ 3, /* 26 */ 4, /* 27 */ 3, /* 28 */ 4, /* 29 */ + 4, /* 30 */ 5, /* 31 */ 1, /* 32 */ 2, /* 33 */ 2, /* 34 */ + 3, /* 35 */ 2, /* 36 */ 3, /* 37 */ 3, /* 38 */ 4, /* 39 */ + 2, /* 40 */ 3, /* 41 */ 3, /* 42 */ 4, /* 43 */ 3, /* 44 */ + 4, /* 45 */ 4, /* 46 */ 5, /* 47 */ 2, /* 48 */ 3, /* 49 */ + 3, /* 50 */ 4, /* 51 */ 3, /* 52 */ 4, /* 53 */ 4, /* 54 */ + 5, /* 55 */ 3, /* 56 */ 4, /* 57 */ 4, /* 58 */ 5, /* 59 */ + 4, /* 60 */ 5, /* 61 */ 5, /* 62 */ 6, /* 63 */ 1, /* 64 */ + 2, /* 65 */ 2, /* 66 */ 3, /* 67 */ 2, /* 68 */ 3, /* 69 */ + 3, /* 70 */ 4, /* 71 */ 2, /* 72 */ 3, /* 73 */ 3, /* 74 */ + 4, /* 75 */ 3, /* 76 */ 4, /* 77 */ 4, /* 78 */ 5, /* 79 */ + 2, /* 80 */ 3, /* 81 */ 3, /* 82 */ 4, /* 83 */ 3, /* 84 */ + 4, /* 85 */ 4, /* 86 */ 5, /* 87 */ 3, /* 88 */ 4, /* 89 */ + 4, /* 90 */ 5, /* 91 */ 4, /* 92 */ 5, /* 93 */ 5, /* 94 */ + 6, /* 95 */ 2, /* 96 */ 3, /* 97 */ 3, /* 98 */ 4, /* 99 */ + 3, /* 100 */ 4, /* 101 */ 4, /* 102 */ 5, /* 103 */ 3, /* 104 */ + 4, /* 105 */ 4, /* 106 */ 5, /* 107 */ 4, /* 108 */ 5, /* 109 */ + 5, /* 110 */ 6, /* 111 */ 3, /* 112 */ 4, /* 113 */ 4, /* 114 */ + 5, /* 115 */ 4, /* 116 */ 5, /* 117 */ 5, /* 118 */ 6, /* 119 */ + 4, /* 120 */ 5, /* 121 */ 5, /* 122 */ 6, /* 123 */ 5, /* 124 */ + 6, /* 125 */ 6, /* 126 */ 7, /* 127 */ 1, /* 128 */ 2, /* 129 */ + 2, /* 130 */ 3, /* 131 */ 2, /* 132 */ 3, /* 133 */ 3, /* 134 */ + 4, /* 135 */ 2, /* 136 */ 3, /* 137 */ 3, /* 138 */ 4, /* 139 */ + 3, /* 140 */ 4, /* 141 */ 4, /* 142 */ 5, /* 143 */ 2, /* 144 */ + 3, /* 145 */ 3, /* 146 */ 4, /* 147 */ 3, /* 148 */ 4, /* 149 */ + 4, /* 150 */ 5, /* 151 */ 3, /* 152 */ 4, /* 153 */ 4, /* 154 */ + 5, /* 155 */ 4, /* 156 */ 5, /* 157 */ 5, /* 158 */ 6, /* 159 */ + 2, /* 160 */ 3, /* 161 */ 3, /* 162 */ 4, /* 163 */ 3, /* 164 */ + 4, /* 165 */ 4, /* 166 */ 5, /* 167 */ 3, /* 168 */ 4, /* 169 */ + 4, /* 170 */ 5, /* 171 */ 4, /* 172 */ 5, /* 173 */ 5, /* 174 */ + 6, /* 175 */ 3, /* 176 */ 4, /* 177 */ 4, /* 178 */ 5, /* 179 */ + 4, /* 180 */ 5, /* 181 */ 5, /* 182 */ 6, /* 183 */ 4, /* 184 */ + 5, /* 185 */ 5, /* 186 */ 6, /* 187 */ 5, /* 188 */ 6, /* 189 */ + 6, /* 190 */ 7, /* 191 */ 2, /* 192 */ 3, /* 193 */ 3, /* 194 */ + 4, /* 195 */ 3, /* 196 */ 4, /* 197 */ 4, /* 198 */ 5, /* 199 */ + 3, /* 200 */ 4, /* 201 */ 4, /* 202 */ 5, /* 203 */ 4, /* 204 */ + 5, /* 205 */ 5, /* 206 */ 6, /* 207 */ 3, /* 208 */ 4, /* 209 */ + 4, /* 210 */ 5, /* 211 */ 4, /* 212 */ 5, /* 213 */ 5, /* 214 */ + 6, /* 215 */ 4, /* 216 */ 5, /* 217 */ 5, /* 218 */ 6, /* 219 */ + 5, /* 220 */ 6, /* 221 */ 6, /* 222 */ 7, /* 223 */ 3, /* 224 */ + 4, /* 225 */ 4, /* 226 */ 5, /* 227 */ 4, /* 228 */ 5, /* 229 */ + 5, /* 230 */ 6, /* 231 */ 4, /* 232 */ 5, /* 233 */ 5, /* 234 */ + 6, /* 235 */ 5, /* 236 */ 6, /* 237 */ 6, /* 238 */ 7, /* 239 */ + 4, /* 240 */ 5, /* 241 */ 5, /* 242 */ 6, /* 243 */ 5, /* 244 */ + 6, /* 245 */ 6, /* 246 */ 7, /* 247 */ 5, /* 248 */ 6, /* 249 */ + 6, /* 250 */ 7, /* 251 */ 6, /* 252 */ 7, /* 253 */ 7, /* 254 */ + 8 /* 255 */ +}; // End _S_bit_count + +/** + * Creates the packed boolean array with a specific size. + * + * @param __size size of the array + * @return \c false if \a __size equals \c 0 or is too big, or + * if memory is insufficient; \c true if \a __size has + * a suitable value and memory allocation is + * successful. + */ +bool bool_array::create(unsigned long __size) +{ + if (__size == 0) + return false; + // Will be optimized away by a decent compiler if ULONG_MAX == UINT_MAX + if (ULONG_MAX > UINT_MAX && ((__size - 1) / 8 + 1) > UINT_MAX) + return false; + + size_t __byte_cnt = (size_t)((__size - 1) / 8 + 1); + if (_M_byte_ptr) + free(_M_byte_ptr); + _M_length = 0; + + // Uses malloc/free instead of new/delete to avoid exception handling + // differences between compilers + if (!(_M_byte_ptr = (BYTE*)malloc(__byte_cnt))) + return false; + + _M_length = __size; + return true; +} + +/** + * Initializes all array elements to a specific value optimally. + * + * @param ___value the boolean value to assign to all elements + */ +void bool_array::initialize(bool ___value) +{ + assert(_M_byte_ptr); + size_t __byte_cnt = (size_t)((_M_length - 1) / 8) + 1; + memset(_M_byte_ptr, ___value ? ~0 : 0, __byte_cnt); + if (___value) + { + int __valid_bits_in_last_byte = (_M_length - 1) % 8 + 1; + _M_byte_ptr[__byte_cnt - 1] &= ~(~0 << __valid_bits_in_last_byte); + } +} + +/** + * Counts elements with a \c true value. + * + * @return the count of \c true elements + */ +unsigned long bool_array::count() const +{ + assert(_M_byte_ptr); + unsigned long __true_cnt = 0; + size_t __byte_cnt = (size_t)((_M_length - 1) / 8) + 1; + for (size_t __i = 0; __i < __byte_cnt; ++__i) + __true_cnt += _S_bit_count[_M_byte_ptr[__i]]; + return __true_cnt; +} + +/** + * Counts elements with a \c true value in a specified range. + * + * @param __beg beginning of the range + * @param __end end of the range (exclusive) + * @return the count of \c true elements + */ +unsigned long bool_array::count(unsigned long __beg, unsigned long __end) const +{ + assert(_M_byte_ptr); + unsigned long __true_cnt = 0; + size_t __byte_idx_beg, __byte_idx_end; + BYTE __byte_val; + + if (__beg >= __end) + return 0; + if (__end > _M_length) + throw std::out_of_range("invalid bool_array subscript"); + --__end; + + __byte_idx_beg = (size_t)(__beg / 8); + __byte_val = _M_byte_ptr[__byte_idx_beg]; + __byte_val &= ~0 << (__beg % 8); + + __byte_idx_end = (size_t)(__end / 8); + if (__byte_idx_beg < __byte_idx_end) + { + __true_cnt = _S_bit_count[__byte_val]; + __byte_val = _M_byte_ptr[__byte_idx_end]; + } + __byte_val &= ~(~0 << (__end % 8 + 1)); + __true_cnt += _S_bit_count[__byte_val]; + + for (++__byte_idx_beg; __byte_idx_beg < __byte_idx_end; ++__byte_idx_beg) + __true_cnt += _S_bit_count[_M_byte_ptr[__byte_idx_beg]]; + return __true_cnt; +} + +/** + * Changes all \c true elements to \c false, and \c false ones to \c true. + */ +void bool_array::flip() +{ + assert(_M_byte_ptr); + size_t __byte_cnt = (size_t)((_M_length - 1) / 8) + 1; + for (size_t __i = 0; __i < __byte_cnt; ++__i) + _M_byte_ptr[__i] = ~_M_byte_ptr[__i]; + int __valid_bits_in_last_byte = (_M_length - 1) % 8 + 1; + _M_byte_ptr[__byte_cnt - 1] &= ~(~0 << __valid_bits_in_last_byte); +} + +} // namespace memory +} // namespace qx + +#endif // _QX_USE_MEM_LEAK_DETECTION +#endif // _QX_MODE_RELEASE +#endif // QT_NO_DEBUG diff --git a/src/QxMemLeak/debug_new.cpp b/src/QxMemLeak/debug_new.cpp new file mode 100644 index 0000000..295343f --- /dev/null +++ b/src/QxMemLeak/debug_new.cpp @@ -0,0 +1,618 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2008 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/** + * @file debug_new.cpp + * + * Implementation of debug versions of new and delete to check leakage. + * + * @version 4.14, 2008/10/20 + * @author Wu Yongwei + * + */ + +#include +#include + +#ifndef QT_NO_DEBUG +#ifndef _QX_MODE_RELEASE +#if _QX_USE_MEM_LEAK_DETECTION + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __unix__ +#include +#endif + +#ifdef _WIN32 +#include +#include +#endif + +#include + +#include +#include + +#ifndef _QX_MEM_LEAK_ONLY_KNOWN_SRC_FILE +#define _QX_MEM_LEAK_ONLY_KNOWN_SRC_FILE 0 +#endif // _QX_MEM_LEAK_ONLY_KNOWN_SRC_FILE + +#if !_FAST_MUTEX_CHECK_INITIALIZATION && !defined(_NOTHREADS) +#error "_FAST_MUTEX_CHECK_INITIALIZATION not set: check_leaks may not work" +#endif + +/** + * @def _DEBUG_NEW_ALIGNMENT + * + * The alignment requirement of allocated memory blocks. It must be a + * power of two. + */ +#ifndef _DEBUG_NEW_ALIGNMENT +#define _DEBUG_NEW_ALIGNMENT 16 +#endif + +/** + * @def _DEBUG_NEW_ERROR_ACTION + * + * The action to take when an error occurs. The default behaviour is to + * call \e abort, unless \c _DEBUG_NEW_ERROR_CRASH is defined, in which + * case a segmentation fault will be triggered instead (which can be + * useful on platforms like Windows that do not generate a core dump + * when \e abort is called). + */ +#ifndef _DEBUG_NEW_ERROR_ACTION +#ifndef _DEBUG_NEW_ERROR_CRASH +#define _DEBUG_NEW_ERROR_ACTION abort() +#else +#define _DEBUG_NEW_ERROR_ACTION do { *((char*)0) = 0; abort(); } while (0) +#endif +#endif + +/** + * @def _DEBUG_NEW_FILENAME_LEN + * + * The length of file name stored if greater than zero. If it is zero, + * only a const char pointer will be stored. Currently the default + * behaviour is to copy the file name, because I found that the exit + * leakage check cannot access the address of the file name sometimes + * (in my case, a core dump will occur when trying to access the file + * name in a shared library after a \c SIGINT). The current default + * value makes the size of new_ptr_list_t 64 on 32-bit platforms. + */ +#ifndef _DEBUG_NEW_FILENAME_LEN +#define _DEBUG_NEW_FILENAME_LEN 44 +#endif + +/** + * @def _DEBUG_NEW_PROGNAME + * + * The program (executable) name to be set at compile time. It is + * better to assign the full program path to #new_progname in \e main + * (at run time) than to use this (compile-time) macro, but this macro + * serves well as a quick hack. Note also that double quotation marks + * need to be used around the program name, i.e., one should specify a + * command-line option like -D_DEBUG_NEW_PROGNAME=\"a.out\" + * in \e bash, or -D_DEBUG_NEW_PROGNAME=\"a.exe\" in the + * Windows command prompt. + */ +#ifndef _DEBUG_NEW_PROGNAME +#define _DEBUG_NEW_PROGNAME NULL +#endif + +/** + * @def _DEBUG_NEW_STD_OPER_NEW + * + * Macro to indicate whether the standard-conformant behaviour of + * operator new is wanted. It is on by default now, but + * the user may set it to \c 0 to revert to the old behaviour. + */ +#ifndef _DEBUG_NEW_STD_OPER_NEW +#define _DEBUG_NEW_STD_OPER_NEW 1 +#endif + +/** + * @def _DEBUG_NEW_TAILCHECK + * + * Macro to indicate whether a writing-past-end check will be performed. + * Define it to a positive integer as the number of padding bytes at the + * end of a memory block for checking. + */ +#ifndef _DEBUG_NEW_TAILCHECK +#define _DEBUG_NEW_TAILCHECK 0 +#endif + +/** + * @def _DEBUG_NEW_TAILCHECK_CHAR + * + * Value of the padding bytes at the end of a memory block. + */ +#ifndef _DEBUG_NEW_TAILCHECK_CHAR +#define _DEBUG_NEW_TAILCHECK_CHAR 0xCC +#endif + +/** + * @def _DEBUG_NEW_USE_ADDR2LINE + * + * Whether to use \e addr2line to convert a caller address to file/line + * information. Defining it to a non-zero value will enable the + * conversion (automatically done if GCC is detected). Defining it to + * zero will disable the conversion. + */ +#ifndef _DEBUG_NEW_USE_ADDR2LINE +#ifdef __GNUC__ +#define _DEBUG_NEW_USE_ADDR2LINE 1 +#else +#define _DEBUG_NEW_USE_ADDR2LINE 0 +#endif +#endif + +#ifdef _MSC_VER +#pragma warning(disable: 4073) // #pragma init_seg(lib) used +#pragma warning(disable: 4290) // C++ exception specification ignored +#pragma init_seg(lib) +#endif + +#undef _DEBUG_NEW_EMULATE_MALLOC +#undef _DEBUG_NEW_REDEFINE_NEW +/** + * Macro to indicate whether redefinition of \c new is wanted. Here it + * is defined to \c 0 to disable the redefinition of \c new. + */ +#define _DEBUG_NEW_REDEFINE_NEW 0 +#include + +/** + * Gets the aligned value of memory block size. + */ +#define align(s) \ + (((s) + _DEBUG_NEW_ALIGNMENT - 1) & ~(_DEBUG_NEW_ALIGNMENT - 1)) + +namespace qx { +namespace memory { + +/** + * Structure to store the position information where \c new occurs. + */ +struct new_ptr_list_t +{ + new_ptr_list_t* next; + new_ptr_list_t* prev; + size_t size; + union + { +#if _DEBUG_NEW_FILENAME_LEN == 0 + const char* file; +#else + char file[_DEBUG_NEW_FILENAME_LEN]; +#endif + void* addr; + }; + unsigned line :31; + unsigned is_array :1; + unsigned magic; +}; + +/** + * Magic number for error detection. + */ +const unsigned MAGIC = 0x4442474E; + +/** + * The extra memory allocated by operator new. + */ +const int ALIGNED_LIST_ITEM_SIZE = align(sizeof(new_ptr_list_t)); + +/** + * List of all new'd pointers. + */ +static new_ptr_list_t new_ptr_list = { + &new_ptr_list, + &new_ptr_list, + 0, + { +#if _DEBUG_NEW_FILENAME_LEN == 0 + NULL +#else + "" +#endif + }, + 0, + 0, + MAGIC +}; + +/** + * The mutex guard to protect simultaneous access to the pointer list. + */ +static fast_mutex new_ptr_lock; + +/** + * The mutex guard to protect simultaneous output to #new_output_fp. + */ +static fast_mutex new_output_lock; + +/** + * Total memory allocated in bytes. + */ +static size_t total_mem_alloc = 0; + +/** + * Flag to control whether #check_leaks will be automatically called on + * program exit. + */ +bool new_autocheck_flag = true; + +/** + * Flag to control whether verbose messages are output. + */ +bool new_verbose_flag = false; + +/** + * Pointer to the output stream. The default output is \e stderr, and + * one may change it to a user stream if needed (say, #new_verbose_flag + * is \c true and there are a lot of (de)allocations). + */ +FILE* new_output_fp = stderr; + +/** + * Pointer to the program name. Its initial value is the macro + * #_DEBUG_NEW_PROGNAME. You should try to assign the program path to + * it early in your application. Assigning argv[0] to it + * in \e main is one way. If you use \e bash or \e ksh (or similar), + * the following statement is probably what you want: + * `new_progname = getenv("_");'. + */ +const char* new_progname = _DEBUG_NEW_PROGNAME; + +#if _DEBUG_NEW_TAILCHECK +/** + * Checks whether the padding bytes at the end of a memory block is + * tampered with. + * + * @param ptr pointer to a new_ptr_list_t struct + * @return \c true if the padding bytes are untouched; \c false + * otherwise + */ +static bool check_tail(new_ptr_list_t* ptr) +{ + const unsigned char* const pointer = (unsigned char*)ptr + + ALIGNED_LIST_ITEM_SIZE + ptr->size; + for (int i = 0; i < _DEBUG_NEW_TAILCHECK; ++i) + if (pointer[i] != _DEBUG_NEW_TAILCHECK_CHAR) + return false; + return true; +} +#endif + +/** + * Allocates memory and initializes control data. + * + * @param size size of the required memory block + * @param file null-terminated string of the file name + * @param line line number + * @param is_array boolean value whether this is an array operation + * @return pointer to the user-requested memory area; \c NULL + * if memory allocation is not successful + */ +static void* alloc_mem(size_t size, const char* file, int line, bool is_array) +{ + assert(line >= 0); + STATIC_ASSERT((_DEBUG_NEW_ALIGNMENT & (_DEBUG_NEW_ALIGNMENT - 1)) == 0, Alignment_must_be_power_of_two); + STATIC_ASSERT(_DEBUG_NEW_TAILCHECK >= 0, Invalid_tail_check_length); + size_t s = size + ALIGNED_LIST_ITEM_SIZE + _DEBUG_NEW_TAILCHECK; + new_ptr_list_t* ptr = (new_ptr_list_t*)malloc(s); + if (ptr == NULL) + { +#if _DEBUG_NEW_STD_OPER_NEW + return NULL; +#else + fast_mutex_autolock lock(new_output_lock); + qDebug("[QxOrm] Out of memory when allocating %u bytes", size); + fflush(new_output_fp); + _DEBUG_NEW_ERROR_ACTION; +#endif + } + void* pointer = (char*)ptr + ALIGNED_LIST_ITEM_SIZE; +#if _DEBUG_NEW_FILENAME_LEN == 0 + ptr->file = file; +#else + if (line) + { + QX_STRNCPY(ptr->file, file, _DEBUG_NEW_FILENAME_LEN - 1); + ptr->file[_DEBUG_NEW_FILENAME_LEN - 1] = '\0'; + } + else + ptr->addr = (void*)file; +#endif + ptr->line = line; + ptr->is_array = is_array; + ptr->size = size; + ptr->magic = MAGIC; + { + fast_mutex_autolock lock(new_ptr_lock); + ptr->prev = new_ptr_list.prev; + ptr->next = &new_ptr_list; + new_ptr_list.prev->next = ptr; + new_ptr_list.prev = ptr; + } +#if _DEBUG_NEW_TAILCHECK + memset((char*)pointer + size, _DEBUG_NEW_TAILCHECK_CHAR, + _DEBUG_NEW_TAILCHECK); +#endif + if (new_verbose_flag) + { + fast_mutex_autolock lock(new_output_lock); + const char * msg_out_file = "[QxOrm] new%s: allocated %p (size %u, %s:%d)"; Q_UNUSED(msg_out_file); + const char * msg_out_no_file = "[QxOrm] new%s: allocated %p (size %u, )"; Q_UNUSED(msg_out_no_file); + if (line != 0) { qDebug(msg_out_file, (is_array ? "[]" : ""), pointer, size, ptr->file, ptr->line); } +#if (! _QX_MEM_LEAK_ONLY_KNOWN_SRC_FILE) + else { qDebug(msg_out_no_file, (is_array ? "[]" : ""), pointer, size); } +#endif // (! _QX_MEM_LEAK_ONLY_KNOWN_SRC_FILE) + } + total_mem_alloc += size; + return pointer; +} + +/** + * Frees memory and adjusts pointers. + * + * @param pointer pointer to delete + * @param addr pointer to the caller + * @param is_array flag indicating whether it is invoked by a + * delete[] call + */ +void __debug_new_recorder::free_pointer(void* pointer, void* addr, bool is_array) +{ + if (pointer == NULL) { return; } + new_ptr_list_t* ptr = (new_ptr_list_t*)((char*)pointer - ALIGNED_LIST_ITEM_SIZE); + if (ptr->magic != MAGIC) + { + { + fast_mutex_autolock lock(new_output_lock); + const char * msg_out = "[QxOrm] delete%s: invalid pointer %p"; Q_UNUSED(msg_out); + qDebug(msg_out, (is_array ? "[]" : ""), pointer); + } + check_mem_corruption(); + fflush(new_output_fp); + _DEBUG_NEW_ERROR_ACTION; + } + if (is_array != ptr->is_array) + { + const char * msg; + if (is_array) { msg = "[QxOrm] delete[] after new"; } + else { msg = "[QxOrm] delete after new[]"; } + fast_mutex_autolock lock(new_output_lock); + const char * msg_out_file = "[QxOrm] %s: pointer %p (size %u)\n\tat %p\n\toriginally allocated at %s:%d"; Q_UNUSED(msg_out_file); + const char * msg_out_no_file = "[QxOrm] %s: pointer %p (size %u)\n\tat %p\n\toriginally allocated at "; Q_UNUSED(msg_out_no_file); + if (ptr->line != 0) { qDebug(msg_out_file, msg, ((char*)ptr + ALIGNED_LIST_ITEM_SIZE), ptr->size, addr, ptr->file, ptr->line); } +#if (! _QX_MEM_LEAK_ONLY_KNOWN_SRC_FILE) + else { qDebug(msg_out_no_file, msg, ((char*)ptr + ALIGNED_LIST_ITEM_SIZE), ptr->size, addr); } +#endif // (! _QX_MEM_LEAK_ONLY_KNOWN_SRC_FILE) + fflush(new_output_fp); + _DEBUG_NEW_ERROR_ACTION; + } +#if _DEBUG_NEW_TAILCHECK + if (!check_tail(ptr)) + { + check_mem_corruption(); + fflush(new_output_fp); + _DEBUG_NEW_ERROR_ACTION; + } +#endif + { + fast_mutex_autolock lock(new_ptr_lock); + total_mem_alloc -= ptr->size; + ptr->magic = 0; + ptr->prev->next = ptr->next; + ptr->next->prev = ptr->prev; + } + if (new_verbose_flag) + { + fast_mutex_autolock lock(new_output_lock); + const char * msg_out = "[QxOrm] delete%s: freed %p (size %u, %u bytes still allocated)"; Q_UNUSED(msg_out); + qDebug(msg_out, (is_array ? "[]" : ""), ((char*)ptr + ALIGNED_LIST_ITEM_SIZE), ptr->size, total_mem_alloc); + } + free(ptr); + return; +} + +/** + * Checks for memory leaks. + * + * @return zero if no leakage is found; the number of leaks otherwise + */ +int check_leaks() +{ + int leak_cnt = 0; + fast_mutex_autolock lock_ptr(new_ptr_lock); + fast_mutex_autolock lock_output(new_output_lock); + new_ptr_list_t* ptr = new_ptr_list.next; + while (ptr != &new_ptr_list) + { + const char* const pointer = (char*)ptr + ALIGNED_LIST_ITEM_SIZE; + if (ptr->magic != MAGIC) + { + const char * msg_out = "[QxOrm] warning: heap data corrupt near %p"; Q_UNUSED(msg_out); + qDebug(msg_out, pointer); + } +#if _DEBUG_NEW_TAILCHECK + if (!check_tail(ptr)) + { + const char * msg_out = "[QxOrm] warning: overwritten past end of object at %p"; Q_UNUSED(msg_out); + qDebug(msg_out, pointer); + } +#endif + const char * msg_out_file = "[QxOrm] Leaked object at %p (size %u, %s:%d)"; Q_UNUSED(msg_out_file); + const char * msg_out_no_file = "[QxOrm] Leaked object at %p (size %u, )"; Q_UNUSED(msg_out_no_file); + if (ptr->line != 0) { qDebug(msg_out_file, pointer, ptr->size, ptr->file, ptr->line); ++leak_cnt; } +#if (! _QX_MEM_LEAK_ONLY_KNOWN_SRC_FILE) + else { qDebug(msg_out_no_file, pointer, ptr->size); ++leak_cnt; } +#endif // (! _QX_MEM_LEAK_ONLY_KNOWN_SRC_FILE) + ptr = ptr->next; + } + if (new_verbose_flag || leak_cnt) + { + const char * msg_out_leak_cnt = "[QxOrm] **** %d memory leaks found ****"; Q_UNUSED(msg_out_leak_cnt); + qDebug(msg_out_leak_cnt, leak_cnt); + } + return leak_cnt; +} + +/** + * Checks for heap corruption. + * + * @return zero if no problem is found; the number of found memory + * corruptions otherwise + */ +int check_mem_corruption() +{ + int corrupt_cnt = 0; + fast_mutex_autolock lock_ptr(new_ptr_lock); + fast_mutex_autolock lock_output(new_output_lock); + const char * msg_out_check_mem_start = "[QxOrm] **** Checking for memory corruption: START ****"; Q_UNUSED(msg_out_check_mem_start); + qDebug("%s", msg_out_check_mem_start); + for (new_ptr_list_t* ptr = new_ptr_list.next; ptr != &new_ptr_list; ptr = ptr->next) + { + const char* const pointer = (char*)ptr + ALIGNED_LIST_ITEM_SIZE; + if (ptr->magic == MAGIC +#if _DEBUG_NEW_TAILCHECK + && check_tail(ptr) +#endif + ) + continue; +#if _DEBUG_NEW_TAILCHECK + if (ptr->magic != MAGIC) + { +#endif + const char * msg_out_file = "[QxOrm] Heap data corrupt near %p (size %u, %s:%d)"; Q_UNUSED(msg_out_file); + const char * msg_out_no_file = "[QxOrm] Heap data corrupt near %p (size %u, )"; Q_UNUSED(msg_out_no_file); + if (ptr->line != 0) { qDebug(msg_out_file, pointer, ptr->size, ptr->file, ptr->line); ++corrupt_cnt; } +#if (! _QX_MEM_LEAK_ONLY_KNOWN_SRC_FILE) + else { qDebug(msg_out_no_file, pointer, ptr->size); ++corrupt_cnt; } +#endif // (! _QX_MEM_LEAK_ONLY_KNOWN_SRC_FILE) +#if _DEBUG_NEW_TAILCHECK + } + else + { + const char * msg_out_file = "[QxOrm] Overwritten past end of object at %p (size %u, %s:%d)"; Q_UNUSED(msg_out_file); + const char * msg_out_no_file = "[QxOrm] Overwritten past end of object at %p (size %u, )"; Q_UNUSED(msg_out_no_file); + if (ptr->line != 0) { qDebug(msg_out_file, pointer, ptr->size, ptr->file, ptr->line); ++corrupt_cnt; } +#if (! _QX_MEM_LEAK_ONLY_KNOWN_SRC_FILE) + else { qDebug(msg_out_no_file, pointer, ptr->size); ++corrupt_cnt; } +#endif // (! _QX_MEM_LEAK_ONLY_KNOWN_SRC_FILE) + } +#endif + } + const char * msg_out_check_mem_end = "[QxOrm] **** Checking for memory corruption: %d FOUND ****"; Q_UNUSED(msg_out_check_mem_end); + qDebug(msg_out_check_mem_end, corrupt_cnt); + return corrupt_cnt; +} + +void __debug_new_recorder::_M_process(void* pointer) +{ + if (pointer == NULL) { return; } + new_ptr_list_t* ptr = (new_ptr_list_t*)((char*)pointer - ALIGNED_LIST_ITEM_SIZE); + if (ptr->magic != MAGIC || ptr->line != 0) + { + fast_mutex_autolock lock(new_output_lock); + const char * msg_out = "[QxOrm] warning: debug_new used with placement new (%s:%d)"; Q_UNUSED(msg_out); + qDebug(msg_out, _M_file, _M_line); + return; + } +#if _DEBUG_NEW_FILENAME_LEN == 0 + ptr->file = _M_file; +#else + QX_STRNCPY(ptr->file, _M_file, _DEBUG_NEW_FILENAME_LEN - 1); + ptr->file[_DEBUG_NEW_FILENAME_LEN - 1] = '\0'; +#endif + ptr->line = _M_line; +} + +int __debug_new_counter::_S_count = 0; +__debug_new_counter::__debug_new_counter() { ++_S_count; } +__debug_new_counter::~__debug_new_counter() { if ((--_S_count == 0) && new_autocheck_flag) { check_leaks(); } } + +} // namespace memory +} // namespace qx + +void* operator new(size_t size, const char* file, int line) +{ + void* ptr = qx::memory::alloc_mem(size, file, line, false); +#if _DEBUG_NEW_STD_OPER_NEW + if (ptr) { return ptr; } + else { throw std::bad_alloc(); } +#else + return ptr; +#endif +} + +void* operator new[](size_t size, const char* file, int line) +{ + void* ptr = qx::memory::alloc_mem(size, file, line, true); +#if _DEBUG_NEW_STD_OPER_NEW + if (ptr) { return ptr; } + else { throw std::bad_alloc(); } +#else + return ptr; +#endif +} + +#if HAVE_PLACEMENT_DELETE +void operator delete(void* pointer, const char* file, int line) throw() +{ + if (qx::memory::new_verbose_flag) + { + qx::memory::fast_mutex_autolock lock(qx::memory::new_output_lock); + const char * msg_out = "[QxOrm] info: exception thrown on initializing object at %p (%s:%d)"; Q_UNUSED(msg_out); + qDebug(msg_out, pointer, file, line); + } + operator delete(pointer); +} + +void operator delete[](void* pointer, const char* file, int line) throw() +{ + if (qx::memory::new_verbose_flag) + { + qx::memory::fast_mutex_autolock lock(qx::memory::new_output_lock); + const char * msg_out = "[QxOrm] info: exception thrown on initializing objects at %p (%s:%d)"; Q_UNUSED(msg_out); + qDebug(msg_out, pointer, file, line); + } + operator delete[](pointer); +} +#endif // HAVE_PLACEMENT_DELETE + +#endif // _QX_USE_MEM_LEAK_DETECTION +#endif // _QX_MODE_RELEASE +#endif // QT_NO_DEBUG diff --git a/src/QxMemLeak/mem_pool_base.cpp b/src/QxMemLeak/mem_pool_base.cpp new file mode 100644 index 0000000..1849e21 --- /dev/null +++ b/src/QxMemLeak/mem_pool_base.cpp @@ -0,0 +1,84 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2008 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/** + * @file mem_pool_base.cpp + * + * Implementation for the memory pool base. + * + * @version 1.2, 2004/07/26 + * @author Wu Yongwei + * + */ + +#include + +#ifndef QT_NO_DEBUG +#ifndef _QX_MODE_RELEASE +#if _QX_USE_MEM_LEAK_DETECTION + +#if defined(_MEM_POOL_USE_MALLOC) +#include +#else +#include +#endif + +#include + +/* Defines macros to abstract system memory routines */ +# ifdef _MEM_POOL_USE_MALLOC +# define _MEM_POOL_ALLOCATE(_Sz) malloc(_Sz) +# define _MEM_POOL_DEALLOCATE(_Ptr) free(_Ptr) +# else +# define _MEM_POOL_ALLOCATE(_Sz) ::operator new((_Sz), std::nothrow) +# define _MEM_POOL_DEALLOCATE(_Ptr) ::operator delete(_Ptr) +# endif + +namespace qx { +namespace memory { + +mem_pool_base::~mem_pool_base() +{ +} + +void* mem_pool_base::alloc_sys(size_t __size) +{ + return _MEM_POOL_ALLOCATE(__size); +} + +void mem_pool_base::dealloc_sys(void* __ptr) +{ + _MEM_POOL_DEALLOCATE(__ptr); +} + +} // namespace memory +} // namespace qx + +#endif // _QX_USE_MEM_LEAK_DETECTION +#endif // _QX_MODE_RELEASE +#endif // QT_NO_DEBUG diff --git a/src/QxMemLeak/static_mem_pool.cpp b/src/QxMemLeak/static_mem_pool.cpp new file mode 100644 index 0000000..ecd8797 --- /dev/null +++ b/src/QxMemLeak/static_mem_pool.cpp @@ -0,0 +1,111 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2008 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/** + * @file static_mem_pool.cpp + * + * Non-template and non-inline code for the `static' memory pool. + * + * @version 1.7, 2006/08/26 + * @author Wu Yongwei + * + */ + +#include + +#ifndef QT_NO_DEBUG +#ifndef _QX_MODE_RELEASE +#if _QX_USE_MEM_LEAK_DETECTION + +#include + +#include +#include + +namespace qx { +namespace memory { + +static_mem_pool_set::static_mem_pool_set() +{ + _STATIC_MEM_POOL_TRACE(false, "The static_mem_pool_set is created"); +} + +static_mem_pool_set::~static_mem_pool_set() +{ + std::for_each(_M_memory_pool_set.rbegin(), + _M_memory_pool_set.rend(), + delete_object()); + _STATIC_MEM_POOL_TRACE(false, "The static_mem_pool_set is destroyed"); +} + +/** + * Creates the singleton instance of #static_mem_pool_set. + * + * @return reference to the instance of #static_mem_pool_set + */ +static_mem_pool_set& static_mem_pool_set::instance() +{ + lock __guard; + static static_mem_pool_set _S_instance; + return _S_instance; +} + +/** + * Asks all static memory pools to recycle unused memory blocks back to + * the system. The caller should get the lock to prevent other + * operations to #static_mem_pool_set during its execution. + */ +void static_mem_pool_set::recycle() +{ + _STATIC_MEM_POOL_TRACE(false, "Memory pools are being recycled"); + container_type::iterator __end = _M_memory_pool_set.end(); + for (container_type::iterator + __i = _M_memory_pool_set.begin(); + __i != __end; ++__i) + { + (*__i)->recycle(); + } +} + +/** + * Adds a new memory pool to #static_mem_pool_set. + * + * @param __memory_pool_p pointer to the memory pool to add + */ +void static_mem_pool_set::add(mem_pool_base* __memory_pool_p) +{ + lock __guard; + _M_memory_pool_set.push_back(__memory_pool_p); +} + +} // namespace memory +} // namespace qx + +#endif // _QX_USE_MEM_LEAK_DETECTION +#endif // _QX_MODE_RELEASE +#endif // QT_NO_DEBUG diff --git a/src/QxModelView/IxModel.cpp b/src/QxModelView/IxModel.cpp new file mode 100644 index 0000000..ee7af2d --- /dev/null +++ b/src/QxModelView/IxModel.cpp @@ -0,0 +1,840 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +#define QX_CONSTRUCT_IX_MODEL() \ + m_pClass(NULL), m_pModelClass(NULL), m_pDataMemberX(NULL), m_pDataMemberId(NULL), m_pCollection(NULL), \ + m_pParent(NULL), m_eAutoUpdateDatabase(IxModel::e_no_auto_update), m_pDataMemberRelationToParent(NULL), m_lManualInsertIndex(0) + +namespace qx +{ + + IxModel::IxModel(QObject *parent /* = 0 */) : QAbstractItemModel(parent), QX_CONSTRUCT_IX_MODEL() { ; } + + IxModel::~IxModel() { ; } + + IxClass *IxModel::getClass() const { return m_pClass; } + + IxClass *IxModel::getModelClass() const { return m_pModelClass; } + + IxCollection *IxModel::getCollection() const { return m_pCollection; } + + QSqlDatabase IxModel::getDatabase() const { return m_database; } + + QSqlError IxModel::getLastError() const { return m_lastError; } + + QString IxModel::getLastErrorAsString() const { return (m_lastError.isValid() ? m_lastError.text() : QString()); } + + QStringList IxModel::getListOfColumns() const { return m_lstColumns; } + + int IxModel::getRowCount() const { return rowCount(); } + + QHash IxModel::getListOfHeaders() const + { + QHash lst; + QHashIterator itr(m_lstHeadersData); + QString sStart = (QString::number(Qt::DisplayRole) + "|"); + while (itr.hasNext()) + { + itr.next(); + if (itr.key().startsWith(sStart)) + { + QString sKey = itr.key(); + sKey.replace(sStart, ""); + lst.insert(sKey, itr.value().toString()); + } + } + return lst; + } + + IxDataMember *IxModel::getDataMember(int column) const { return (((column < 0) || (column >= m_lstDataMember.count())) ? NULL : m_lstDataMember.at(column)); } + + QString IxModel::getDataMemberKey(int column) const + { + IxDataMember *p = getDataMember(column); + return (p ? p->getKey() : QString()); + } + + QVariant IxModel::getModelValue(int row, const QString &column) const + { + if (!m_lstDataMemberByKey.contains(column)) + { + return QVariant(); + } + int iColumnIndex = m_lstDataMemberByKey.value(column); + QModelIndex idx = index(row, iColumnIndex, QModelIndex()); + return data(idx, Qt::DisplayRole); + } + + int IxModel::getColumnIndex(const QString &sColumnName) const + { + if (!m_lstDataMemberByKey.contains(sColumnName)) + { + qAssert(false); + return -1; + } + return m_lstDataMemberByKey.value(sColumnName); + } + + int IxModel::getAutoUpdateDatabase_() const { return static_cast(m_eAutoUpdateDatabase); } + + IxModel::e_auto_update_database IxModel::getAutoUpdateDatabase() const { return m_eAutoUpdateDatabase; } + + QVariant IxModel::getCustomProperty(const QString &key) const { return (m_hCustomProperties.contains(key) ? m_hCustomProperties.value(key) : QVariant()); } + + QObject *IxModel::getParentModel() const { return static_cast(m_pParent); } + + void IxModel::dumpModel(bool bJsonFormat /* = true */) const { dumpModelImpl(bJsonFormat); } + + QObject *IxModel::cloneModel() { return cloneModelImpl(); } + + void IxModel::setDatabase(const QSqlDatabase &db) { m_database = db; } + + void IxModel::setListOfColumns(const QStringList &lst) + { + m_lstColumns = lst; + clear(true); + } + + void IxModel::setListOfHeaders(const QHash &lst) + { + QHashIterator itr(lst); + while (itr.hasNext()) + { + itr.next(); + QString sHeaderDataKey = (QString::number(Qt::DisplayRole) + "|" + itr.key()); + QVariant vHeaderDataValue = QVariant(itr.value()); + m_lstHeadersData.insert(sHeaderDataKey, vHeaderDataValue); + } + Q_EMIT headerDataChanged(Qt::Horizontal, 0, (m_lstDataMember.count() - 1)); + } + + bool IxModel::setModelValue(int row, const QString &column, const QVariant &value) + { + if (!m_lstDataMemberByKey.contains(column)) + { + return false; + } + int iColumnIndex = m_lstDataMemberByKey.value(column); + QModelIndex idx = index(row, iColumnIndex, QModelIndex()); + return setData(idx, value, Qt::EditRole); + } + + void IxModel::setParentModel(IxModel *pParent) + { + m_pParent = pParent; + m_pDataMemberRelationToParent = NULL; + if (!pParent || !m_pDataMemberX) + { + return; + } + long lCount = m_pDataMemberX->count_WithDaoStrategy(); + for (long l = 0; l < lCount; l++) + { + IxDataMember *p = m_pDataMemberX->get_WithDaoStrategy(l); + if (!p) + { + continue; + } + IxSqlRelation *pRelation = p->getSqlRelation(); + if (!pRelation) + { + continue; + } + pRelation->init(); + if (pRelation->getDataId() == pParent->m_pDataMemberId) + { + m_pDataMemberRelationToParent = p; + break; + } + } + } + + void IxModel::setAutoUpdateDatabase_(int i) { m_eAutoUpdateDatabase = static_cast(i); } + + void IxModel::setAutoUpdateDatabase(IxModel::e_auto_update_database e) { m_eAutoUpdateDatabase = e; } + + void IxModel::setCustomProperty(const QString &key, const QVariant &val) { m_hCustomProperties.insert(key, val); } + + int IxModel::qxCount_(const QString &sQuery) + { + qx_query query(sQuery); + return static_cast(qxCount(query, database(NULL))); + } + + bool IxModel::qxFetchById_(const QVariant &id, const QStringList &relation /* = QStringList() */) { return (!qxFetchById(id, relation, database(NULL)).isValid()); } + + bool IxModel::qxFetchAll_(const QStringList &relation /* = QStringList() */) { return (!qxFetchAll(relation, database(NULL)).isValid()); } + + bool IxModel::qxFetchByQuery_(const QString &sQuery, const QStringList &relation /* = QStringList() */) + { + qx_query query(sQuery); + return (!qxFetchByQuery(query, relation, database(NULL)).isValid()); + } + + bool IxModel::qxFetchRow_(int row, const QStringList &relation /* = QStringList() */) { return (!qxFetchRow(row, relation, database(NULL)).isValid()); } + + bool IxModel::qxInsert_(const QStringList &relation /* = QStringList() */, bool bUseExecBatch /* = false */) { return (!qxInsert(relation, database(NULL), bUseExecBatch).isValid()); } + + bool IxModel::qxInsertRow_(int row, const QStringList &relation /* = QStringList() */) { return (!qxInsertRow(row, relation, database(NULL)).isValid()); } + + bool IxModel::qxUpdate_(const QString &sQuery, const QStringList &relation /* = QStringList() */, bool bUseExecBatch /* = false */) + { + qx_query query(sQuery); + return (!qxUpdate(query, relation, database(NULL), bUseExecBatch).isValid()); + } + + bool IxModel::qxUpdateRow_(int row, const QString &sQuery, const QStringList &relation /* = QStringList() */) + { + qx_query query(sQuery); + return (!qxUpdateRow(row, query, relation, database(NULL)).isValid()); + } + + bool IxModel::qxSave_(const QStringList &relation /* = QStringList() */) { return (!qxSave(relation, database(NULL)).isValid()); } + + bool IxModel::qxSaveRow_(int row, const QStringList &relation /* = QStringList() */) { return (!qxSaveRow(row, relation, database(NULL)).isValid()); } + + bool IxModel::qxDeleteById_(const QVariant &id) { return (!qxDeleteById(id, database(NULL)).isValid()); } + + bool IxModel::qxDeleteAll_() { return (!qxDeleteAll(database(NULL)).isValid()); } + + bool IxModel::qxDeleteByQuery_(const QString &sQuery) + { + qx_query query(sQuery); + return (!qxDeleteByQuery(query, database(NULL)).isValid()); + } + + bool IxModel::qxDeleteRow_(int row) { return (!qxDeleteRow(row, database(NULL)).isValid()); } + + bool IxModel::qxDestroyById_(const QVariant &id) { return (!qxDestroyById(id, database(NULL)).isValid()); } + + bool IxModel::qxDestroyAll_() { return (!qxDestroyAll(database(NULL)).isValid()); } + + bool IxModel::qxDestroyByQuery_(const QString &sQuery) + { + qx_query query(sQuery); + return (!qxDestroyByQuery(query, database(NULL)).isValid()); + } + + bool IxModel::qxDestroyRow_(int row) { return (!qxDestroyRow(row, database(NULL)).isValid()); } + + bool IxModel::qxExecuteQuery_(const QString &sQuery) + { + qx_query query(sQuery); + return (!qxExecuteQuery(query, database(NULL)).isValid()); + } + + bool IxModel::qxExist_(const QVariant &id) { return qxExist(id, database(NULL)).getValue(); } + + QString IxModel::qxValidate_(const QStringList &groups /* = QStringList() */) + { + qx::QxInvalidValueX invalidValueX = qxValidate(groups); + return (invalidValueX ? QString() : invalidValueX.text()); + } + + QString IxModel::qxValidateRow_(int row, const QStringList &groups /* = QStringList() */) + { + qx::QxInvalidValueX invalidValueX = qxValidateRow(row, groups); + return (invalidValueX ? QString() : invalidValueX.text()); + } + + void IxModel::raiseEvent_headerDataChanged(Qt::Orientation orientation, int first, int last) { Q_EMIT headerDataChanged(orientation, first, last); } + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + + void IxModel::raiseEvent_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles /* = QVector() */) { Q_EMIT dataChanged(topLeft, bottomRight, roles); } + + void IxModel::raiseEvent_layoutAboutToBeChanged(const QList &parents /* = QList() */, QAbstractItemModel::LayoutChangeHint hint /* = QAbstractItemModel::NoLayoutChangeHint */) { Q_EMIT layoutAboutToBeChanged(parents, hint); } + + void IxModel::raiseEvent_layoutChanged(const QList &parents /* = QList() */, QAbstractItemModel::LayoutChangeHint hint /* = QAbstractItemModel::NoLayoutChangeHint */) { Q_EMIT layoutChanged(parents, hint); } + +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + + void IxModel::raiseEvent_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { Q_EMIT dataChanged(topLeft, bottomRight); } + + void IxModel::raiseEvent_layoutAboutToBeChanged() { Q_EMIT layoutAboutToBeChanged(); } + + void IxModel::raiseEvent_layoutChanged() { Q_EMIT layoutChanged(); } + +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + + void IxModel::clear(bool bUpdateColumns /* = false */) + { + if (!m_pCollection) + { + qAssert(false); + return; + } + if (!bUpdateColumns && (m_pCollection->_count() <= 0)) + { + return; + } + beginResetModel(); + m_pCollection->_clear(); + for (long l = (m_lstChild.count() - 1); l >= 0; l--) + { + removeListOfChild(l); + } + m_lstChild.clear(); + m_hChild.clear(); + if (bUpdateColumns) + { + generateRoleNames(); + } + endResetModel(); + + if (getShowEmptyLine()) + { + setShowEmptyLine(false); + setShowEmptyLine(true); + } + } + + IxModel *IxModel::getChild(long row, const QString &relation) + { + if ((row < 0) || (row >= m_lstChild.count())) + { + return NULL; + } + IxModel::type_relation_by_name child = m_lstChild.at(row); + if (!child.contains(relation)) + { + return NULL; + } + return child.value(relation); + } + + void IxModel::insertChild(long row, const QString &relation, IxModel *pChild) + { + if ((row < 0) || (!pChild)) + { + return; + } + if (relation.isEmpty()) + { + return; + } + while (row > (m_lstChild.count() - 1)) + { + IxModel::type_relation_by_name tmp; + m_lstChild.append(tmp); + } + m_lstChild[row].insert(relation, pChild); + QPair pairRowRelation(static_cast(row), relation); + m_hChild.insert(pChild, pairRowRelation); + } + + void IxModel::removeListOfChild(long row) + { + if ((row < 0) || (row >= m_lstChild.count())) + { + return; + } + IxModel::type_relation_by_name lst = m_lstChild.at(row); + Q_FOREACH (IxModel *p, lst) + { + if (p) + { + m_hChild.remove(p); + delete p; + } + } + m_lstChild.removeAt(row); + } + + QSqlError IxModel::saveChildRelations(IxModel *pChild) + { + if (!m_hChild.contains(pChild)) + { + return QSqlError(); + } + QPair pairRowRelation = m_hChild.value(pChild); + return qxSaveRow(pairRowRelation.first, (QStringList() << pairRowRelation.second)); + } + + QVariant IxModel::getIdFromChild(IxModel *pChild) const + { + if (!m_hChild.contains(pChild)) + { + return QVariant(); + } + if (!m_pCollection || !m_pDataMemberId) + { + qAssert(false); + return QVariant(); + } + int row = m_hChild.value(pChild).first; + if ((row < 0) || (row >= m_pCollection->_count())) + { + return QVariant(); + } + void *pItem = getRowItemAsVoidPtr(row); + return (pItem ? m_pDataMemberId->toVariant(pItem) : QVariant()); + } + + QPair IxModel::getChildPosition(IxModel *pChild) const + { + if (!m_hChild.contains(pChild)) + { + return QPair(-1, ""); + } + return m_hChild.value(pChild); + } + + QVariant IxModel::data(const QModelIndex &index, int role /* = Qt::DisplayRole */) const + { + if (!index.isValid()) + { + return QVariant(); + } + if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) + { + if ((index.column() < 0) || (index.column() >= m_lstDataMember.count())) + { + return QVariant(); + } + else if ((index.row() < 0) || (index.row() >= rowCount())) + { + return QVariant(); + } + IxDataMember *pDataMember = m_lstDataMember.at(index.column()); + void *pItem = getRowItemAsVoidPtr(index.row()); + if (!pDataMember || !pItem) + { + return QVariant(); + } + return pDataMember->toVariant(pItem); + } + else if (role >= (Qt::UserRole + 1)) + { + QModelIndex idx = this->index(index.row(), (role - Qt::UserRole - 1), QModelIndex()); + return data(idx, Qt::DisplayRole); + } + return QVariant(); + } + + bool IxModel::setData(const QModelIndex &index, const QVariant &value, int role /* = Qt::EditRole */) + { + if (!index.isValid()) + { + return false; + } + if (role == Qt::EditRole) + { + if ((index.column() < 0) || (index.column() >= m_lstDataMember.count())) + { + return false; + } + else if ((index.row() < 0) || (index.row() >= rowCount())) + { + return false; + } + IxDataMember *pDataMember = m_lstDataMember.at(index.column()); + bool bDirtyRow = isDirtyRow(index.row()); + void *pItem = getRowItemAsVoidPtr(index.row()); + if (!pDataMember || !pItem) + { + return false; + } + QVariant vCurrentValue = pDataMember->toVariant(pItem); + if (vCurrentValue == value) + { + return true; + } + qx_bool bSetData = pDataMember->fromVariant(pItem, value); + if (bSetData && (m_eAutoUpdateDatabase == qx::IxModel::e_auto_update_on_field_change)) + { + qxSaveRowData(index.row(), (QStringList() << pDataMember->getKey())); + if (!m_lastError.isValid() && bDirtyRow) + { + insertDirtyRowToModel(); + } + if (m_lastError.isValid()) + { + if (!bDirtyRow) + { + qDebug("[QxOrm] qx::IxModel::setData() : %s", "an error occurred saving value in database (more details with 'getLastError()' method), so previous value has been restored"); + pDataMember->fromVariant(pItem, vCurrentValue); + return false; + } + } + else if (pDataMember->hasSqlRelation()) + { + qxFetchRow(index.row(), (QStringList() << pDataMember->getSqlRelation()->getKey())); + } + } + if (bSetData) + { + raiseEvent_dataChanged(index, index); + } + return bSetData; + } + else if (role >= (Qt::UserRole + 1)) + { + QModelIndex idx = this->index(index.row(), (role - Qt::UserRole - 1), QModelIndex()); + return setData(idx, value, Qt::EditRole); + } + return false; + } + + int IxModel::rowCount(const QModelIndex &parent /* = QModelIndex() */) const + { + if (parent.isValid() || !m_pCollection) + { + return 0; + } + return (static_cast(m_pCollection->_count()) + (getShowEmptyLine() ? 1 : 0)); + } + + int IxModel::columnCount(const QModelIndex &parent /* = QModelIndex() */) const + { + if (parent.isValid()) + { + return 0; + } + qAssert(m_lstRoleNames.count() == m_lstDataMember.count()); + return static_cast(m_lstRoleNames.count()); + } + + QModelIndex IxModel::index(int row, int column, const QModelIndex &parent /* = QModelIndex() */) const + { + if (!hasIndex(row, column, parent)) + { + return QModelIndex(); + } + if ((column < 0) || (column >= m_lstDataMember.count())) + { + return QModelIndex(); + } + IxDataMember *pDataMember = m_lstDataMember.at(column); + if (pDataMember) + { + return createIndex(row, column, static_cast(pDataMember)); + } + else + { + return QModelIndex(); + } + } + + QModelIndex IxModel::parent(const QModelIndex &index) const + { + Q_UNUSED(index); + return QModelIndex(); + } + + bool IxModel::hasChildren(const QModelIndex &parent /* = QModelIndex() */) const + { + Q_UNUSED(parent); + return false; + } + + QVariant IxModel::headerData(int section, Qt::Orientation orientation, int role /* = Qt::DisplayRole */) const + { + if (role == Qt::TextAlignmentRole) + { + return Qt::AlignCenter; + } + if ((orientation == Qt::Vertical) && (role == Qt::DisplayRole) && (isDirtyRow(section))) + { + return QVariant(QString("*")); + } + if (orientation != Qt::Horizontal) + { + return QAbstractItemModel::headerData(section, orientation, role); + } + if ((section < 0) || (section >= m_lstDataMember.count())) + { + return QAbstractItemModel::headerData(section, orientation, role); + } + IxDataMember *pDataMember = m_lstDataMember.at(section); + if (!pDataMember) + { + return QAbstractItemModel::headerData(section, orientation, role); + } + QString sHeaderDataKey = (QString::number(role) + "|" + pDataMember->getKey()); + if (m_lstHeadersData.contains(sHeaderDataKey)) + { + return m_lstHeadersData.value(sHeaderDataKey); + } + if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) + { + if (!pDataMember->getDescription().isEmpty()) + { + return pDataMember->getDescription(); + } + return pDataMember->getKey(); + } + return QVariant(); + } + + Qt::ItemFlags IxModel::flags(const QModelIndex &index) const + { + if (!index.isValid()) + { + return Qt::ItemIsEnabled; + } + return (Qt::ItemIsEditable | QAbstractItemModel::flags(index)); + } + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + QHash IxModel::roleNames() const + { + return m_lstRoleNames; + } +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + Qt::DropActions IxModel::supportedDragActions() const + { + return QAbstractItemModel::supportedDragActions(); + } +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + + Qt::DropActions IxModel::supportedDropActions() const + { + return QAbstractItemModel::supportedDropActions(); + } + + bool IxModel::removeRows(int row, int count, const QModelIndex &parent /* = QModelIndex() */) + { + if (parent.isValid()) + { + return false; + } + if (m_eAutoUpdateDatabase == IxModel::e_auto_update_on_field_change) + { + return removeRowsAutoUpdateOnFieldChange(row, count); + } + return removeRowsGeneric(row, count); + } + + bool IxModel::removeRowsGeneric(int row, int count) + { + if (!m_pCollection) + { + qAssert(false); + return false; + } + beginRemoveRows(QModelIndex(), row, (row + count - 1)); + for (int i = 0; i < count; ++i) + { + m_pCollection->_remove(row); + removeListOfChild(row); + } + updateShowEmptyLine(); + endRemoveRows(); + return true; + } + + bool IxModel::removeRowsAutoUpdateOnFieldChange(int row, int count) + { + if (!m_pCollection) + { + qAssert(false); + return false; + } + for (int i = 0; i < count; ++i) + { + if ((row >= 0) && (row < m_pCollection->_count())) + { + if (qxDeleteRow_(row)) + { + removeRowsGeneric(row, 1); + } + else + { + qDebug("[QxOrm] qx::IxModel::removeRowsAutoUpdateOnFieldChange() : %s", "an error occurred deleting row from database (more details with 'getLastError()' method), so row is not removed from model"); + return false; + } + } + else if (isDirtyRow(row)) + { + setShowEmptyLine(false); + setShowEmptyLine(true); + } + else + { + return true; + } + } + return true; + } + + bool IxModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role /* = Qt::EditRole */) + { + if (orientation != Qt::Horizontal) + { + return QAbstractItemModel::setHeaderData(section, orientation, value, role); + } + if ((section < 0) || (section >= m_lstDataMember.count())) + { + return false; + } + IxDataMember *pDataMember = m_lstDataMember.at(section); + if (!pDataMember) + { + return false; + } + + if ((role == Qt::EditRole) || (role == Qt::DisplayRole)) + { + QString sHeaderDataKey = (QString::number(Qt::EditRole) + "|" + pDataMember->getKey()); + m_lstHeadersData.insert(sHeaderDataKey, value); + sHeaderDataKey = (QString::number(Qt::DisplayRole) + "|" + pDataMember->getKey()); + m_lstHeadersData.insert(sHeaderDataKey, value); + } + else + { + QString sHeaderDataKey = (QString::number(role) + "|" + pDataMember->getKey()); + m_lstHeadersData.insert(sHeaderDataKey, value); + } + + Q_EMIT headerDataChanged(orientation, section, section); + return true; + } + + bool IxModel::setHeaderData(const QString &sColumnName, const QVariant &value, int role /* = Qt::EditRole */) + { + int section = getColumnIndex(sColumnName); + return ((section == -1) ? false : setHeaderData(section, Qt::Horizontal, value, role)); + } + + void IxModel::syncNestedModel(int row, const QStringList &relation) + { + Q_UNUSED(row); + Q_UNUSED(relation); + } + + void IxModel::syncAllNestedModel(const QStringList &relation) { Q_UNUSED(relation); } + + void IxModel::syncNestedModelRecursive(IxModel *pNestedModel, const QStringList &relation) + { + if (pNestedModel) + { + pNestedModel->syncAllNestedModel(relation); + } + } + + void IxModel::generateRoleNames() + { + int iRoleCount = 0; + m_lstRoleNames.clear(); + m_lstDataMember.clear(); + m_lstDataMemberByKey.clear(); + + for (long l = 0; l < (m_pDataMemberX ? m_pDataMemberX->count_WithDaoStrategy() : 0); ++l) + { + IxDataMember *pDataMember = m_pDataMemberX->get_WithDaoStrategy(l); + if (!pDataMember) + { + continue; + } + IxSqlRelation *pRelation = (pDataMember->hasSqlRelation() ? pDataMember->getSqlRelation() : NULL); + IxSqlRelation::relation_type eRelationType = (pRelation ? pRelation->getRelationType() : IxSqlRelation::no_relation); + if (pRelation && ((eRelationType == IxSqlRelation::one_to_many) || (eRelationType == IxSqlRelation::many_to_many))) + { + continue; + } + if ((m_lstColumns.count() > 0) && (!m_lstColumns.contains(pDataMember->getKey()))) + { + continue; + } + m_lstRoleNames.insert(static_cast(Qt::UserRole + 1 + iRoleCount), pDataMember->getKey().toLatin1()); + m_lstDataMemberByKey.insert(pDataMember->getKey(), static_cast(m_lstDataMember.count())); + m_lstDataMember.append(pDataMember); + iRoleCount++; + } + +#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) + setRoleNames(m_lstRoleNames); +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) + } + + QSqlDatabase *IxModel::database(QSqlDatabase *other) + { + return (other ? other : (m_database.isValid() ? (&m_database) : NULL)); + } + +#ifndef _QX_NO_JSON + + QString IxModel::toJson(int row /* = -1 */) const { return toJson_Helper(row); } + + bool IxModel::fromJson(const QString &json, int row /* = -1 */) { return fromJson_Helper(json, row); } + + QVariant IxModel::getRelationshipValues(int row, const QString &relation, bool bLoadFromDatabase /* = false */, const QString &sAppendRelations /* = QString() */) { return getRelationshipValues_Helper(row, relation, bLoadFromDatabase, sAppendRelations); } + + bool IxModel::setRelationshipValues(int row, const QString &relation, const QVariant &values) { return setRelationshipValues_Helper(row, relation, values); } + +#else // _QX_NO_JSON + + QString IxModel::toJson(int row /* = -1 */) const + { + qDebug("[QxOrm] qx::IxModel::toJson() : %s", "not implemented when _QX_NO_JSON compilation option is defined"); + Q_UNUSED(row); + return QString(); + } + + bool IxModel::fromJson(const QString &json, int row /* = -1 */) + { + qDebug("[QxOrm] qx::IxModel::fromJson() : %s", "not implemented when _QX_NO_JSON compilation option is defined"); + Q_UNUSED(json); + Q_UNUSED(row); + return false; + } + + QVariant IxModel::getRelationshipValues(int row, const QString &relation, bool bLoadFromDatabase /* = false */, const QString &sAppendRelations /* = QString() */) + { + qDebug("[QxOrm] qx::IxModel::getRelationshipValues() : %s", "not implemented when _QX_NO_JSON compilation option is defined"); + Q_UNUSED(row); + Q_UNUSED(relation); + Q_UNUSED(bLoadFromDatabase); + Q_UNUSED(sAppendRelations); + return QVariant(); + } + + bool IxModel::setRelationshipValues(int row, const QString &relation, const QVariant &values) + { + qDebug("[QxOrm] qx::IxModel::setRelationshipValues() : %s", "not implemented when _QX_NO_JSON compilation option is defined"); + Q_UNUSED(row); + Q_UNUSED(relation); + Q_UNUSED(values); + return false; + } + +#endif // _QX_NO_JSON + +} // namespace qx diff --git a/src/QxModelView/QxModelRowCompare.cpp b/src/QxModelView/QxModelRowCompare.cpp new file mode 100644 index 0000000..e2b0cbc --- /dev/null +++ b/src/QxModelView/QxModelRowCompare.cpp @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +namespace qx +{ + namespace model_view + { + + bool compareQVariant(const QVariant &v1, const QVariant &v2, bool bAscending) + { + int type1 = static_cast(v1.type()); + int type2 = static_cast(v2.type()); + + if (type1 != type2) + { + if (bAscending) + { + return (v1.toString() < v2.toString()); + } + else + { + return (v1.toString() > v2.toString()); + } + } + + if (type1 == QVariant::Bool) + { + int i1 = (v1.toBool() ? 1 : 0); + int i2 = (v2.toBool() ? 1 : 0); + if (bAscending) + { + return (i1 < i2); + } + else + { + return (i1 > i2); + } + } + else if (type1 == QVariant::Int) + { + if (bAscending) + { + return (v1.toInt() < v2.toInt()); + } + else + { + return (v1.toInt() > v2.toInt()); + } + } + else if (type1 == QVariant::UInt) + { + if (bAscending) + { + return (v1.toUInt() < v2.toUInt()); + } + else + { + return (v1.toUInt() > v2.toUInt()); + } + } + else if (type1 == QVariant::LongLong) + { + if (bAscending) + { + return (v1.toLongLong() < v2.toLongLong()); + } + else + { + return (v1.toLongLong() > v2.toLongLong()); + } + } + else if (type1 == QVariant::ULongLong) + { + if (bAscending) + { + return (v1.toULongLong() < v2.toULongLong()); + } + else + { + return (v1.toULongLong() > v2.toULongLong()); + } + } + else if (type1 == QVariant::Double) + { + if (bAscending) + { + return (v1.toDouble() < v2.toDouble()); + } + else + { + return (v1.toDouble() > v2.toDouble()); + } + } + else if (type1 == QVariant::Date) + { + if (bAscending) + { + return (v1.toDate() < v2.toDate()); + } + else + { + return (v1.toDate() > v2.toDate()); + } + } + else if (type1 == QVariant::DateTime) + { + if (bAscending) + { + return (v1.toDateTime() < v2.toDateTime()); + } + else + { + return (v1.toDateTime() > v2.toDateTime()); + } + } + else if (type1 == QVariant::Time) + { + if (bAscending) + { + return (v1.toTime() < v2.toTime()); + } + else + { + return (v1.toTime() > v2.toTime()); + } + } + else if (type1 == QVariant::ByteArray) + { + if (bAscending) + { + return (v1.toByteArray() < v2.toByteArray()); + } + else + { + return (v1.toByteArray() > v2.toByteArray()); + } + } + + // Default comparaison converting to string + if (bAscending) + { + return (v1.toString() < v2.toString()); + } + return (v1.toString() > v2.toString()); + } + + } // namespace model_view +} // namespace qx diff --git a/src/QxModelView/QxNestedModel.cpp b/src/QxModelView/QxNestedModel.cpp new file mode 100644 index 0000000..440c69a --- /dev/null +++ b/src/QxModelView/QxNestedModel.cpp @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +namespace qx +{ + namespace model_view + { + namespace detail + { + + } // namespace detail + } // namespace model_view +} // namespace qx diff --git a/src/QxRegister/IxClass.cpp b/src/QxRegister/IxClass.cpp new file mode 100644 index 0000000..3e40342 --- /dev/null +++ b/src/QxRegister/IxClass.cpp @@ -0,0 +1,367 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include +#include + +#include + +#include + +namespace qx +{ + + struct Q_DECL_HIDDEN IxClass::IxClassImpl + { + + IxDataMemberX *m_pDataMemberX; //!< List of data member + IxFunctionX_ptr m_pFctMemberX; //!< List of function member + IxFunctionX_ptr m_pFctStaticX; //!< List of function static + + QMutex m_mutex; //!< Mutex => qx::IxClass is thread-safe + QString m_sKey; //!< 'IxClass' key <=> class name + QString m_sName; //!< 'IxClass' name <=> database table name (if empty => class name) + QString m_sDescription; //!< 'IxClass' description + long m_lVersion; //!< 'IxClass' version + bool m_bFinalClass; //!< Class without base class (for example, qx::trait::no_base_class_defined and QObject) + bool m_bDaoReadOnly; //!< If 'true', cannot INSERT, UPDATE OR DELETE an instance of this class using qx::dao namespace + bool m_bRegistered; //!< Class registered into QxOrm context + qx::dao::strategy::inheritance m_eDaoStrategy; //!< Dao class strategy to access data member + qx::QxSoftDelete m_oSoftDelete; //!< Soft delete (or logical delete) behavior + IxValidatorX_ptr m_pAllValidator; //!< List of validator associated to the class + std::shared_ptr m_pSqlRelationX; //!< Collection of SQL relationships + std::shared_ptr> m_pSqlDataMemberX; //!< Collection of SQL columns (data member) + + QByteArray m_byteName; //!< Optimization to retrieve name under "const char *" format + const char *m_pName; //!< Optimization to retrieve name under "const char *" format + + IxClassImpl() : m_pDataMemberX(NULL), m_lVersion(-1), m_bFinalClass(false), m_bDaoReadOnly(false), m_bRegistered(false), m_eDaoStrategy(qx::dao::strategy::concrete_table_inheritance), m_pName(NULL) { ; } + ~IxClassImpl() { ; } + + void updateNamePtr() + { + m_byteName = m_sName.toLatin1(); + m_pName = m_byteName.constData(); + } + + IxDataMember *isValid_SqlRelation(long lIndex) const + { + IxDataMember *p = (m_pDataMemberX ? m_pDataMemberX->get_WithDaoStrategy(lIndex) : NULL); + bool bIsValid = (p && p->getDao() && p->hasSqlRelation()); + if (bIsValid) + { + p->getSqlRelation()->init(); + } + return (bIsValid ? p : NULL); + } + + IxDataMember *isValid_SqlDataMember(long lIndex) const + { + IxDataMember *pId = (m_pDataMemberX ? m_pDataMemberX->getId_WithDaoStrategy() : NULL); + IxDataMember *p = (m_pDataMemberX ? m_pDataMemberX->get_WithDaoStrategy(lIndex) : NULL); + bool bValid = (p && p->getDao() && !p->hasSqlRelation()); + bValid = (bValid && (p != pId)); + return (bValid ? p : NULL); + } + }; + + IxClass::IxClass() : qx::QxPropertyBag(), m_pImpl(new IxClassImpl()) { ; } + + IxClass::~IxClass() + { + if (QxClassX::isSingletonNull() || m_pImpl->m_sKey.isEmpty()) + { + return; + } + if (!QxClassX::getSingleton()->exist(m_pImpl->m_sKey)) + { + return; + } + QxClassX::getSingleton()->remove(m_pImpl->m_sKey); + } + + QString IxClass::getKey() const { return m_pImpl->m_sKey; } + + QString IxClass::getName() const { return m_pImpl->m_sName; } + + const char *IxClass::getNamePtr() const { return m_pImpl->m_pName; } + + QString IxClass::getDescription() const { return m_pImpl->m_sDescription; } + + long IxClass::getVersion() const { return m_pImpl->m_lVersion; } + + qx::dao::strategy::inheritance IxClass::getDaoStrategy() const { return m_pImpl->m_eDaoStrategy; } + + qx::QxSoftDelete IxClass::getSoftDelete() const { return m_pImpl->m_oSoftDelete; } + + bool IxClass::isFinalClass() const { return m_pImpl->m_bFinalClass; } + + bool IxClass::isDaoReadOnly() const { return m_pImpl->m_bDaoReadOnly; } + + bool IxClass::isRegistered() const { return m_pImpl->m_bRegistered; } + + IxDataMemberX *IxClass::getDataMemberX() const { return m_pImpl->m_pDataMemberX; } + + IxFunctionX *IxClass::getFctMemberX() const { return m_pImpl->m_pFctMemberX.get(); } + + IxFunctionX *IxClass::getFctStaticX() const { return m_pImpl->m_pFctStaticX.get(); } + + std::shared_ptr> IxClass::getSqlRelationX() + { + if ((m_pImpl->m_pSqlRelationX) || (!m_pImpl->m_pDataMemberX)) + { + return m_pImpl->m_pSqlRelationX; + } + + QMutexLocker locker(&m_pImpl->m_mutex); + if (m_pImpl->m_pSqlRelationX) + { + return m_pImpl->m_pSqlRelationX; + } + m_pImpl->m_pSqlRelationX = std::make_shared(); + IxDataMember *p = NULL; + long lCount = m_pImpl->m_pDataMemberX->count_WithDaoStrategy(); + for (long l = 0; l < lCount; ++l) + { + if ((p = m_pImpl->isValid_SqlRelation(l))) + { + m_pImpl->m_pSqlRelationX->insert(p->getKey(), p->getSqlRelation()); + } + } + return m_pImpl->m_pSqlRelationX; + } + + std::shared_ptr> IxClass::getSqlDataMemberX() + { + if ((m_pImpl->m_pSqlDataMemberX) || (!m_pImpl->m_pDataMemberX)) + { + return m_pImpl->m_pSqlDataMemberX; + } + + QMutexLocker locker(&m_pImpl->m_mutex); + if (m_pImpl->m_pSqlDataMemberX) + { + return m_pImpl->m_pSqlDataMemberX; + } + m_pImpl->m_pSqlDataMemberX = std::make_shared>(); + IxDataMember *p = NULL; + long lCount = m_pImpl->m_pDataMemberX->count_WithDaoStrategy(); + for (long l = 0; l < lCount; ++l) + { + if ((p = m_pImpl->isValid_SqlDataMember(l))) + { + m_pImpl->m_pSqlDataMemberX->insert(p->getKey(), p); + } + } + return m_pImpl->m_pSqlDataMemberX; + } + + IxValidatorX_ptr &IxClass::getAllValidatorRef() { return m_pImpl->m_pAllValidator; } + + void IxClass::setKey(const QString &sKey) { m_pImpl->m_sKey = sKey; } + + void IxClass::setName(const QString &sName) + { + m_pImpl->m_sName = sName; + m_pImpl->updateNamePtr(); + } + + void IxClass::setDescription(const QString &sDesc) { m_pImpl->m_sDescription = sDesc; } + + void IxClass::setDaoStrategy(qx::dao::strategy::inheritance eDaoStrategy) { m_pImpl->m_eDaoStrategy = eDaoStrategy; } + + void IxClass::setSoftDelete(const qx::QxSoftDelete &oSoftDelete) + { + m_pImpl->m_oSoftDelete = oSoftDelete; + if (m_pImpl->m_oSoftDelete.getTableName().isEmpty()) + { + m_pImpl->m_oSoftDelete.setTableName(m_pImpl->m_sName); + } + } + + void IxClass::setDaoReadOnly(bool bDaoReadOnly) { m_pImpl->m_bDaoReadOnly = bDaoReadOnly; } + + void IxClass::setVersion(long l) { m_pImpl->m_lVersion = l; } + + void IxClass::setRegistered(bool b) { m_pImpl->m_bRegistered = b; } + + void IxClass::setFinalClass(bool b) { m_pImpl->m_bFinalClass = b; } + + void IxClass::setDataMemberX(IxDataMemberX *p) { m_pImpl->m_pDataMemberX = p; } + + void IxClass::setFctMemberX(IxFunctionX *p) { m_pImpl->m_pFctMemberX.reset(p); } + + void IxClass::setFctStaticX(IxFunctionX *p) { m_pImpl->m_pFctStaticX.reset(p); } + + void IxClass::updateClassX() + { + qAssert(!m_pImpl->m_sKey.isEmpty() && !QxClassX::getSingleton()->exist(m_pImpl->m_sKey)); + QxClassX::getSingleton()->insert(m_pImpl->m_sKey, this); + } + + IxValidatorX *IxClass::getAllValidator() + { + if (!m_pImpl->m_pAllValidator) + { + return NULL; + } + m_pImpl->m_pAllValidator->setClass(this); + return m_pImpl->m_pAllValidator.get(); + } + + IxDataMember *IxClass::getId(bool bRecursive /* = false */) const + { + IxDataMember *pId = (m_pImpl->m_pDataMemberX ? m_pImpl->m_pDataMemberX->getId() : NULL); + if (pId || !bRecursive) + { + return pId; + } + IxClass *pBaseClass = getBaseClass(); + return (pBaseClass ? pBaseClass->getId(bRecursive) : NULL); + } + + bool IxClass::isKindOf(const QString &sClassName) const + { + if (sClassName.isEmpty()) + { + qAssert(false); + return false; + } + if (m_pImpl->m_sKey == sClassName) + { + return true; + } + + IxClass *p = getBaseClass(); + while (p != NULL) + { + if (p->getKey() == sClassName) + { + return true; + } + p = p->getBaseClass(); + } + + return false; + } + +#ifndef _QX_NO_RTTI + bool IxClass::isKindOf(const std::type_info &typeInfo) const + { + if (typeInfo == typeid(void)) + { + qAssert(false); + return false; + } + if (typeInfo == this->typeInfo()) + { + return true; + } + + IxClass *p = getBaseClass(); + while (p != NULL) + { + if (typeInfo == p->typeInfo()) + { + return true; + } + p = p->getBaseClass(); + } + + return false; + } +#endif // _QX_NO_RTTI + + QString IxClass::dumpClass() const + { + QString sDump; + sDump += "-- class '" + m_pImpl->m_sKey + "' (name '" + m_pImpl->m_sName + "', "; + sDump += "description '" + m_pImpl->m_sDescription + "', version '" + QString::number(m_pImpl->m_lVersion) + "', "; + sDump += "base class '" + (getBaseClass() ? getBaseClass()->getKey() : "") + "')\n"; + + long lCount = (m_pImpl->m_pDataMemberX ? m_pImpl->m_pDataMemberX->count() : 0); + sDump += "\t* list of registered properties (" + QString::number(lCount) + ")\n"; + if (m_pImpl->m_pDataMemberX) + { + IxDataMember *pId = this->getId(); + for (long l = 0; l < lCount; l++) + { + IxDataMember *p = m_pImpl->m_pDataMemberX->get(l); + if (!p) + { + continue; + } + IxSqlRelation *pRelation = p->getSqlRelation(); + QString sInfos = p->getKey() + ((p == pId) ? QString(" (id)") : QString()) + " - " + p->getType(); + sInfos += (pRelation ? (QString(" (") + pRelation->getDescription() + QString(")")) : QString()); + sDump += "\t\t" + sInfos + "\n"; + } + } + + lCount = (m_pImpl->m_pFctMemberX ? m_pImpl->m_pFctMemberX->count() : 0); + sDump += "\t* list of registered member functions (" + QString::number(lCount) + ")\n"; + if (m_pImpl->m_pFctMemberX) + { + for (auto itr = m_pImpl->m_pFctMemberX->begin(); itr != m_pImpl->m_pFctMemberX->end(); ++itr) + { + IxFunction_ptr p = itr->second; + if (!p) + { + continue; + } + QString sKey = p->getKey(); + sDump += "\t\t" + sKey + "\n"; + } + } + + lCount = (m_pImpl->m_pFctStaticX ? m_pImpl->m_pFctStaticX->count() : 0); + sDump += "\t* list of registered static functions (" + QString::number(lCount) + ")\n"; + if (m_pImpl->m_pFctStaticX) + { + for (auto itr = m_pImpl->m_pFctStaticX->begin(); itr != m_pImpl->m_pFctStaticX->end(); ++itr) + { + IxFunction_ptr p = itr->second; + if (!p) + { + continue; + } + QString sKey = p->getKey(); + sDump += "\t\t" + sKey + "\n"; + } + } + + qDebug("%s", qPrintable(sDump)); + return sDump; + } + +} // namespace qx diff --git a/src/QxRegister/QxClassX.cpp b/src/QxRegister/QxClassX.cpp new file mode 100644 index 0000000..bf309d3 --- /dev/null +++ b/src/QxRegister/QxClassX.cpp @@ -0,0 +1,523 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +#include +#include + +#include +#include + +#include + +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::QxClassX) + +namespace qx +{ + + QxClassX::QxClassX() : QxSingleton("qx::QxClassX") + { + initSqlTypeByClassName(); + initValidatorMessage(); + } + + QxClassX::~QxClassX() { ; } + + QxCollection *QxClassX::getAll() + { + return (&m_lstClass); + } + + IxClass *QxClassX::get(const QString &sKey) const + { + IxClass *pClass = (m_lstClass.exist(sKey) ? m_lstClass.getByKey(sKey) : NULL); + if (!pClass) + { + qx::create(sKey); + } + if (!pClass) + { + pClass = (m_lstClass.exist(sKey) ? m_lstClass.getByKey(sKey) : NULL); + } + + return pClass; + } + + bool QxClassX::exist(const QString &sKey) const + { + return m_lstClass.exist(sKey); + } + + bool QxClassX::insert(const QString &sKey, IxClass *pClass) + { + return ((!pClass || sKey.isEmpty() || m_lstClass.exist(sKey)) ? false : m_lstClass.insert(sKey, pClass)); + } + + bool QxClassX::remove(const QString &sKey) + { + return m_lstClass.removeByKey(sKey); + } + + void QxClassX::clear() + { + m_lstClass.clear(); + } + + qx::any QxClassX::create(const QString &sKey) + { + return qx::create(sKey); + } + + IxClass *QxClassX::getClass(const QString &sKey) + { + return QxClassX::getSingleton()->get(sKey); + } + + IxDataMemberX *QxClassX::getDataMemberX(const QString &sKey) + { + IxClass *pClass = QxClassX::getClass(sKey); + return (pClass ? pClass->getDataMemberX() : NULL); + } + + IxFunctionX *QxClassX::getFctMemberX(const QString &sKey) + { + IxClass *pClass = QxClassX::getClass(sKey); + return (pClass ? pClass->getFctMemberX() : NULL); + } + + IxFunctionX *QxClassX::getFctStaticX(const QString &sKey) + { + IxClass *pClass = QxClassX::getClass(sKey); + return (pClass ? pClass->getFctStaticX() : NULL); + } + + IxDataMember *QxClassX::getDataMember(const QString &sClassKey, const QString &sDataKey, bool bRecursive /* = true */) + { + QString sBaseClassKey = sClassKey; + IxClass *pClass(NULL); + IxDataMemberX *pDataX(NULL); + IxDataMember *pData(NULL); + + do + { + pClass = QxClassX::getClass(sBaseClassKey); + pDataX = QxClassX::getDataMemberX(sBaseClassKey); + pData = ((pDataX && pDataX->exist(sDataKey)) ? pDataX->get(sDataKey) : NULL); + sBaseClassKey = ((pClass && pClass->getBaseClass()) ? pClass->getBaseClass()->getKey() : QString("")); + } while (bRecursive && !pData && pClass && !sBaseClassKey.isEmpty() && !pClass->isFinalClass()); + + return pData; + } + + IxFunction *QxClassX::getFctMember(const QString &sClassKey, const QString &sFctKey, bool bRecursive /* = true */) + { + QString sBaseClassKey = sClassKey; + IxClass *pClass(NULL); + IxFunctionX *pFctX(NULL); + IxFunction *pFct(NULL); + + do + { + pClass = QxClassX::getClass(sBaseClassKey); + pFctX = QxClassX::getFctMemberX(sBaseClassKey); + pFct = ((pFctX && pFctX->exist(sFctKey)) ? pFctX->getByKey(sFctKey).get() : NULL); + sBaseClassKey = ((pClass && pClass->getBaseClass()) ? pClass->getBaseClass()->getKey() : QString("")); + } while (bRecursive && !pFct && pClass && !sBaseClassKey.isEmpty() && !pClass->isFinalClass()); + + return pFct; + } + + IxFunction *QxClassX::getFctStatic(const QString &sClassKey, const QString &sFctKey, bool bRecursive /* = true */) + { + QString sBaseClassKey = sClassKey; + IxClass *pClass(NULL); + IxFunctionX *pFctX(NULL); + IxFunction *pFct(NULL); + + do + { + pClass = QxClassX::getClass(sBaseClassKey); + pFctX = QxClassX::getFctStaticX(sBaseClassKey); + pFct = ((pFctX && pFctX->exist(sFctKey)) ? pFctX->getByKey(sFctKey).get() : NULL); + sBaseClassKey = ((pClass && pClass->getBaseClass()) ? pClass->getBaseClass()->getKey() : QString("")); + } while (bRecursive && !pFct && pClass && !sBaseClassKey.isEmpty() && !pClass->isFinalClass()); + + return pFct; + } + + bool QxClassX::implementIxPersistable(const QString &sKey, bool bTraceIfFalse /* = true */) + { + IxClass *pClass = QxClassX::getClass(sKey); + bool bImplement = (pClass ? pClass->implementIxPersistable() : false); + if (!bImplement && bTraceIfFalse) + { + qDebug("[QxOrm] class '%s' doesn't implement qx::IxPersistable interface", qPrintable(sKey)); + } + return bImplement; + } + + qx_bool QxClassX::invokeVoidPtr(const QString &sClassKey, const QString &sFctKey, void *pOwner, const QString ¶ms /* = QString() */, qx::any *ret /* = NULL */) + { + IxFunction *pFct = QxClassX::getFctMember(sClassKey, sFctKey, true); + return ((pOwner && pFct) ? pFct->invoke(pOwner, params, ret) : qx_bool(false)); + } + + qx_bool QxClassX::invokeVoidPtr(const QString &sClassKey, const QString &sFctKey, void *pOwner, const type_any_params ¶ms, qx::any *ret /* = NULL */) + { + IxFunction *pFct = QxClassX::getFctMember(sClassKey, sFctKey, true); + return ((pOwner && pFct) ? pFct->invoke(pOwner, params, ret) : qx_bool(false)); + } + + qx_bool QxClassX::invokeStatic(const QString &sClassKey, const QString &sFctKey, const QString ¶ms /* = QString() */, qx::any *ret /* = NULL */) + { + IxFunction *pFct = QxClassX::getFctStatic(sClassKey, sFctKey, true); + return (pFct ? pFct->invoke(params, ret) : qx_bool(false)); + } + + qx_bool QxClassX::invokeStatic(const QString &sClassKey, const QString &sFctKey, const type_any_params ¶ms, qx::any *ret /* = NULL */) + { + IxFunction *pFct = QxClassX::getFctStatic(sClassKey, sFctKey, true); + return (pFct ? pFct->invoke(params, ret) : qx_bool(false)); + } + +#ifndef _QX_NO_RTTI + const std::type_info &QxClassX::typeInfo(const QString &sKey) const + { + IxClass *pClass = QxClassX::getClass(sKey); + return (pClass ? pClass->typeInfo() : typeid(void)); + } +#endif // _QX_NO_RTTI + + QxCollection *QxClassX::getAllClasses() + { + QxClassX::registerAllClasses(); + return QxClassX::getSingleton()->getAll(); + } + + void QxClassX::registerAllClasses(bool bInitAllRelations /* = true */, bool bInitValidator /* = true */) + { + QHash *pAllFactory = QxFactoryX::getSingleton()->getAllFactory(); + if (!pAllFactory) + { + qAssert(false); + return; + } + + QHashIterator itr(*pAllFactory); + while (itr.hasNext()) + { + itr.next(); + IxClass *pClass = QxClassX::getClass(itr.key()); + qAssert(pClass != NULL); + if ((pClass) && (bInitValidator)) + { + pClass->getAllValidator(); + } + } + + if (!bInitAllRelations) + { + return; + } + QxCollection *pAllClasses = QxClassX::getSingleton()->getAll(); + if (!pAllClasses) + { + qAssert(false); + return; + } + + for (long k = 0; k < pAllClasses->count(); k++) + { + IxClass *pClass = pAllClasses->getByIndex(k); + if (!pClass) + { + continue; + } + IxDataMemberX *pDataMemberX = pClass->getDataMemberX(); + if (!pDataMemberX) + { + continue; + } + for (long l = 0; l < pDataMemberX->count_WithDaoStrategy(); l++) + { + IxDataMember *pDataMember = pDataMemberX->get_WithDaoStrategy(l); + if (!pDataMember) + { + continue; + } + if (isValid_SqlRelation(pDataMember)) + { + pDataMember->getSqlRelation()->init(); + } + } + pClass->getSqlDataMemberX(); + pClass->getSqlRelationX(); + } + } + + QString QxClassX::dumpAllClasses() + { + QxClassX::registerAllClasses(); + QxCollection *pAllClasses = QxClassX::getAllClasses(); + if (!pAllClasses) + { + qAssert(false); + return ""; + } + + QString sDump; + long lCount = pAllClasses->count(); + qDebug("[QxOrm] start dump all registered classes (%ld)", lCount); + for (auto itr = pAllClasses->begin(); itr != pAllClasses->end(); ++itr) + { + IxClass *pClass = itr->second; + if (pClass) + { + sDump += pClass->dumpClass(); + } + } + qDebug("[QxOrm] %s", "end dump all registered classes"); + + return sDump; + } + + QString QxClassX::dumpSqlSchema() + { + qDebug("[QxOrm] qx::QxClassX::dumpSqlSchema() : %s", "be careful with this function, it's just an example and tested only with SQLite database, so it's strongly recommended to write your own function to create your SQL schema"); + QxCollection *pAllClasses = QxClassX::getAllClasses(); + if (!pAllClasses) + { + qAssert(false); + return ""; + } + QString sql; + long lSqlCount = 0; + + for (long k = 0; k < pAllClasses->count(); k++) + { + IxClass *pClass = pAllClasses->getByIndex(k); + if (!pClass) + { + continue; + } + + // If the class is a parameter or a service from 'QxService' module, it's not a persistent class + if (pClass->isKindOf("qx::service::IxParameter") || pClass->isKindOf("qx::service::IxService")) + { + continue; + } + + // ---- + // Here, you can filter other classes using property bag (meta-data), for example : + // QString sProp = pClass->getPropertyBag("NOT_A_DATABASE_OBJECT").toString(); + // if (sProp == "1") { continue; } + // ---- + + // Get the version of the class : if (version = 0) then 'CREATE TABLE', else if (version > 0) then 'ALTER TABLE' + long lVersion = pClass->getVersion(); + bool bCreateTable = (lVersion <= 0); + sql += (bCreateTable ? "CREATE TABLE " : "ALTER TABLE "); + sql += pClass->getName() + " "; + sql += (bCreateTable ? "(" : "ADD ("); + int iSqlCountRef = sql.length(); + + // Get the primary key (id) of table, all columns into table, and other parameters associated to table + IxDataMember *pId = pClass->getId(); + IxDataMemberX *pDataMemberX = pClass->getDataMemberX(); + QxSoftDelete oSoftDelete = pClass->getSoftDelete(); + + // Insert primary key (id) to SQL schema + if (pId && (bCreateTable || (pId->getVersion() >= lVersion))) + { + sql += pId->getSqlNameAndTypeAndParams(", ") + ", "; + qAssert(!pId->getSqlType().isEmpty()); + } + + // Insert all columns to SQL schema + for (long l = 0; (pDataMemberX && (l < pDataMemberX->count_WithDaoStrategy())); l++) + { + IxDataMember *p = pDataMemberX->get_WithDaoStrategy(l); + if (isValid_DataMember(p) && (p != pId) && (bCreateTable || (p->getVersion() >= lVersion))) + { + sql += p->getSqlNameAndTypeAndParams(", ") + ", "; + qAssert(!p->getSqlType().isEmpty()); + } + + // ---- + // Here, you can use property bag (meta-data) to add some SQL features, for example : + // QString sProp = p->getPropertyBag("INDEX").toString(); + // if (sProp == "1") { sql += "CREATE INDEX" + etc...; } + // ---- + } + + // Insert soft delete behaviour to SQL schema + if (bCreateTable && !oSoftDelete.isEmpty()) + { + sql += oSoftDelete.buildSqlQueryToCreateTable() + ", "; + } + + // Insert all relations to SQL schema + for (long l = 0; (pDataMemberX && (l < pDataMemberX->count_WithDaoStrategy())); l++) + { + IxDataMember *p = pDataMemberX->get_WithDaoStrategy(l); + QxSqlRelationParams params(0, 0, (&sql), NULL, NULL, NULL); + if (isValid_SqlRelation(p) && (p != pId) && (bCreateTable || (p->getVersion() >= lVersion))) + { + p->getSqlRelation()->createTable(params); + } + } + + // Terminate SQL schema for current class + bool bAddBracket = (sql.length() != iSqlCountRef); + sql = sql.left(sql.length() - 2); // Remove last ", " + if (bAddBracket) + { + sql += ")\n"; + } + else + { + sql += "\n"; + } + lSqlCount++; + + // Create extra-table from relations (for example, many-to-many relation needs an extra-table) + for (long l = 0; (pDataMemberX && (l < pDataMemberX->count_WithDaoStrategy())); l++) + { + IxDataMember *p = pDataMemberX->get_WithDaoStrategy(l); + if (isValid_SqlRelation(p) && (p != pId) && (bCreateTable || (p->getVersion() >= lVersion))) + { + QString sqlExtraTable = p->getSqlRelation()->createExtraTable(); + if (sqlExtraTable.isEmpty()) + { + continue; + } + sql += sqlExtraTable + "\n"; + } + } + } + + qDebug("[QxOrm] start dump SQL schema (%ld)", lSqlCount); + qDebug("%s", qPrintable(sql)); + qDebug("[QxOrm] %s", "end dump SQL schema"); + + return sql; + } + + bool QxClassX::isValid_DataMember(IxDataMember *p) + { + return (p && p->getDao() && !p->hasSqlRelation()); + } + + bool QxClassX::isValid_SqlRelation(IxDataMember *p) + { + bool bIsValid = (p && p->getDao() && p->hasSqlRelation()); + if (bIsValid) + { + p->getSqlRelation()->init(); + } + return bIsValid; + } + + void QxClassX::initSqlTypeByClassName() + { + m_lstSqlTypeByClassName.clear(); + + m_lstSqlTypeByClassName.insert("bool", "SMALLINT"); + m_lstSqlTypeByClassName.insert("qx_bool", "SMALLINT"); + m_lstSqlTypeByClassName.insert("char", "SMALLINT"); + m_lstSqlTypeByClassName.insert("short", "SMALLINT"); + m_lstSqlTypeByClassName.insert("int", "INTEGER"); + m_lstSqlTypeByClassName.insert("long", "INTEGER"); + m_lstSqlTypeByClassName.insert("long long", "INTEGER"); + m_lstSqlTypeByClassName.insert("float", "FLOAT"); + m_lstSqlTypeByClassName.insert("double", "FLOAT"); + m_lstSqlTypeByClassName.insert("long double", "FLOAT"); + m_lstSqlTypeByClassName.insert("unsigned short", "SMALLINT"); + m_lstSqlTypeByClassName.insert("unsigned int", "INTEGER"); + m_lstSqlTypeByClassName.insert("unsigned long", "INTEGER"); + m_lstSqlTypeByClassName.insert("unsigned long long", "INTEGER"); + m_lstSqlTypeByClassName.insert("std::string", "TEXT"); + m_lstSqlTypeByClassName.insert("std::wstring", "TEXT"); + m_lstSqlTypeByClassName.insert("QString", "TEXT"); + m_lstSqlTypeByClassName.insert("QVariant", "TEXT"); + m_lstSqlTypeByClassName.insert("QUuid", "TEXT"); + m_lstSqlTypeByClassName.insert("QDate", "DATE"); + m_lstSqlTypeByClassName.insert("QTime", "TIME"); + m_lstSqlTypeByClassName.insert("QDateTime", "TIMESTAMP"); + m_lstSqlTypeByClassName.insert("QByteArray", "BLOB"); + m_lstSqlTypeByClassName.insert("qx::QxDateNeutral", "TEXT"); + m_lstSqlTypeByClassName.insert("qx::QxTimeNeutral", "TEXT"); + m_lstSqlTypeByClassName.insert("qx::QxDateTimeNeutral", "TEXT"); + } + + void QxClassX::initValidatorMessage() + { + m_lstValidatorMessage.clear(); + + m_lstValidatorMessage.insert("not_null", "value '%NAME%' cannot be null"); + m_lstValidatorMessage.insert("not_empty", "value '%NAME%' cannot be empty"); + m_lstValidatorMessage.insert("min_value", "value '%NAME%' must be greater than or equal to '%CONSTRAINT%'"); + m_lstValidatorMessage.insert("max_value", "value '%NAME%' must be lesser than or equal to '%CONSTRAINT%'"); + m_lstValidatorMessage.insert("min_length", "size of '%NAME%' must be greater than or equal to '%CONSTRAINT%' characters"); + m_lstValidatorMessage.insert("max_length", "size of '%NAME%' must be lesser than or equal to '%CONSTRAINT%' characters"); + m_lstValidatorMessage.insert("date_past", "date '%NAME%' must be in the past"); + m_lstValidatorMessage.insert("date_future", "date '%NAME%' must be in the future"); + m_lstValidatorMessage.insert("min_decimal", "value '%NAME%' must be greater than or equal to '%CONSTRAINT%'"); + m_lstValidatorMessage.insert("max_decimal", "value '%NAME%' must be lesser than or equal to '%CONSTRAINT%'"); + m_lstValidatorMessage.insert("regular_expression", "value '%NAME%' doesn't match the regular expression '%CONSTRAINT%'"); + m_lstValidatorMessage.insert("e_mail", "value '%NAME%' is not a valid e-mail"); + } + + namespace trait + { + namespace detail + { + + /* Defined into './include/QxTraits/get_sql_type.h' file */ + const char *get_sql_type_by_class_name(const char *sClassName, const char *sDefaultValue) + { + static std::string s; +#ifndef QT_NO_STL + s = qx::QxClassX::getSqlTypeByClassName(sClassName).toStdString(); +#else // QT_NO_STL + s = qx::QxClassX::getSqlTypeByClassName(sClassName).toLatin1().constData(); +#endif // QT_NO_STL + return (s.empty() ? sDefaultValue : s.c_str()); + } + + } // namespace detail + } // namespace trait +} // namespace qx diff --git a/src/QxRestApi/QxRestApi.cpp b/src/QxRestApi/QxRestApi.cpp new file mode 100644 index 0000000..a6e35d9 --- /dev/null +++ b/src/QxRestApi/QxRestApi.cpp @@ -0,0 +1,1045 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#ifndef _QX_NO_JSON +#include +#include +#include +#include +#include +#include +#endif // _QX_NO_JSON + +#include +#include + +#include + +namespace qx +{ + + struct Q_DECL_HIDDEN QxRestApi::QxRestApiImpl + { + + QString m_requestId; //!< Request identifier (GUID for example) + QString m_entity; //!< Entity where to process request + QString m_action; //!< Action to execute (count, fetch_by_id, fetch_all, fetch_by_query, insert, update, etc...) + QString m_function; //!< Entity static function to call with action 'call_entity_function' and signature : static QJsonValue myEntity::myFct(const QJsonValue & request) + QStringList m_columns; //!< Columns to fetch + QStringList m_relations; //!< Relations to fetch + QStringList m_outputFormat; //!< JSON output format (same syntax as relations) + QString m_database; //!< Database to use to process request + QString m_query; //!< SQL query to execute + QString m_data; //!< Data used to process request + QSqlError m_error; //!< Error after executing the request + qx::IxPersistable_ptr m_instance; //!< Current instance to execute request + QSqlDatabase m_db; //!< Current database to execute request + qx_query m_qxQuery; //!< Query used by some actions + long m_countResult; //!< Result after a count query + qx_bool m_existResult; //!< Result after a exist query + QxInvalidValueX m_validateResult; //!< Result after a validate query + qx::dao::save_mode::e_save_mode m_eSaveMode; //!< Save mode for 'save' action + bool m_bUseExecBatch; //!< If true then use the QSqlQuery::execBatch() method to improve performance inserting/updating/deleting a list of instances to database (but doesn't fill the last inserted identifier in the C++ instances) + +#ifndef _QX_NO_JSON + + QJsonValue m_requestJson; //!< Request which contains all parameters (as JSON format) + QJsonValue m_responseJson; //!< Response after executing the request (as JSON format) + QJsonValue m_dataJson; //!< Data used to process request (as JSON format) + QJsonValue m_errorJson; //!< Error after executing the request (as JSON format) + +#endif // _QX_NO_JSON + + QxRestApiImpl() : m_countResult(0), m_eSaveMode(qx::dao::save_mode::e_none), m_bUseExecBatch(false) + { + ; + } + ~QxRestApiImpl() { ; } + +#ifndef _QX_NO_JSON + + void clear(); + void resetRequest(); + QJsonValue processRequestAsArray(const QJsonValue &request); + void buildError(int errCode, const QString &errDesc); + void buildError(const QSqlError &error); + bool parseRequest(const QString &request); + bool createInstance(); + bool decodeRequest(); + bool checkRequest(); + bool executeAction(); + bool buildResponse(); + bool formatResponse(); + bool doRequest(); + void getMetaData(); + QJsonValue getMetaData(IxClass *pClass); + qx_bool callEntityFunction(); + void getDatabases(); + +#endif // _QX_NO_JSON + }; + + QxRestApi::QxRestApi(QObject *parent /* = NULL */) : QObject(parent), m_pImpl(new QxRestApiImpl()) { ; } + + QxRestApi::~QxRestApi() { ; } + + void QxRestApi::clearAll() { (*m_pImpl) = QxRestApiImpl(); } + + QString QxRestApi::getErrorDesc() const { return (m_pImpl->m_error.isValid() ? (m_pImpl->m_error.driverText() + "\n" + m_pImpl->m_error.databaseText()) : QString()); } + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + int QxRestApi::getErrorCode() const + { + return (m_pImpl->m_error.isValid() ? m_pImpl->m_error.nativeErrorCode().toInt() : 0); + } +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + int QxRestApi::getErrorCode() const + { + return (m_pImpl->m_error.isValid() ? m_pImpl->m_error.number() : 0); + } +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + + QSqlError QxRestApi::getError() const + { + return m_pImpl->m_error; + } + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + QString QxRestApi::getNativeErrorCode() const + { + return m_pImpl->m_error.nativeErrorCode(); + } +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + + void QxRestApi::setEntity(const QString &entity) + { + m_pImpl->m_entity = entity; + } + + void QxRestApi::setAction(const QString &action) { m_pImpl->m_action = action; } + + void QxRestApi::setFunction(const QString &fct) { m_pImpl->m_function = fct; } + + void QxRestApi::setColumns(const QStringList &columns) { m_pImpl->m_columns = columns; } + + void QxRestApi::setRelations(const QStringList &relations) { m_pImpl->m_relations = relations; } + + void QxRestApi::setOutputFormat(const QStringList &outputFormat) { m_pImpl->m_outputFormat = outputFormat; } + + void QxRestApi::setDatabase(const QString &database) { m_pImpl->m_database = database; } + + void QxRestApi::setQuery(const QString &query) { m_pImpl->m_query = query; } + + void QxRestApi::setData(const QString &data) { m_pImpl->m_data = data; } + + void QxRestApi::setUseExecBatch(bool useExecBatch) { m_pImpl->m_bUseExecBatch = useExecBatch; } + + QString QxRestApi::processRequest(const QString &request) + { +#ifdef _QX_NO_JSON + QString msg = "QxOrm library must be built without _QX_NO_JSON compilation option to be able to call REST API module"; + m_pImpl->m_error = QSqlError(msg, "Cannot process request with _QX_NO_JSON compilation option", QSqlError::UnknownError); + return "{ \"error\": \"" + msg + "\" }"; +#else // _QX_NO_JSON + m_pImpl->clear(); + if (m_pImpl->parseRequest(request)) + { + processRequest(m_pImpl->m_requestJson); + } + QJsonValue result = (m_pImpl->m_errorJson.isNull() ? m_pImpl->m_responseJson : m_pImpl->m_errorJson); + return qx::cvt::to_string(result); +#endif // _QX_NO_JSON + } + +#ifndef _QX_NO_JSON + + void QxRestApi::setData(const QJsonValue &data) + { + m_pImpl->m_dataJson = data; + } + + QJsonValue QxRestApi::processRequest(const QJsonValue &request) + { + m_pImpl->clear(); + +#ifdef _QX_ENABLE_MONGODB + if (qx::QxSqlDatabase::getSingleton()->getDriverName() == "QXMONGODB") + { + m_pImpl->m_db = QSqlDatabase(); + } + else +#endif // _QX_ENABLE_MONGODB + { + m_pImpl->m_db = qx::QxSqlDatabase::getDatabase(m_pImpl->m_error); + } + + if (m_pImpl->m_error.isValid()) + { + m_pImpl->buildError(m_pImpl->m_error); + return m_pImpl->m_errorJson; + } + if (request.isArray()) + { + return m_pImpl->processRequestAsArray(request); + } + m_pImpl->m_requestJson = request; + if (!m_pImpl->doRequest()) + { + return m_pImpl->m_errorJson; + } + return m_pImpl->m_responseJson; + } + + QJsonValue QxRestApi::QxRestApiImpl::processRequestAsArray(const QJsonValue &request) + { + QJsonArray responseArray; + QJsonArray requestArray = request.toArray(); + if (requestArray.count() <= 0) + { + buildError(9999, "Request array is empty"); + return m_errorJson; + } + + bool bTransaction = false; + if (m_db.driver() && m_db.driver()->hasFeature(QSqlDriver::Transactions)) + { + bTransaction = m_db.transaction(); + } + + for (int i = 0; i < requestArray.count(); i++) + { + resetRequest(); + m_requestJson = requestArray.at(i); + if (!doRequest()) + { + break; + } + responseArray.append(m_responseJson); + } + + bool bTransactionOk = true; + if (!m_errorJson.isNull() && bTransaction) + { + bTransactionOk = m_db.rollback(); + } + else if (bTransaction) + { + bTransactionOk = m_db.commit(); + } + if (m_errorJson.isNull() && !bTransactionOk) + { + buildError(m_db.lastError()); + } + + if (!m_errorJson.isNull()) + { + return m_errorJson; + } + m_responseJson = responseArray; + return m_responseJson; + } + + bool QxRestApi::QxRestApiImpl::doRequest() + { + if (!decodeRequest()) + { + return false; + } + if (!checkRequest()) + { + return false; + } + if (!createInstance()) + { + return false; + } + if (!executeAction()) + { + return false; + } + if (!buildResponse()) + { + return false; + } + if (!formatResponse()) + { + return false; + } + return true; + } + + void QxRestApi::QxRestApiImpl::clear() + { + // Reset error and instance + m_responseJson = QJsonValue(); + m_errorJson = QJsonValue(); + m_error = QSqlError(); + m_instance.reset(); + m_db = QSqlDatabase(); + } + + void QxRestApi::QxRestApiImpl::resetRequest() + { + m_requestId = ""; + m_action = ""; + m_entity = ""; + m_data = ""; + m_query = ""; + m_function = ""; + m_database = ""; + m_qxQuery = qx_query(); + m_dataJson = QJsonValue(); + m_requestJson = QJsonValue(); + m_responseJson = QJsonValue(); + m_eSaveMode = qx::dao::save_mode::e_none; + m_bUseExecBatch = false; + m_columns.clear(); + m_relations.clear(); + m_outputFormat.clear(); + m_instance.reset(); + } + + void QxRestApi::QxRestApiImpl::buildError(int errCode, const QString &errDesc) + { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + m_error = QSqlError(errDesc, "", QSqlError::UnknownError, QString::number(errCode)); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + m_error = QSqlError(errDesc, "", QSqlError::UnknownError, errCode); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + QJsonObject errJson; + QJsonObject errDetail; + errDetail.insert("code", errCode); + errDetail.insert("desc", errDesc); + errJson.insert("error", errDetail); + if (!m_requestId.isEmpty()) + { + errJson.insert("request_id", m_requestId); + } + m_errorJson = errJson; + } + + void QxRestApi::QxRestApiImpl::buildError(const QSqlError &error) + { + if (!error.isValid()) + { + return; + } + m_error = error; + QJsonObject errJson; + QJsonObject errDetail; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + errDetail.insert("code", error.nativeErrorCode()); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + errDetail.insert("code", error.number()); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + QString errMsg = (error.driverText() + "\n" + error.databaseText()); + errDetail.insert("desc", errMsg); + errJson.insert("error", errDetail); + if (!m_requestId.isEmpty()) + { + errJson.insert("request_id", m_requestId); + } + m_errorJson = errJson; + } + + bool QxRestApi::QxRestApiImpl::createInstance() + { + // Some actions doesn't require any instance + if (m_action == "get_meta_data") + { + return true; + } + if (m_action == "get_databases") + { + return true; + } + if (m_action == "call_custom_query") + { + return true; + } + if (m_action == "call_entity_function") + { + return true; + } + + // Check if entity implements qx::IxPersistable interface + m_instance.reset(); + if (m_entity.isEmpty()) + { + buildError(9999, "JSON request is invalid : 'entity' field is empty"); + return false; + } + if (!qx::QxClassX::implementIxPersistable(m_entity)) + { + buildError(9999, "Entity '" + m_entity + "' must implement qx::IxPersistable interface"); + return false; + } + m_instance = qx::IxPersistable_ptr(static_cast(qx::create_void_ptr(m_entity))); + if (!m_instance) + { + buildError(9999, "Entity '" + m_entity + "' is not valid : unable to create a new qx::IxPersistable instance"); + return false; + } + + // Check if action requires some input data + if (m_action == "count") + { + return true; + } + if (m_action == "delete_all") + { + return true; + } + if (m_action == "destroy_all") + { + return true; + } + if (m_action == "delete_by_query") + { + return true; + } + if (m_action == "destroy_by_query") + { + return true; + } + + // Check if some input data has been provided by caller + if (m_dataJson.isNull() && (m_action != "fetch_all") && (m_action != "fetch_by_query")) + { + buildError(9999, "No data provided for entity '" + m_entity + "'"); + return false; + } + + // Check data format : single instance, or array of instances, or collection of instances (hash-map key/value) + if (m_dataJson.isArray()) + { + std::shared_ptr lst; + QJsonArray dataArray = m_dataJson.toArray(); + if (dataArray.count() <= 0) + { + buildError(9999, "Data array is empty for entity '" + m_entity + "'"); + return false; + } + QJsonValue dataFirst = dataArray.first(); + if (dataFirst.isNull()) + { + buildError(9999, "First item in data array is null for entity '" + m_entity + "'"); + return false; + } + if (!dataFirst.isObject()) + { + buildError(9999, "First item in data array is not an object for entity '" + m_entity + "'"); + return false; + } + QJsonObject dataFirstObject = dataFirst.toObject(); + if (dataFirstObject.isEmpty()) + { + buildError(9999, "First object in data array is empty for entity '" + m_entity + "'"); + return false; + } + if ((dataFirstObject.count() == 2) && dataFirstObject.contains("key") && dataFirstObject.contains("value")) + { + lst = m_instance->qxNewPersistableCollection(false); + } + else + { + lst = m_instance->qxNewPersistableCollection(true); + } + m_instance = std::static_pointer_cast(lst); + } + else if ((m_action == "fetch_all") || (m_action == "fetch_by_query")) + { + std::shared_ptr lst; + lst = m_instance->qxNewPersistableCollection(true); + m_instance = std::static_pointer_cast(lst); + } + + // Fill data in qx::IxPersistable instance + qx_bool bFromJson = (m_dataJson.isNull() ? qx_bool(true) : m_instance->fromJson_(m_dataJson)); + if (!bFromJson) + { + buildError(static_cast(bFromJson.getCode()), bFromJson.getDesc()); + return false; + } + return true; + } + + bool QxRestApi::QxRestApiImpl::parseRequest(const QString &request) + { + // Parse request as JSON format + QJsonParseError jsonError; + QByteArray requestAsByteArray = request.toUtf8(); + QJsonDocument doc = QJsonDocument::fromJson(requestAsByteArray, (&jsonError)); + if (jsonError.error != QJsonParseError::NoError) + { + buildError(static_cast(jsonError.error), "Error parsing JSON request : " + jsonError.errorString()); + return false; + } + + // Request can be an array or a single object + m_requestJson = (doc.isArray() ? QJsonValue(doc.array()) : QJsonValue(doc.object())); + return true; + } + + bool QxRestApi::QxRestApiImpl::decodeRequest() + { + // Check if request is a valid JSON object + if (m_requestJson.isNull()) + { + buildError(9999, "Request is NULL"); + return false; + } + if (!m_requestJson.isObject()) + { + buildError(9999, "Request is not a JSON object"); + return false; + } + QJsonObject request = m_requestJson.toObject(); + + // Extract request identifier + if (request.contains("request_id")) + { + m_requestId = request.value("request_id").toString(); + } + + // Extract action + if (request.contains("action")) + { + m_action = request.value("action").toString(); + } + else + { + buildError(9999, "Parameter 'action' is required and cannot be empty"); + return false; + } + if (m_action.isEmpty()) + { + buildError(9999, "Parameter 'action' cannot be empty"); + return false; + } + + // Extract entity + if (request.contains("entity")) + { + m_entity = request.value("entity").toString(); + } + + // Extract data + if (request.contains("data")) + { + m_dataJson = request.value("data"); + } + else if (!m_data.isEmpty()) + { + qx_bool bParseOk = qx::cvt::from_string(m_data, m_dataJson); + if (!bParseOk) + { + buildError(static_cast(bParseOk.getCode()), bParseOk.getDesc()); + return false; + } + } + + // Extract function + if (request.contains("fct")) + { + qx::cvt::from_json(request.value("fct"), m_function); + } + + // Extract columns + if (request.contains("columns")) + { + qx::cvt::from_json(request.value("columns"), m_columns); + } + + // Extract relations + if (request.contains("relations")) + { + qx::cvt::from_json(request.value("relations"), m_relations); + } + + // Extract output format + if (request.contains("output_format")) + { + qx::cvt::from_json(request.value("output_format"), m_outputFormat); + } + + // Extract database key + if (request.contains("database")) + { + m_database = request.value("database").toString(); + } + + // Extract use exec batch setting + if (request.contains("use_exec_batch")) + { + m_bUseExecBatch = (request.value("use_exec_batch").toBool() || (request.value("use_exec_batch").toString() == "1")); + } + + // Extract save mode for 'save' action + if (request.contains("save_mode")) + { + QString sSaveMode = request.value("save_mode").toString(); + bool bSaveModeAsInt = false; + int iSaveMode = sSaveMode.toInt(&bSaveModeAsInt); + if (bSaveModeAsInt) + { + m_eSaveMode = static_cast(iSaveMode); + } + else + { + m_eSaveMode = ((sSaveMode == "check_insert_or_update") ? qx::dao::save_mode::e_check_insert_or_update : ((sSaveMode == "insert_only") ? qx::dao::save_mode::e_insert_only : ((sSaveMode == "update_only") ? qx::dao::save_mode::e_update_only : qx::dao::save_mode::e_none))); + } + } + + // Extract query + if (request.contains("query")) + { + qx::cvt::from_json(request.value("query"), m_qxQuery); + } + else if (!m_query.isEmpty()) + { + m_qxQuery = qx_query(m_query); + } + + return true; + } + + bool QxRestApi::QxRestApiImpl::checkRequest() + { + bool isEntityRequired = ((m_action == "get_meta_data") || (m_action == "call_entity_function")); + isEntityRequired = (isEntityRequired || (m_action == "fetch_by_id") || (m_action == "fetch_all") || (m_action == "fetch_by_query")); + isEntityRequired = (isEntityRequired || (m_action == "insert") || (m_action == "update") || (m_action == "save")); + isEntityRequired = (isEntityRequired || (m_action == "delete_by_id") || (m_action == "delete_all") || (m_action == "delete_by_query")); + isEntityRequired = (isEntityRequired || (m_action == "destroy_by_id") || (m_action == "destroy_all") || (m_action == "destroy_by_query")); + isEntityRequired = (isEntityRequired || (m_action == "exec_custom_query") || (m_action == "exist") || (m_action == "validate") || (m_action == "count")); + if (isEntityRequired && (m_entity.isEmpty())) + { + buildError(9999, "Parameter 'entity' is required and cannot be empty for action '" + m_action + "'"); + return false; + } + + bool isDataRequired = ((m_action == "fetch_by_id") || (m_action == "delete_by_id") || (m_action == "destroy_by_id")); + isDataRequired = (isDataRequired || (m_action == "insert") || (m_action == "update") || (m_action == "save")); + isDataRequired = (isDataRequired || (m_action == "exist") || (m_action == "validate")); + if (isDataRequired && (m_dataJson.isNull())) + { + buildError(9999, "Parameter 'data' is required and cannot be empty for action '" + m_action + "'"); + return false; + } + + bool isQueryRequired = ((m_action == "fetch_by_query") || (m_action == "delete_by_query") || (m_action == "destroy_by_query")); + isQueryRequired = (isQueryRequired || (m_action == "exec_custom_query") || (m_action == "call_custom_query")); + if (isQueryRequired && (m_qxQuery.query().isEmpty())) + { + buildError(9999, "Parameter 'query' is required and cannot be empty for action '" + m_action + "'"); + return false; + } + + bool isFunctionRequired = (m_action == "call_entity_function"); + if (isFunctionRequired && (m_function.isEmpty())) + { + buildError(9999, "Parameter 'fct' is required and cannot be empty for action '" + m_action + "'"); + return false; + } + + return true; + } + + bool QxRestApi::QxRestApiImpl::executeAction() + { + m_error = QSqlError(); + m_errorJson = QJsonValue(); + + try + { + if (m_action == "count") + { + m_countResult = 0; + m_error = m_instance->qxCount(m_countResult, m_qxQuery, (&m_db), m_relations); + } + else if (m_action == "fetch_by_id") + { + QVariant id; + m_error = m_instance->qxFetchById(id, m_columns, m_relations, (&m_db)); + } + else if (m_action == "fetch_all") + { + m_error = m_instance->qxFetchAll(NULL, m_columns, m_relations, (&m_db)); + } + else if (m_action == "fetch_by_query") + { + m_error = m_instance->qxFetchByQuery(m_qxQuery, NULL, m_columns, m_relations, (&m_db)); + } + else if (m_action == "insert") + { + m_error = m_instance->qxInsert(m_relations, (&m_db), m_bUseExecBatch); + } + else if (m_action == "update") + { + m_error = m_instance->qxUpdate(m_qxQuery, m_columns, m_relations, (&m_db), m_bUseExecBatch); + } + else if (m_action == "save") + { + m_error = m_instance->qxSave(m_relations, (&m_db), m_eSaveMode); + } + else if (m_action == "delete_by_id") + { + QVariant id; + m_error = m_instance->qxDeleteById(id, (&m_db), m_bUseExecBatch); + } + else if (m_action == "delete_all") + { + m_error = m_instance->qxDeleteAll(&m_db); + } + else if (m_action == "delete_by_query") + { + m_error = m_instance->qxDeleteByQuery(m_qxQuery, (&m_db)); + } + else if (m_action == "destroy_by_id") + { + QVariant id; + m_error = m_instance->qxDestroyById(id, (&m_db), m_bUseExecBatch); + } + else if (m_action == "destroy_all") + { + m_error = m_instance->qxDestroyAll(&m_db); + } + else if (m_action == "destroy_by_query") + { + m_error = m_instance->qxDestroyByQuery(m_qxQuery, (&m_db)); + } + else if (m_action == "exec_custom_query") + { + m_error = m_instance->qxExecuteQuery(m_qxQuery, NULL, (&m_db)); + } + else if (m_action == "exist") + { + QVariant id; + m_existResult = m_instance->qxExist(id, (&m_db)); + } + else if (m_action == "validate") + { + m_validateResult = m_instance->qxValidate(); + } + else if (m_action == "call_custom_query") + { + m_error = qx::dao::call_query(m_qxQuery, (&m_db)); + } + else if (m_action == "call_entity_function") + { + if (!callEntityFunction()) + { + return false; + } + } + else if (m_action == "get_meta_data") + { + getMetaData(); + } + else if (m_action == "get_databases") + { + getDatabases(); + } + else + { + buildError(9999, "Unknown action '" + m_action + "'"); + return false; + } + } + catch (const qx::exception &x) + { + buildError(9999, "An exception occurred executing action '" + m_action + "' : " + QString::number(x.getCode()) + " - " + x.getDescription()); + return false; + } + catch (const std::exception &e) + { + buildError(9999, "An exception occurred executing action '" + m_action + "' : " + QString(e.what())); + return false; + } + catch (...) + { + buildError(9999, "Unknown exception occurred executing action '" + m_action + "'"); + return false; + } + + if (m_error.isValid()) + { + buildError(m_error); + } + return (!m_error.isValid()); + } + + bool QxRestApi::QxRestApiImpl::buildResponse() + { + if (m_action == "get_meta_data") + { + return m_errorJson.isNull(); + } + if (m_action == "get_databases") + { + return m_errorJson.isNull(); + } + + QJsonObject response; + if (m_action == "count") + { + response.insert("count", static_cast(m_countResult)); + m_responseJson = response; + return true; + } + else if (m_action == "exist") + { + response.insert("exist", (m_existResult ? true : false)); + m_responseJson = response; + return true; + } + else if (m_action == "validate") + { + response.insert("invalid_values", qx::cvt::to_json(m_validateResult)); + m_responseJson = response; + return true; + } + else if (m_action == "call_custom_query") + { + response.insert("query_output", qx::cvt::to_json(m_qxQuery)); + m_responseJson = response; + return true; + } + else if ((m_action == "delete_all") || (m_action == "delete_by_query")) + { + response.insert("deleted", true); + m_responseJson = response; + return true; + } + else if ((m_action == "destroy_all") || (m_action == "destroy_by_query")) + { + response.insert("destroyed", true); + m_responseJson = response; + return true; + } + else if (m_action == "call_entity_function") + { + return true; + } + + QString outputFormat = (m_outputFormat.isEmpty() ? QString() : QString("filter: " + m_outputFormat.join(" | "))); + if (outputFormat.isEmpty() && ((m_action == "insert") || (m_action == "update") || (m_action == "save"))) + { + outputFormat = QX_JSON_SERIALIZE_ONLY_ID; + } + else if (outputFormat.isEmpty() && ((m_action == "delete_by_id") || (m_action == "destroy_by_id"))) + { + outputFormat = QX_JSON_SERIALIZE_ONLY_ID; + } + m_responseJson = m_instance->toJson_(outputFormat); + return true; + } + + bool QxRestApi::QxRestApiImpl::formatResponse() + { + QJsonObject response; + if (!m_requestId.isEmpty()) + { + response.insert("request_id", m_requestId); + } + response.insert("data", m_responseJson); + m_responseJson = response; + return true; + } + + qx_bool QxRestApi::QxRestApiImpl::callEntityFunction() + { + qx::any anyResponse; + std::vector anyRequest; + anyRequest.push_back(m_dataJson); + if (m_entity.isEmpty()) + { + buildError(9999, "Unable to call entity function : 'entity' field is empty"); + return qx_bool(false); + } + if (m_function.isEmpty()) + { + buildError(9999, "Unable to call entity function : 'fct' field is empty"); + return qx_bool(false); + } + if (!qx::QxClassX::getFctStatic(m_entity, m_function, true)) + { + buildError(9999, "Unable to call entity function : '" + m_entity + "::" + m_function + "' function not found (or not registered in QxOrm context)"); + return qx_bool(false); + } + qx_bool bInvokeFct = qx::QxClassX::invokeStatic(m_entity, m_function, anyRequest, (&anyResponse)); + if (bInvokeFct) + { + m_responseJson = qx::any_cast(anyResponse); + } + else + { + buildError(static_cast(bInvokeFct.getCode()), bInvokeFct.getDesc()); + } + return bInvokeFct; + } + + void QxRestApi::QxRestApiImpl::getMetaData() + { + QJsonObject response; + QxClassX::registerAllClasses(); + if (m_entity == "*") + { + QJsonArray entities; + QxCollection *pAllClasses = QxClassX::getAllClasses(); + if (!pAllClasses) + { + buildError(9999, "Unable to access to registered classes"); + return; + } + for (auto itr = pAllClasses->begin(); itr != pAllClasses->end(); ++itr) + { + IxClass *pClass = itr->second; + if (!pClass || !pClass->implementIxPersistable()) + { + continue; + } + entities.append(getMetaData(pClass)); + } + response.insert("entities", entities); + } + else + { + IxClass *pClass = QxClassX::getClass(m_entity); + if (!pClass) + { + buildError(9999, "Entity not found : " + m_entity); + return; + } + if (!pClass->implementIxPersistable()) + { + buildError(9999, "Entity doesn't implement qx::IxPersistable interface : " + m_entity); + return; + } + response.insert("entity", getMetaData(pClass)); + } + m_responseJson = response; + } + + QJsonValue QxRestApi::QxRestApiImpl::getMetaData(IxClass *pClass) + { + // Fill data about entity + QJsonObject entity; + entity.insert("key", pClass->getKey()); + entity.insert("name", pClass->getName()); + entity.insert("description", pClass->getDescription()); + entity.insert("version", static_cast(pClass->getVersion())); + entity.insert("base_entity", (pClass->getBaseClass() ? pClass->getBaseClass()->getKey() : QString())); + + // Fill primary key information + IxDataMember *pDataMemberId = pClass->getId(); + QJsonObject primaryKey; + if (pDataMemberId) + { + primaryKey.insert("key", pDataMemberId->getKey()); + primaryKey.insert("description", pDataMemberId->getDescription()); + primaryKey.insert("type", pDataMemberId->getType()); + } + entity.insert("entity_id", primaryKey); + + // Prepare arrays for properties and relations + QJsonArray properties; + QJsonArray relations; + + // Process all data members in a single loop + IxDataMemberX *pDataMemberX = pClass->getDataMemberX(); + if (pDataMemberX) + { + const long memberCount = pDataMemberX->count(); + for (long i = 0; i < memberCount; ++i) + { + IxDataMember *pDataMember = pDataMemberX->get(i); + if (!pDataMember || (pDataMember == pDataMemberId)) + { + continue; + } + + IxSqlRelation *pRelation = pDataMember->getSqlRelation(); + if (pRelation) + { + // This is a relation + QJsonObject relation; + relation.insert("key", pDataMember->getKey()); + relation.insert("description", pDataMember->getDescription()); + relation.insert("type", pDataMember->getType()); + relation.insert("type_relation", pRelation->getDescription()); + relation.insert("target", (pRelation->getClass() ? pRelation->getClass()->getKey() : QString())); + relations.append(relation); + } + else + { + // This is a regular property + QJsonObject property; + property.insert("key", pDataMember->getKey()); + property.insert("description", pDataMember->getDescription()); + property.insert("type", pDataMember->getType()); + properties.append(property); + } + } + } + + entity.insert("properties", properties); + entity.insert("relations", relations); + + return entity; + } + + void QxRestApi::QxRestApiImpl::getDatabases() + { + } + +#endif // _QX_NO_JSON + +} // namespace qx diff --git a/src/QxSerialize/QDataStream/QxSerializeQDataStream_QObject.cpp b/src/QxSerialize/QDataStream/QxSerializeQDataStream_QObject.cpp new file mode 100644 index 0000000..6063806 --- /dev/null +++ b/src/QxSerialize/QDataStream/QxSerializeQDataStream_QObject.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +QDataStream &operator<<(QDataStream &stream, const QObject &t) +{ + QList propertyKeyList = t.dynamicPropertyNames(); + qint16 iPropertyCount = static_cast(propertyKeyList.count()); + QPair pair_string_variant; + stream << iPropertyCount; + + for (qint16 i = 0; i < iPropertyCount; i++) + { + pair_string_variant.first = propertyKeyList.at(i); + pair_string_variant.second = t.property(propertyKeyList.at(i).constData()); + stream << pair_string_variant; + } + + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QObject &t) +{ + qint16 iPropertyCount = 0; + stream >> iPropertyCount; + + for (qint16 i = 0; i < iPropertyCount; i++) + { + QPair pair_string_variant; + stream >> pair_string_variant; + t.setProperty(pair_string_variant.first.constData(), pair_string_variant.second); + } + + return stream; +} diff --git a/src/QxSerialize/QDataStream/QxSerializeQDataStream_QSqlError.cpp b/src/QxSerialize/QDataStream/QxSerializeQDataStream_QSqlError.cpp new file mode 100644 index 0000000..1b3f61e --- /dev/null +++ b/src/QxSerialize/QDataStream/QxSerializeQDataStream_QSqlError.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +QDataStream &operator<<(QDataStream &stream, const QSqlError &t) +{ + QString sDatabaseText = t.databaseText(); + QString sDriverText = t.driverText(); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + qint32 iNumber = static_cast(t.nativeErrorCode().toInt()); + QString sNativeErrorCode = t.nativeErrorCode(); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + qint32 iNumber = static_cast(t.number()); + QString sNativeErrorCode = ""; +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + qint32 iType = static_cast(t.type()); + + stream << sDatabaseText; + stream << sDriverText; + stream << iNumber; + stream << iType; + stream << sNativeErrorCode; + + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QSqlError &t) +{ + QString sDatabaseText; + QString sDriverText; + qint32 iNumber(0); + qint32 iType(0); + QString sNativeErrorCode; + + stream >> sDatabaseText; + stream >> sDriverText; + stream >> iNumber; + stream >> iType; + stream >> sNativeErrorCode; + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + t = QSqlError(sDriverText, sDatabaseText, static_cast(iType), sNativeErrorCode); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + t.setDatabaseText(sDatabaseText); + t.setDriverText(sDriverText); + t.setNumber(static_cast(iNumber)); + t.setType(static_cast(iType)); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + + return stream; +} diff --git a/src/QxSerialize/QDataStream/QxSerializeQDataStream_primitive_type.cpp b/src/QxSerialize/QDataStream/QxSerializeQDataStream_primitive_type.cpp new file mode 100644 index 0000000..0fa706a --- /dev/null +++ b/src/QxSerialize/QDataStream/QxSerializeQDataStream_primitive_type.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +QDataStream &operator<<(QDataStream &stream, const long &t) +{ + qint64 tmp = static_cast(t); + stream << tmp; + return stream; +} + +QDataStream &operator>>(QDataStream &stream, long &t) +{ + qint64 tmp = 0; + stream >> tmp; + t = static_cast(tmp); + return stream; +} diff --git a/src/QxSerialize/QDataStream/QxSerializeQDataStream_qx_registered_class.cpp b/src/QxSerialize/QDataStream/QxSerializeQDataStream_qx_registered_class.cpp new file mode 100644 index 0000000..17c513e --- /dev/null +++ b/src/QxSerialize/QDataStream/QxSerializeQDataStream_qx_registered_class.cpp @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include +#include + +#include + +namespace qx +{ + + QDataStream &QxSerializeRegistered_Helper::save(QDataStream &stream, IxClass *pClass, const void *pOwner) + { + if (!pClass || !pOwner) + { + qAssert(false); + return stream; + } + stream << (quint32)(13939); + bool bJustId = false; + + if (qx::serialization::helper::QxSerializeCheckInstance::contains(pOwner, pClass)) + { + bJustId = true; + stream << bJustId; + qx::IxDataMember *pId = pClass->getId(true); + if (!pId) + { + return stream; + } + QVariant val = pId->toVariant(pOwner); + stream << val; + return stream; + } + qx::serialization::helper::QxSerializeCheckInstance checker(pOwner, pClass); + stream << bJustId; + Q_UNUSED(checker); + + do + { + qx::QxSerializeRegistered_Helper::saveHelper(stream, pClass, pOwner); + pClass = pClass->getBaseClass(); + } while (pClass != NULL); + + return stream; + } + + QDataStream &QxSerializeRegistered_Helper::load(QDataStream &stream, IxClass *pClass, void *pOwner) + { + if (!pClass || !pOwner) + { + qAssert(false); + return stream; + } + + bool bJustId = false; + quint32 magic = 0; + stream >> magic; + if ((magic != 13937) && (magic != 13939)) + { + qDebug("[QxOrm] qx::QxSerializeRegistered_Helper::load() : %s", "input binary data is not valid"); + return stream; + } + if (magic > 13937) + { + stream >> bJustId; + } + + if (bJustId) + { + qx::IxDataMember *pId = pClass->getId(true); + if (!pId) + { + return stream; + } + QVariant val; + stream >> val; + pId->fromVariant(pOwner, val); + return stream; + } + + do + { + qx::QxSerializeRegistered_Helper::loadHelper(stream, pClass, pOwner); + pClass = pClass->getBaseClass(); + } while (pClass != NULL); + + return stream; + } + + void QxSerializeRegistered_Helper::saveHelper(QDataStream &stream, IxClass *pClass, const void *pOwner) + { + IxDataMemberX *pDataMemberX = (pClass ? pClass->getDataMemberX() : NULL); + if (!pDataMemberX) + { + return; + } + qint16 iVersion = static_cast(pClass->getVersion()); + stream << iVersion; + + for (long l = 0; l < pDataMemberX->count(); l++) + { + IxDataMember *pDataMember = pDataMemberX->get(l); + if (!pDataMember || !pDataMember->getSerialize()) + { + continue; + } + if (pDataMember->getVersion() > static_cast(iVersion)) + { + qAssert(false); + continue; + } + QVariant vValue = pDataMember->toVariant(pOwner, -1, qx::cvt::context::e_serialize_registered); + stream << vValue; + } + } + + void QxSerializeRegistered_Helper::loadHelper(QDataStream &stream, IxClass *pClass, void *pOwner) + { + IxDataMemberX *pDataMemberX = (pClass ? pClass->getDataMemberX() : NULL); + if (!pDataMemberX) + { + return; + } + qint16 iVersion = 0; + stream >> iVersion; + + for (long l = 0; l < pDataMemberX->count(); l++) + { + IxDataMember *pDataMember = pDataMemberX->get(l); + if (!pDataMember || !pDataMember->getSerialize()) + { + continue; + } + if (pDataMember->getVersion() > static_cast(iVersion)) + { + continue; + } + QVariant vValue; + stream >> vValue; + pDataMember->fromVariant(pOwner, vValue, -1, qx::cvt::context::e_serialize_registered); + } + } + +} // namespace qx diff --git a/src/QxSerialize/QDataStream/QxSerializeQDataStream_std_string.cpp b/src/QxSerialize/QDataStream/QxSerializeQDataStream_std_string.cpp new file mode 100644 index 0000000..38daff3 --- /dev/null +++ b/src/QxSerialize/QDataStream/QxSerializeQDataStream_std_string.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include +#include + +#include + +QDataStream &operator<<(QDataStream &stream, const std::string &t) +{ + QString tmp = qx::cvt::detail::QxConvert_ToString::toString(t, "", 0, qx::cvt::context::e_no_context); + stream << tmp; + return stream; +} + +QDataStream &operator>>(QDataStream &stream, std::string &t) +{ + QString tmp; + stream >> tmp; + qx::cvt::detail::QxConvert_FromString::fromString(tmp, t, "", 0, qx::cvt::context::e_no_context); + return stream; +} + +QDataStream &operator<<(QDataStream &stream, const std::wstring &t) +{ + QString tmp = qx::cvt::detail::QxConvert_ToString::toString(t, "", 0, qx::cvt::context::e_no_context); + stream << tmp; + return stream; +} + +QDataStream &operator>>(QDataStream &stream, std::wstring &t) +{ + QString tmp; + stream >> tmp; + qx::cvt::detail::QxConvert_FromString::fromString(tmp, t, "", 0, qx::cvt::context::e_no_context); + return stream; +} diff --git a/src/QxSerialize/QJson/QxSerializeQJson_IxService.cpp b/src/QxSerialize/QJson/QxSerializeQJson_IxService.cpp new file mode 100644 index 0000000..84c74b9 --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_IxService.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_NETWORK + +#include + +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const qx::service::IxService &t, const QString &format) + { + QJsonObject obj; + obj.insert("service_name", QJsonValue(t.m_sServiceName)); + obj.insert("service_method", QJsonValue(t.m_sServiceMethodName)); + obj.insert("message_return", qx::cvt::to_json(t.m_bMessageReturn, format)); + + QJsonValue input; + if (t.m_pInputParameter) + { + t.m_pInputParameter->registerClass(); + QString sClassName = t.m_pInputParameter->getClassName(); + obj.insert("input_parameter_class_name", sClassName); + input = t.m_pInputParameter->saveToJson(); + } + obj.insert("input_parameter", input); + + QJsonValue output; + if (t.m_pOutputParameter) + { + t.m_pOutputParameter->registerClass(); + QString sClassName = t.m_pOutputParameter->getClassName(); + obj.insert("output_parameter_class_name", sClassName); + output = t.m_pOutputParameter->saveToJson(); + } + obj.insert("output_parameter", output); + + return QJsonValue(obj); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, qx::service::IxService &t, const QString &format) + { + t = qx::service::IxService(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + t.m_sServiceName = obj.value("service_name").toString(); + t.m_sServiceMethodName = obj.value("service_method").toString(); + qx::cvt::from_json(obj.value("message_return"), t.m_bMessageReturn, format); + + t.m_pInputParameter.reset(); + if (obj.contains("input_parameter_class_name")) + { + QString sClassName = obj.value("input_parameter_class_name").toString(); + qx::service::IxParameter *ptr = qx::create_nude_ptr(sClassName); + if (!ptr) + { + qAssertMsg(false, "[QxOrm] qx::cvt::detail::QxConvert_FromJson< qx::service::IxService >, loading QJson", "unable to create nude pointer for input parameter"); + } + else + { + ptr->registerClass(); + ptr->loadFromJson(obj.value("input_parameter")); + } + t.m_pInputParameter.reset(ptr); + } + + t.m_pOutputParameter.reset(); + if (obj.contains("output_parameter_class_name")) + { + QString sClassName = obj.value("output_parameter_class_name").toString(); + qx::service::IxParameter *ptr = qx::create_nude_ptr(sClassName); + if (!ptr) + { + qAssertMsg(false, "[QxOrm] qx::cvt::detail::QxConvert_FromJson< qx::service::IxService >, loading QJson", "unable to create nude pointer for output parameter"); + } + else + { + ptr->registerClass(); + ptr->loadFromJson(obj.value("output_parameter")); + } + t.m_pOutputParameter.reset(ptr); + } + + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_ENABLE_QT_NETWORK +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/QJson/QxSerializeQJson_IxSqlElement.cpp b/src/QxSerialize/QJson/QxSerializeQJson_IxSqlElement.cpp new file mode 100644 index 0000000..b6d3ede --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_IxSqlElement.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON + +#include +#include +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const qx::dao::detail::IxSqlElement &t, const QString &format) + { + QJsonObject obj; + obj.insert("index", QJsonValue(t.m_iIndex)); + obj.insert("list_columns", qx::cvt::to_json(t.m_lstColumns, format)); + obj.insert("list_keys", qx::cvt::to_json(t.m_lstKeys, format)); + obj.insert("list_values", qx::cvt::to_json(t.m_lstValues, format)); + obj.insert("extra_settings", QJsonValue(t.getExtraSettings())); + return QJsonValue(obj); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, qx::dao::detail::IxSqlElement &t, const QString &format) + { + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + t.m_iIndex = qRound(obj.value("index").toDouble()); + qx::cvt::from_json(obj.value("list_columns"), t.m_lstColumns, format); + qx::cvt::from_json(obj.value("list_keys"), t.m_lstKeys, format); + qx::cvt::from_json(obj.value("list_values"), t.m_lstValues, format); + t.setExtraSettings(obj.value("extra_settings").toString()); + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/QJson/QxSerializeQJson_QBrush.cpp b/src/QxSerialize/QJson/QxSerializeQJson_QBrush.cpp new file mode 100644 index 0000000..3060e16 --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_QBrush.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_GUI + +#include +#include +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const QBrush &t, const QString &format) + { + QJsonObject obj; + int iStyle = static_cast(t.style()); + obj.insert("style", qx::cvt::to_json(iStyle, format)); + QColor color(t.color()); + obj.insert("color", qx::cvt::to_json(color, format)); + QPixmap texture(t.texture()); + obj.insert("texture", qx::cvt::to_json(texture, format)); + return QJsonValue(obj); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QBrush &t, const QString &format) + { + t = QBrush(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + int iStyle = 0; + QColor color; + QPixmap texture; + qx::cvt::from_json(obj.value("style"), iStyle, format); + qx::cvt::from_json(obj.value("color"), color, format); + qx::cvt::from_json(obj.value("texture"), texture, format); + t.setStyle(static_cast(iStyle)); + ((iStyle == Qt::TexturePattern) ? t.setTexture(texture) : t.setColor(color)); + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/QJson/QxSerializeQJson_QColor.cpp b/src/QxSerialize/QJson/QxSerializeQJson_QColor.cpp new file mode 100644 index 0000000..be54104 --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_QColor.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_GUI + +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const QColor &t, const QString &format) + { + QJsonObject obj; + QColor clr = t.toRgb(); + int iR(clr.red()), iG(clr.green()), iB(clr.blue()), iA(t.alpha()); + obj.insert("red", qx::cvt::to_json(iR, format)); + obj.insert("green", qx::cvt::to_json(iG, format)); + obj.insert("blue", qx::cvt::to_json(iB, format)); + obj.insert("alpha", qx::cvt::to_json(iA, format)); + return QJsonValue(obj); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QColor &t, const QString &format) + { + t = QColor(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + int iR(0), iG(0), iB(0), iA(0); + qx::cvt::from_json(obj.value("red"), iR, format); + qx::cvt::from_json(obj.value("green"), iG, format); + qx::cvt::from_json(obj.value("blue"), iB, format); + qx::cvt::from_json(obj.value("alpha"), iA, format); + t.setRed(iR); + t.setGreen(iG); + t.setBlue(iB); + t.setAlpha(iA); + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/QJson/QxSerializeQJson_QFont.cpp b/src/QxSerialize/QJson/QxSerializeQJson_QFont.cpp new file mode 100644 index 0000000..ae79f1c --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_QFont.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_GUI + +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const QFont &t, const QString &format) + { + Q_UNUSED(format); + return QJsonValue(t.toString()); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QFont &t, const QString &format) + { + Q_UNUSED(format); + t.fromString(j.toString()); + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/QJson/QxSerializeQJson_QImage.cpp b/src/QxSerialize/QJson/QxSerializeQJson_QImage.cpp new file mode 100644 index 0000000..57c9a13 --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_QImage.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_GUI + +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const QImage &t, const QString &format) + { + if (t.isNull()) + { + return QJsonValue(); + } + QByteArray bytes; + QBuffer buffer(&bytes); + buffer.open(QIODevice::ReadWrite); + t.save(&buffer, "PNG"); + return qx::cvt::to_json(bytes, format); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QImage &t, const QString &format) + { + t = QImage(); + if (j.isNull()) + { + return qx_bool(true); + } + QByteArray bytes; + qx::cvt::from_json(j, bytes, format); + t.loadFromData(bytes); + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/QJson/QxSerializeQJson_QMatrix.cpp b/src/QxSerialize/QJson/QxSerializeQJson_QMatrix.cpp new file mode 100644 index 0000000..e0bf0ef --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_QMatrix.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_GUI + +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const QMatrix &t, const QString &format) + { + Q_UNUSED(format); + QJsonArray arr; + double m11(t.m11()), m12(t.m12()), m21(t.m21()), m22(t.m22()), dx(t.dx()), dy(t.dy()); + arr.append(QJsonValue(m11)); + arr.append(QJsonValue(m12)); + arr.append(QJsonValue(m21)); + arr.append(QJsonValue(m22)); + arr.append(QJsonValue(dx)); + arr.append(QJsonValue(dy)); + return QJsonValue(arr); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QMatrix &t, const QString &format) + { + Q_UNUSED(format); + t = QMatrix(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + double m11(arr.at(0).toDouble()); + double m12(arr.at(1).toDouble()); + double m21(arr.at(2).toDouble()); + double m22(arr.at(3).toDouble()); + double dx(arr.at(4).toDouble()); + double dy(arr.at(5).toDouble()); + t.setMatrix(m11, m12, m21, m22, dx, dy); + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_NO_JSON +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) diff --git a/src/QxSerialize/QJson/QxSerializeQJson_QObject.cpp b/src/QxSerialize/QJson/QxSerializeQJson_QObject.cpp new file mode 100644 index 0000000..0bf407c --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_QObject.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON + +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const QObject &t, const QString &format) + { + Q_UNUSED(format); + QJsonObject obj; + QList propertyKeyList = t.dynamicPropertyNames(); + + for (long l = 0; l < propertyKeyList.count(); l++) + { + QString key = propertyKeyList.at(l); + QVariant val = t.property(propertyKeyList.at(l).constData()); + obj.insert(key, QJsonValue::fromVariant(val)); + } + + return QJsonValue(obj); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QObject &t, const QString &format) + { + Q_UNUSED(format); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + + for (QJsonObject::const_iterator itr = obj.constBegin(); itr != obj.constEnd(); ++itr) + { + QString key = itr.key(); + t.setProperty(qPrintable(key), itr.value().toVariant()); + } + + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/QJson/QxSerializeQJson_QPicture.cpp b/src/QxSerialize/QJson/QxSerializeQJson_QPicture.cpp new file mode 100644 index 0000000..277b9b2 --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_QPicture.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_GUI + +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const QPicture &t, const QString &format) + { + if (t.isNull()) + { + return QJsonValue(); + } + QByteArray bytes; + QBuffer buffer(&bytes); + QPicture *pTmp = const_cast(&t); + buffer.open(QIODevice::ReadWrite); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + pTmp->save(&buffer); +#else // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + pTmp->save(&buffer, "PNG"); +#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + return qx::cvt::to_json(bytes, format); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QPicture &t, const QString &format) + { + t = QPicture(); + if (j.isNull()) + { + return qx_bool(true); + } + QByteArray bytes; + QBuffer buffer(&bytes); + buffer.open(QIODevice::ReadWrite); + qx::cvt::from_json(j, bytes, format); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + t.load(&buffer); +#else // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + t.load(&buffer, "PNG"); +#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/QJson/QxSerializeQJson_QPixmap.cpp b/src/QxSerialize/QJson/QxSerializeQJson_QPixmap.cpp new file mode 100644 index 0000000..437d28e --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_QPixmap.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_GUI + +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const QPixmap &t, const QString &format) + { + if (t.isNull()) + { + return QJsonValue(); + } + QByteArray bytes; + QBuffer buffer(&bytes); + buffer.open(QIODevice::ReadWrite); + t.save(&buffer, "PNG"); + return qx::cvt::to_json(bytes, format); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QPixmap &t, const QString &format) + { + t = QPixmap(); + if (j.isNull()) + { + return qx_bool(true); + } + QByteArray bytes; + qx::cvt::from_json(j, bytes, format); + t.loadFromData(bytes); + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/QJson/QxSerializeQJson_QPoint.cpp b/src/QxSerialize/QJson/QxSerializeQJson_QPoint.cpp new file mode 100644 index 0000000..47d9d18 --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_QPoint.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON + +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const QPoint &t, const QString &format) + { + Q_UNUSED(format); + QJsonArray arr; + arr.append(QJsonValue(t.x())); + arr.append(QJsonValue(t.y())); + return QJsonValue(arr); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QPoint &t, const QString &format) + { + Q_UNUSED(format); + t = QPoint(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + t.setX(qRound(arr.at(0).toDouble())); + t.setY(qRound(arr.at(1).toDouble())); + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/QJson/QxSerializeQJson_QRect.cpp b/src/QxSerialize/QJson/QxSerializeQJson_QRect.cpp new file mode 100644 index 0000000..08333e0 --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_QRect.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON + +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const QRect &t, const QString &format) + { + Q_UNUSED(format); + QJsonArray arr; + arr.append(QJsonValue(t.left())); + arr.append(QJsonValue(t.right())); + arr.append(QJsonValue(t.top())); + arr.append(QJsonValue(t.bottom())); + return QJsonValue(arr); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QRect &t, const QString &format) + { + Q_UNUSED(format); + t = QRect(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + t.setLeft(qRound(arr.at(0).toDouble())); + t.setRight(qRound(arr.at(1).toDouble())); + t.setTop(qRound(arr.at(2).toDouble())); + t.setBottom(qRound(arr.at(3).toDouble())); + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/QJson/QxSerializeQJson_QRegExp.cpp b/src/QxSerialize/QJson/QxSerializeQJson_QRegExp.cpp new file mode 100644 index 0000000..e109364 --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_QRegExp.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#ifndef _QX_NO_JSON + +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const QRegExp &t, const QString &format) + { + Q_UNUSED(format); + QJsonArray arr; + arr.append(QJsonValue(t.pattern())); + arr.append(QJsonValue(static_cast(t.caseSensitivity()))); + arr.append(QJsonValue(static_cast(t.patternSyntax()))); + arr.append(QJsonValue(t.isMinimal())); + return QJsonValue(arr); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QRegExp &t, const QString &format) + { + Q_UNUSED(format); + t = QRegExp(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + t.setPattern(arr.at(0).toString()); + t.setCaseSensitivity(static_cast(qRound(arr.at(1).toDouble()))); + t.setPatternSyntax(static_cast(qRound(arr.at(2).toDouble()))); + t.setMinimal(arr.at(3).toBool()); + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_NO_JSON +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) diff --git a/src/QxSerialize/QJson/QxSerializeQJson_QRegion.cpp b/src/QxSerialize/QJson/QxSerializeQJson_QRegion.cpp new file mode 100644 index 0000000..ef73142 --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_QRegion.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_GUI + +#include +#include +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const QRegion &t, const QString &format) + { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + QVector rectList(t.begin(), t.end()); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + QVector rectList = t.rects(); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + return qx::cvt::to_json(rectList, format); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QRegion &t, const QString &format) + { + QVector rectList; + qx::cvt::from_json(j, rectList, format); + t.setRects(rectList.data(), rectList.count()); + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/QJson/QxSerializeQJson_QSize.cpp b/src/QxSerialize/QJson/QxSerializeQJson_QSize.cpp new file mode 100644 index 0000000..86c709c --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_QSize.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON + +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const QSize &t, const QString &format) + { + Q_UNUSED(format); + QJsonArray arr; + arr.append(QJsonValue(t.width())); + arr.append(QJsonValue(t.height())); + return QJsonValue(arr); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QSize &t, const QString &format) + { + Q_UNUSED(format); + t = QSize(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + t.setWidth(qRound(arr.at(0).toDouble())); + t.setHeight(qRound(arr.at(1).toDouble())); + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/QJson/QxSerializeQJson_QSqlError.cpp b/src/QxSerialize/QJson/QxSerializeQJson_QSqlError.cpp new file mode 100644 index 0000000..d4172df --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_QSqlError.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON + +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const QSqlError &t, const QString &format) + { + Q_UNUSED(format); + QJsonArray arr; + arr.append(QJsonValue(t.databaseText())); + arr.append(QJsonValue(t.driverText())); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + arr.append(QJsonValue(t.nativeErrorCode())); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + arr.append(QJsonValue(t.number())); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + arr.append(QJsonValue(static_cast(t.type()))); + return QJsonValue(arr); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QSqlError &t, const QString &format) + { + Q_UNUSED(format); + t = QSqlError(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + t = QSqlError(arr.at(1).toString(), arr.at(0).toString(), static_cast(qRound(arr.at(3).toDouble())), arr.at(2).toString()); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + t.setDatabaseText(arr.at(0).toString()); + t.setDriverText(arr.at(1).toString()); + t.setNumber(qRound(arr.at(2).toDouble())); + t.setType(static_cast(qRound(arr.at(3).toDouble()))); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/QJson/QxSerializeQJson_QStringList.cpp b/src/QxSerialize/QJson/QxSerializeQJson_QStringList.cpp new file mode 100644 index 0000000..fb7e798 --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_QStringList.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON + +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const QStringList &t, const QString &format) + { + QJsonArray arr; + for (int i = 0; i < t.count(); i++) + { + arr.append(qx::cvt::to_json(t.at(i), format)); + } + return QJsonValue(arr); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, QStringList &t, const QString &format) + { + t.clear(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + +#if (QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) + t.reserve(arr.count()); +#endif // (QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) + + for (int i = 0; i < arr.count(); i++) + { + QString tmp; + qx::cvt::from_json(arr.at(i), tmp, format); + t.append(tmp); + } + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/QJson/QxSerializeQJson_QxInvalidValue.cpp b/src/QxSerialize/QJson/QxSerializeQJson_QxInvalidValue.cpp new file mode 100644 index 0000000..7c11243 --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_QxInvalidValue.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON + +#include +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const qx::QxInvalidValue &t, const QString &format) + { + QJsonObject obj; + obj.insert("message", t.m_sMessage); + if (!t.m_sPropertyName.isEmpty()) + { + obj.insert("property_name", t.m_sPropertyName); + } + if (!t.m_sPath.isEmpty()) + { + obj.insert("path", t.m_sPath); + } + if ((t.m_lstPropertyBag) && (t.m_lstPropertyBag->count() > 0)) + { + obj.insert("property_bag", qx::cvt::to_json(t.m_lstPropertyBag, format)); + } + return obj; + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, qx::QxInvalidValue &t, const QString &format) + { + t = qx::QxInvalidValue(); + + if (j.isArray()) + { + QJsonArray arr = j.toArray(); + t.m_sMessage = arr.at(0).toString(); + t.m_sPropertyName = arr.at(1).toString(); + t.m_sPath = arr.at(2).toString(); + qx::cvt::from_json(arr.at(3), t.m_lstPropertyBag, format); + } + else if (j.isObject()) + { + QJsonObject obj = j.toObject(); + t.m_sMessage = obj.value("message").toString(); + t.m_sPropertyName = obj.value("property_name").toString(); + t.m_sPath = obj.value("path").toString(); + qx::cvt::from_json(obj.value("property_bag"), t.m_lstPropertyBag, format); + } + + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/QJson/QxSerializeQJson_QxInvalidValueX.cpp b/src/QxSerialize/QJson/QxSerializeQJson_QxInvalidValueX.cpp new file mode 100644 index 0000000..4577c5b --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_QxInvalidValueX.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON + +#include +#include +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const qx::QxInvalidValueX &t, const QString &format) + { + if (t.m_lstInvalidValues.count() <= 0) + { + return QJsonValue(); + } + + QJsonArray arr; + arr.append(QJsonValue(t.m_sCurrentPath)); + arr.append(qx::cvt::to_json(t.m_lstInvalidValues, format)); + return QJsonValue(arr); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, qx::QxInvalidValueX &t, const QString &format) + { + t = qx::QxInvalidValueX(); + if (!j.isArray()) + { + return qx_bool(true); + } + QJsonArray arr = j.toArray(); + t.m_sCurrentPath = arr.at(0).toString(); + qx::cvt::from_json(arr.at(1), t.m_lstInvalidValues, format); + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/QJson/QxSerializeQJson_QxSqlQuery.cpp b/src/QxSerialize/QJson/QxSerializeQJson_QxSqlQuery.cpp new file mode 100644 index 0000000..45767c6 --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_QxSqlQuery.cpp @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const qx::QxSqlQuery &t, const QString &format) + { + QJsonObject obj; + QJsonArray arr; + QHash lstResultPosByKey; + QVector> lstResultValues; + qx::dao::detail::IxSqlElement::type_class eNoSqlType = qx::dao::detail::IxSqlElement::_no_type; + + if (t.m_pSqlResult) + { + lstResultPosByKey = t.m_pSqlResult->positionByKey; + lstResultValues = t.m_pSqlResult->values; + } + + obj.insert("query", qx::cvt::to_json(t.m_sQuery)); + obj.insert("list_values", qx::cvt::to_json(t.m_lstValue, format)); + obj.insert("sql_element_index", QJsonValue(t.m_iSqlElementIndex)); + obj.insert("parenthesis_count", QJsonValue(t.m_iParenthesisCount)); + obj.insert("distinct", QJsonValue(t.m_bDistinct)); + obj.insert("result_position_by_key", qx::cvt::to_json(lstResultPosByKey, format)); + obj.insert("result_values", qx::cvt::to_json(lstResultValues, format)); + obj.insert("response", QJsonValue(t.m_vResponse.toString())); + obj.insert("type", QJsonValue(t.m_sType)); + obj.insert("list_join_query_user", qx::cvt::to_json(t.m_lstJoinQueryUser, format)); + obj.insert("list_join_query_resolve", qx::cvt::to_json(t.m_lstJoinQueryToResolve, format)); + + if (!t.m_pSqlElementTemp) + { + obj.insert("sql_element_temp_type", QJsonValue(static_cast(eNoSqlType))); + } + else + { + qx::dao::detail::IxSqlElement::type_class eTypeSqlElement = t.m_pSqlElementTemp->getTypeClass(); + obj.insert("sql_element_temp_type", QJsonValue(static_cast(eTypeSqlElement))); + obj.insert("sql_element_temp", qx::cvt::to_json((*t.m_pSqlElementTemp), format)); + } + + Q_FOREACH (qx::dao::detail::IxSqlElement_ptr pSqlElement, t.m_lstSqlElement) + { + QJsonObject item; + if (!pSqlElement) + { + item.insert("sql_element_type", QJsonValue(static_cast(eNoSqlType))); + arr.append(QJsonValue(item)); + continue; + } + qx::dao::detail::IxSqlElement::type_class eTypeSqlElement = pSqlElement->getTypeClass(); + item.insert("sql_element_type", QJsonValue(static_cast(eTypeSqlElement))); + item.insert("sql_element", qx::cvt::to_json((*pSqlElement), format)); + arr.append(QJsonValue(item)); + } + obj.insert("sql_element_list", QJsonValue(arr)); + + return QJsonValue(obj); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, qx::QxSqlQuery &t, const QString &format) + { + t = qx::QxSqlQuery(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + QHash lstResultPosByKey; + QVector> lstResultValues; + + if (obj.contains("sql")) + { + QJsonValue jsonSql = obj.value("sql"); + if (jsonSql.isString()) + { + t.m_sQuery.clear(); + t.m_sQuery << jsonSql.toString(); + } + } + else + { + qx::cvt::from_json(obj.value("query"), t.m_sQuery); + } + + if (obj.contains("params")) + { + QJsonArray arrParams = obj.value("params").toArray(); + for (int i = 0; i < arrParams.count(); i++) + { + QJsonValue objParam = arrParams.at(i); + if (!objParam.isObject()) + { + continue; + } + QJsonObject jsonParam = objParam.toObject(); + QSql::ParamType jsonParamType = QSql::In; + if (jsonParam.contains("type")) + { + QString paramType = jsonParam.value("type").toString(); + if (paramType == "in") + { + jsonParamType = QSql::In; + } + else if (paramType == "out") + { + jsonParamType = QSql::Out; + } + else if (paramType == "in_out") + { + jsonParamType = QSql::InOut; + } + else if (paramType == "binary") + { + jsonParamType = QSql::Binary; + } + } + QString jsonParamKey; + QVariant jsonParamValue; + qx::cvt::from_json(jsonParam.value("key"), jsonParamKey); + qx::cvt::from_json(jsonParam.value("value"), jsonParamValue); + if (jsonParamKey.isEmpty()) + { + t.bind(jsonParamValue, jsonParamType); + } + else + { + t.bind(jsonParamKey, jsonParamValue, jsonParamType); + } + } + } + else + { + qx::cvt::from_json(obj.value("list_values"), t.m_lstValue, format); + } + + t.m_iSqlElementIndex = qRound(obj.value("sql_element_index").toDouble()); + t.m_iParenthesisCount = qRound(obj.value("parenthesis_count").toDouble()); + t.m_bDistinct = obj.value("distinct").toBool(); + qx::cvt::from_json(obj.value("result_position_by_key"), lstResultPosByKey, format); + qx::cvt::from_json(obj.value("result_values"), lstResultValues, format); + t.m_vResponse = obj.value("response").toVariant(); + t.m_sType = obj.value("type").toString(); + qx::cvt::from_json(obj.value("list_join_query_user"), t.m_lstJoinQueryUser, format); + qx::cvt::from_json(obj.value("list_join_query_resolve"), t.m_lstJoinQueryToResolve, format); + + t.m_pSqlResult.reset(); + if ((lstResultPosByKey.count() > 0) || (lstResultValues.count() > 0)) + { + t.m_pSqlResult = std::shared_ptr(new qx::QxSqlQuery::QxSqlResult()); + t.m_pSqlResult->positionByKey = lstResultPosByKey; + t.m_pSqlResult->values = lstResultValues; + } + + t.m_pSqlElementTemp.reset(); + qx::dao::detail::IxSqlElement::type_class eTypeSqlElement = qx::dao::detail::IxSqlElement::_no_type; + eTypeSqlElement = static_cast(qRound(obj.value("sql_element_temp_type").toDouble())); + if (eTypeSqlElement != qx::dao::detail::IxSqlElement::_no_type) + { + t.m_pSqlElementTemp = qx::dao::detail::create_sql_element(eTypeSqlElement); + qAssert(t.m_pSqlElementTemp); + if (t.m_pSqlElementTemp) + { + qx::cvt::from_json(obj.value("sql_element_temp"), (*t.m_pSqlElementTemp), format); + } + } + + t.m_lstSqlElement.clear(); + QJsonArray arr = obj.value("sql_element_list").toArray(); + for (int i = 0; i < arr.count(); i++) + { + QJsonValue val = arr.at(i); + if (!val.isObject()) + { + continue; + } + QJsonObject item = val.toObject(); + qx::dao::detail::IxSqlElement_ptr pSqlElement; + eTypeSqlElement = qx::dao::detail::IxSqlElement::_no_type; + eTypeSqlElement = static_cast(qRound(item.value("sql_element_type").toDouble())); + if (eTypeSqlElement != qx::dao::detail::IxSqlElement::_no_type) + { + pSqlElement = qx::dao::detail::create_sql_element(eTypeSqlElement); + qAssert(pSqlElement); + if (pSqlElement) + { + qx::cvt::from_json(item.value("sql_element"), (*pSqlElement), format); + } + } + t.m_lstSqlElement.append(pSqlElement); + } + + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/QJson/QxSerializeQJson_QxTransaction.cpp b/src/QxSerialize/QJson/QxSerializeQJson_QxTransaction.cpp new file mode 100644 index 0000000..f8a3793 --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_QxTransaction.cpp @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON +#ifdef _QX_ENABLE_QT_NETWORK + +#include + +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + QJsonValue QxConvert_ToJson_Helper(const qx::service::QxTransaction &t, const QString &format) + { + QJsonObject obj; + obj.insert("transaction_id", QJsonValue(t.m_sTransactionId)); + obj.insert("input_transaction_size", QJsonValue(static_cast(t.m_uiInputTransactionSize))); + obj.insert("output_transaction_size", QJsonValue(static_cast(t.m_uiOutputTransactionSize))); + obj.insert("dt_transaction_begin", qx::cvt::to_json(t.m_dtTransactionBegin, format)); + obj.insert("dt_transaction_request_sent", qx::cvt::to_json(t.m_dtTransactionRequestSent, format)); + obj.insert("dt_transaction_request_received", qx::cvt::to_json(t.m_dtTransactionRequestReceived, format)); + obj.insert("dt_transaction_reply_sent", qx::cvt::to_json(t.m_dtTransactionReplySent, format)); + obj.insert("dt_transaction_reply_received", qx::cvt::to_json(t.m_dtTransactionReplyReceived, format)); + obj.insert("dt_transaction_end", qx::cvt::to_json(t.m_dtTransactionEnd, format)); + obj.insert("ip_source", QJsonValue(t.m_sIpSource)); + obj.insert("ip_target", QJsonValue(t.m_sIpTarget)); + obj.insert("port_source", QJsonValue(static_cast(t.m_lPortSource))); + obj.insert("port_target", QJsonValue(static_cast(t.m_lPortTarget))); + obj.insert("service_name", QJsonValue(t.m_sServiceName)); + obj.insert("service_method", QJsonValue(t.m_sServiceMethod)); + obj.insert("message_return", qx::cvt::to_json(t.m_bMessageReturn, format)); + + QJsonValue input; + if (t.m_pInputParameter) + { + t.m_pInputParameter->registerClass(); + QString sClassName = t.m_pInputParameter->getClassName(); + obj.insert("input_parameter_class_name", sClassName); + input = t.m_pInputParameter->saveToJson(); + } + obj.insert("input_parameter", input); + + QJsonValue output; + if (t.m_pOutputParameter) + { + t.m_pOutputParameter->registerClass(); + QString sClassName = t.m_pOutputParameter->getClassName(); + obj.insert("output_parameter_class_name", sClassName); + output = t.m_pOutputParameter->saveToJson(); + } + obj.insert("output_parameter", output); + + return QJsonValue(obj); + } + + qx_bool QxConvert_FromJson_Helper(const QJsonValue &j, qx::service::QxTransaction &t, const QString &format) + { + t.clear(); + if (!j.isObject()) + { + return qx_bool(true); + } + QJsonObject obj = j.toObject(); + t.m_sTransactionId = obj.value("transaction_id").toString(); + t.m_uiInputTransactionSize = static_cast(qRound64(obj.value("input_transaction_size").toDouble())); + t.m_uiOutputTransactionSize = static_cast(qRound64(obj.value("output_transaction_size").toDouble())); + qx::cvt::from_json(obj.value("dt_transaction_begin"), t.m_dtTransactionBegin, format); + qx::cvt::from_json(obj.value("dt_transaction_request_sent"), t.m_dtTransactionRequestSent, format); + qx::cvt::from_json(obj.value("dt_transaction_request_received"), t.m_dtTransactionRequestReceived, format); + qx::cvt::from_json(obj.value("dt_transaction_reply_sent"), t.m_dtTransactionReplySent, format); + qx::cvt::from_json(obj.value("dt_transaction_reply_received"), t.m_dtTransactionReplyReceived, format); + qx::cvt::from_json(obj.value("dt_transaction_end"), t.m_dtTransactionEnd, format); + t.m_sIpSource = obj.value("ip_source").toString(); + t.m_sIpTarget = obj.value("ip_target").toString(); + t.m_lPortSource = static_cast(qRound64(obj.value("port_source").toDouble())); + t.m_lPortTarget = static_cast(qRound64(obj.value("port_target").toDouble())); + t.m_sServiceName = obj.value("service_name").toString(); + t.m_sServiceMethod = obj.value("service_method").toString(); + qx::cvt::from_json(obj.value("message_return"), t.m_bMessageReturn, format); + + t.m_pInputParameter.reset(); + if (obj.contains("input_parameter_class_name")) + { + QString sClassName = obj.value("input_parameter_class_name").toString(); + qx::service::IxParameter *ptr = qx::create_nude_ptr(sClassName); + if (!ptr) + { + qAssertMsg(false, "[QxOrm] qx::cvt::detail::QxConvert_FromJson< qx::service::QxTransaction >, loading QJson", "unable to create nude pointer for input parameter"); + } + else + { + ptr->registerClass(); + ptr->loadFromJson(obj.value("input_parameter")); + } + t.m_pInputParameter.reset(ptr); + } + + t.m_pOutputParameter.reset(); + if (obj.contains("output_parameter_class_name")) + { + QString sClassName = obj.value("output_parameter_class_name").toString(); + qx::service::IxParameter *ptr = qx::create_nude_ptr(sClassName); + if (!ptr) + { + qAssertMsg(false, "[QxOrm] qx::cvt::detail::QxConvert_FromJson< qx::service::QxTransaction >, loading QJson", "unable to create nude pointer for output parameter"); + } + else + { + ptr->registerClass(); + ptr->loadFromJson(obj.value("output_parameter")); + } + t.m_pOutputParameter.reset(ptr); + } + + return qx_bool(true); + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_ENABLE_QT_NETWORK +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/QJson/QxSerializeQJson_qx_registered_class.cpp b/src/QxSerialize/QJson/QxSerializeQJson_qx_registered_class.cpp new file mode 100644 index 0000000..c6c3ea2 --- /dev/null +++ b/src/QxSerialize/QJson/QxSerializeQJson_qx_registered_class.cpp @@ -0,0 +1,570 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifndef _QX_NO_JSON + +#include +#include + +#include + +#include + +#include + +namespace qx +{ + namespace cvt + { + namespace detail + { + + void QxSerializeJsonRegistered_saveHelper(QJsonObject &obj, IxClass *pClass, const void *pOwner, const QString &format); + void QxSerializeJsonRegistered_saveHelper_MongoDB(QJsonObject &obj, IxClass *pClass, const void *pOwner, const QString &format); + void QxSerializeJsonRegistered_saveHelper_WithFilter(QJsonObject &obj, IxClass *pClass, const void *pOwner, const QString &format); + + void QxSerializeJsonRegistered_loadHelper(const QJsonObject &obj, IxClass *pClass, void *pOwner, const QString &format); + void QxSerializeJsonRegistered_loadHelper_MongoDB(const QJsonObject &obj, IxClass *pClass, void *pOwner, const QString &format); + void QxSerializeJsonRegistered_loadHelper_WithFilter(const QJsonObject &obj, IxClass *pClass, void *pOwner, const QString &format); + + qx_bool QxSerializeJsonRegistered_initHierarchy_WithFilter(IxClass *pClass, const void *pOwner, const QString &format); + + QJsonValue QxSerializeJsonRegistered_Helper::save(IxClass *pClass, const void *pOwner, const QString &format) + { + if (!pClass || !pOwner) + { + qAssert(false); + return QJsonValue(); + } + bool bOnlyId = ((!format.isEmpty()) && ((format == QX_JSON_SERIALIZE_ONLY_ID) || (format == "mongodb:only_id") || (format == "mongodb:relation_id"))); + bool bCheckInstance = qx::serialization::helper::QxSerializeCheckInstance::contains(pOwner, pClass); + QJsonObject obj; + + if (bCheckInstance || bOnlyId) + { + qx::IxDataMember *pId = pClass->getId(true); + if (!pId) + { + return QJsonValue(); + } + QString key = ((format == "mongodb:only_id") ? QString("_id") : pId->getKey()); + QJsonValue val = pId->toJson(pOwner, format); + if (format == "mongodb:relation_id") + { + return val; + } + obj.insert(key, val); + return QJsonValue(obj); + } + + bool bMongoDB = format.startsWith("mongodb"); + bool bWithFilter = format.startsWith("filter:"); + qx_bool bHierarchyOk = (bWithFilter ? QxSerializeJsonRegistered_initHierarchy_WithFilter(pClass, pOwner, format) : qx_bool(true)); + if (!bHierarchyOk) + { + obj.insert("error", bHierarchyOk.getDesc()); + return QJsonValue(obj); + } + qx::serialization::helper::QxSerializeCheckInstance checker(pOwner, pClass); + Q_UNUSED(checker); + + do + { + if (bMongoDB) + { + qx::cvt::detail::QxSerializeJsonRegistered_saveHelper_MongoDB(obj, pClass, pOwner, format); + } + else if (bWithFilter) + { + qx::cvt::detail::QxSerializeJsonRegistered_saveHelper_WithFilter(obj, pClass, pOwner, format); + break; + } + else + { + qx::cvt::detail::QxSerializeJsonRegistered_saveHelper(obj, pClass, pOwner, format); + } + pClass = pClass->getBaseClass(); + } while (pClass != NULL); + + return QJsonValue(obj); + } + + qx_bool QxSerializeJsonRegistered_Helper::load(const QJsonValue &j, IxClass *pClass, void *pOwner, const QString &format) + { + if (!pClass || !pOwner) + { + qAssert(false); + return qx_bool(true); + } + if (!j.isObject()) + { + qx::IxDataMember *pId = pClass->getId(true); + if (!pId) + { + return qx_bool(true); + } + return pId->fromJson(pOwner, j, format); + } + + bool bMongoDB = format.startsWith("mongodb"); + bool bWithFilter = format.startsWith("filter:"); + QJsonObject obj = j.toObject(); + + do + { + if (bMongoDB) + { + qx::cvt::detail::QxSerializeJsonRegistered_loadHelper_MongoDB(obj, pClass, pOwner, format); + } + else if (bWithFilter) + { + qx::cvt::detail::QxSerializeJsonRegistered_loadHelper_WithFilter(obj, pClass, pOwner, format); + break; + } + else + { + qx::cvt::detail::QxSerializeJsonRegistered_loadHelper(obj, pClass, pOwner, format); + } + pClass = pClass->getBaseClass(); + } while (pClass != NULL); + + return qx_bool(true); + } + + qx_bool QxSerializeJsonRegistered_initHierarchy_WithFilter(IxClass *pClass, const void *pOwner, const QString &format) + { + qx_bool bHierarchyOk(true); + Q_UNUSED(pOwner); + if (!qx::serialization::helper::QxSerializeCheckInstance::isRoot()) + { + return bHierarchyOk; + } + std::shared_ptr pRelationLinked = qx::QxSqlRelationLinked::getHierarchy(pClass, format.right(format.size() - 7).split("|"), bHierarchyOk); + if (!bHierarchyOk || !pRelationLinked) + { + return qx_bool(false, bHierarchyOk.getDesc()); + } + + QString empty; + qx::serialization::helper::QxSerializeCheckInstance::type_hierarchy hierarchy = qMakePair(pRelationLinked, empty); + qx::serialization::helper::QxSerializeCheckInstance::setHierarchy(hierarchy); + return bHierarchyOk; + } + + void QxSerializeJsonRegistered_saveHelper(QJsonObject &obj, IxClass *pClass, const void *pOwner, const QString &format) + { + qx::IxDataMemberX *pDataMemberX = (pClass ? pClass->getDataMemberX() : NULL); + if (!pDataMemberX) + { + return; + } + + for (long l = 0; l < pDataMemberX->count(); l++) + { + qx::IxDataMember *pDataMember = pDataMemberX->get(l); + if (!pDataMember || !pDataMember->getSerialize()) + { + continue; + } + QJsonValue val = pDataMember->toJson(pOwner, format); + obj.insert(pDataMember->getKey(), val); + } + } + + void QxSerializeJsonRegistered_saveHelper_MongoDB(QJsonObject &obj, IxClass *pClass, const void *pOwner, const QString &format) + { + qx::IxDataMemberX *pDataMemberX = (pClass ? pClass->getDataMemberX() : NULL); + if (!pDataMemberX) + { + return; + } + bool bMongoDBColumns = (format.contains(":columns{") && format.contains("}")); + bool bMongoDBChild = format.startsWith("mongodb:child"); + + for (long l = 0; l < pDataMemberX->count(); l++) + { + qx::IxDataMember *pDataMember = pDataMemberX->get(l); + if (!pDataMember || !pDataMember->getSerialize() || !pDataMember->getDao()) + { + continue; + } + if (bMongoDBColumns && !pDataMember->getIsPrimaryKey() && !format.contains("," + pDataMember->getKey() + ",")) + { + continue; + } + qx::IxSqlRelation *pRelation = pDataMember->getSqlRelation(); + qx::IxSqlRelation::relation_type eRelationType = (pRelation ? pRelation->getRelationType() : qx::IxSqlRelation::no_relation); + if ((eRelationType == qx::IxSqlRelation::one_to_many) || (eRelationType == qx::IxSqlRelation::many_to_many)) + { + continue; + } + QString key = ((!bMongoDBChild && pDataMember->getIsPrimaryKey()) ? QString("_id") : pDataMember->getKey()); + QString formatTmp = (pRelation ? QString("mongodb:relation_id") : (QString("mongodb:child:") + format)); + if (pDataMember->getIsPrimaryKey()) + { + QVariant id = pDataMember->toVariant(pOwner); + if (!qx::trait::is_valid_primary_key(id)) + { + continue; + } + } + QJsonValue val = pDataMember->toJson(pOwner, formatTmp); + obj.insert(key, val); + } + } + + void QxSerializeJsonRegistered_saveHelper_WithFilter(QJsonObject &obj, IxClass *pClass, const void *pOwner, const QString &format) + { + qx::serialization::helper::QxSerializeCheckInstance::type_hierarchy currHierarchy = qx::serialization::helper::QxSerializeCheckInstance::getHierarchy(); + std::shared_ptr pRelationLinked = currHierarchy.first; + QString sRelation = currHierarchy.second; + + if (!pRelationLinked || !pClass) + { + QString msg = ("hierarchy (relations linked) not found : " + sRelation); + qAssertMsg(false, "QxSerializeJsonRegistered_saveHelper_WithFilter", qPrintable(msg)); + return; + } + qx::QxSqlRelationLinked::type_lst_relation pRelations = pRelationLinked->getRelationX(); + + qx::IxDataMember *pDataMemberId = pClass->getId(true); + if (pDataMemberId) + { + QJsonValue val = pDataMemberId->toJson(pOwner, format); + obj.insert(pDataMemberId->getKey(), val); + } + + if (pRelationLinked->isRoot() && sRelation.isEmpty()) + { + std::shared_ptr> pDataMemberX = pClass->getSqlDataMemberX(); + if (!pDataMemberX) + { + return; + } + for (long l = 0; l < pDataMemberX->count(); l++) + { + qx::IxDataMember *pDataMember = pDataMemberX->getByIndex(l); + if (!pDataMember || !pDataMember->getSerialize()) + { + continue; + } + if (!pRelationLinked->checkRootColumns(pDataMember->getKey())) + { + continue; + } + QJsonValue val = pDataMember->toJson(pOwner, format); + obj.insert(pDataMember->getKey(), val); + } + + for (auto itr = pRelations.begin(); itr != pRelations.end(); ++itr) + { + const qx::QxSqlRelationLinked::type_relation &item = itr->second; + qx::IxSqlRelation *pRelation = std::get<1>(item); + if (!pRelation) + { + continue; + } + qx::IxDataMember *pDataMember = pRelation->getDataMember(); + if (!pDataMember || !pDataMember->getSerialize()) + { + continue; + } + qx::serialization::helper::QxSerializeCheckInstance::type_hierarchy nextHierarchy = qMakePair(pRelationLinked, itr->first); + qx::serialization::helper::QxSerializeCheckInstance::setHierarchy(nextHierarchy); + QJsonValue val = pDataMember->toJson(pOwner, format); + obj.insert(pDataMember->getKey(), val); + } + + std::shared_ptr pNullRelationLinked; + QString empty; + qx::serialization::helper::QxSerializeCheckInstance::type_hierarchy nullHierarchy = qMakePair(pNullRelationLinked, empty); + qx::serialization::helper::QxSerializeCheckInstance::setHierarchy(nullHierarchy); + } + else if (pRelations.exist(sRelation)) + { + const qx::QxSqlRelationLinked::type_relation &currRelation = pRelations.getByKey(sRelation); + qx::IxSqlRelation *pRelation = std::get<1>(currRelation); + if (!pRelation) + { + return; + } + QSet columns = std::get<2>(currRelation).first; + long l = 0; + qx::IxDataMember *pDataMember = NULL; + while ((pDataMember = pRelation->nextData(l))) + { + if (!pDataMember || !pDataMember->getSerialize()) + { + continue; + } + if ((columns.count() > 0) && (!columns.contains(pDataMember->getKey()))) + { + continue; + } + QJsonValue val = pDataMember->toJson(pOwner, format); + obj.insert(pDataMember->getKey(), val); + } + + qx::QxSqlRelationLinked::type_lst_relation_linked allSubRelationsLinked = pRelationLinked->getRelationLinkedX(); + qx::QxSqlRelationLinked::type_ptr pSubRelationLinked = allSubRelationsLinked.value(pRelation->getKey()); + if (!pSubRelationLinked) + { + return; + } + qx::QxSqlRelationLinked::type_lst_relation pSubRelations = pSubRelationLinked->getRelationX(); + for (auto itr = pSubRelations.begin(); itr != pSubRelations.end(); ++itr) + { + const qx::QxSqlRelationLinked::type_relation &item = itr->second; + qx::IxSqlRelation *pSubRelation = std::get<1>(item); + if (!pSubRelation) + { + continue; + } + pDataMember = pSubRelation->getDataMember(); + if (!pDataMember || !pDataMember->getSerialize()) + { + continue; + } + qx::serialization::helper::QxSerializeCheckInstance::type_hierarchy nextHierarchy = qMakePair(pSubRelationLinked, itr->first); + qx::serialization::helper::QxSerializeCheckInstance::setHierarchy(nextHierarchy); + QJsonValue val = pDataMember->toJson(pOwner, format); + obj.insert(pDataMember->getKey(), val); + qx::serialization::helper::QxSerializeCheckInstance::setHierarchy(currHierarchy); + } + } + else + { + qAssertMsg(false, "QxSerializeJsonRegistered_saveHelper_WithFilter", "unknown error"); + } + } + + void QxSerializeJsonRegistered_loadHelper(const QJsonObject &obj, IxClass *pClass, void *pOwner, const QString &format) + { + qx::IxDataMemberX *pDataMemberX = (pClass ? pClass->getDataMemberX() : NULL); + if (!pDataMemberX) + { + return; + } + + for (long l = 0; l < pDataMemberX->count(); l++) + { + qx::IxDataMember *pDataMember = pDataMemberX->get(l); + if (!pDataMember || !pDataMember->getSerialize()) + { + continue; + } + QString key = pDataMember->getKey(); + if (obj.contains(key)) + { + pDataMember->fromJson(pOwner, obj.value(key), format); + } + } + } + + void QxSerializeJsonRegistered_loadHelper_MongoDB(const QJsonObject &obj, IxClass *pClass, void *pOwner, const QString &format) + { + qx::IxDataMemberX *pDataMemberX = (pClass ? pClass->getDataMemberX() : NULL); + if (!pDataMemberX) + { + return; + } + bool bMongoDBColumns = (format.contains(":columns{") && format.contains("}")); + bool bMongoDBChild = format.startsWith("mongodb:child"); + + for (long l = 0; l < pDataMemberX->count(); l++) + { + qx::IxDataMember *pDataMember = pDataMemberX->get(l); + if (!pDataMember || !pDataMember->getSerialize() || !pDataMember->getDao()) + { + continue; + } + if (bMongoDBColumns && !pDataMember->getIsPrimaryKey() && !format.contains("," + pDataMember->getKey() + ",")) + { + continue; + } + QString key = ((!bMongoDBChild && pDataMember->getIsPrimaryKey()) ? QString("_id") : pDataMember->getKey()); + key = ((bMongoDBChild && pDataMember->getIsPrimaryKey() && (!obj.contains(key))) ? QString("_id") : key); + if (obj.contains(key)) + { + pDataMember->fromJson(pOwner, obj.value(key), (QString("mongodb:child:") + format)); + } + } + } + + void QxSerializeJsonRegistered_loadHelper_WithFilter(const QJsonObject &obj, IxClass *pClass, void *pOwner, const QString &format) + { + qx_bool bHierarchyOk = QxSerializeJsonRegistered_initHierarchy_WithFilter(pClass, pOwner, format); + if (!bHierarchyOk) + { + QString msg = bHierarchyOk.getDesc(); + qDebug("[QxOrm] !!! Error in QxSerializeJsonRegistered_loadHelper_WithFilter !!! : '%s'", qPrintable(msg)); + return; + } + qx::serialization::helper::QxSerializeCheckInstance checker(pOwner, pClass); + Q_UNUSED(checker); + + qx::serialization::helper::QxSerializeCheckInstance::type_hierarchy currHierarchy = qx::serialization::helper::QxSerializeCheckInstance::getHierarchy(); + std::shared_ptr pRelationLinked = currHierarchy.first; + QString sRelation = currHierarchy.second; + + if (!pRelationLinked || !pClass) + { + QString msg = ("hierarchy (relations linked) not found : " + sRelation); + qAssertMsg(false, "QxSerializeJsonRegistered_loadHelper_WithFilter", qPrintable(msg)); + return; + } + qx::QxSqlRelationLinked::type_lst_relation pRelations = pRelationLinked->getRelationX(); + + qx::IxDataMember *pDataMemberId = pClass->getId(true); + if (pDataMemberId && obj.contains(pDataMemberId->getKey())) + { + pDataMemberId->fromJson(pOwner, obj.value(pDataMemberId->getKey()), format); + } + + if (pRelationLinked->isRoot() && sRelation.isEmpty()) + { + std::shared_ptr> pDataMemberX = pClass->getSqlDataMemberX(); + if (!pDataMemberX) + { + return; + } + for (long l = 0; l < pDataMemberX->count(); l++) + { + qx::IxDataMember *pDataMember = pDataMemberX->getByIndex(l); + if (!pDataMember || !pDataMember->getSerialize()) + { + continue; + } + if (!pRelationLinked->checkRootColumns(pDataMember->getKey())) + { + continue; + } + if (obj.contains(pDataMember->getKey())) + { + pDataMember->fromJson(pOwner, obj.value(pDataMember->getKey()), format); + } + } + + for (auto itr = pRelations.begin(); itr != pRelations.end(); ++itr) + { + const qx::QxSqlRelationLinked::type_relation &item = itr->second; + qx::IxSqlRelation *pRelation = std::get<1>(item); + if (!pRelation) + { + continue; + } + qx::IxDataMember *pDataMember = pRelation->getDataMember(); + if (!pDataMember || !pDataMember->getSerialize()) + { + continue; + } + qx::serialization::helper::QxSerializeCheckInstance::type_hierarchy nextHierarchy = qMakePair(pRelationLinked, itr->first); + qx::serialization::helper::QxSerializeCheckInstance::setHierarchy(nextHierarchy); + if (obj.contains(pDataMember->getKey())) + { + pDataMember->fromJson(pOwner, obj.value(pDataMember->getKey()), format); + } + } + + std::shared_ptr pNullRelationLinked; + QString empty; + qx::serialization::helper::QxSerializeCheckInstance::type_hierarchy nullHierarchy = qMakePair(pNullRelationLinked, empty); + qx::serialization::helper::QxSerializeCheckInstance::setHierarchy(nullHierarchy); + } + else if (pRelations.exist(sRelation)) + { + const qx::QxSqlRelationLinked::type_relation &currRelation = pRelations.getByKey(sRelation); + qx::IxSqlRelation *pRelation = std::get<1>(currRelation); + if (!pRelation) + { + return; + } + QSet columns = std::get<2>(currRelation).first; + long l = 0; + qx::IxDataMember *pDataMember = NULL; + while ((pDataMember = pRelation->nextData(l))) + { + if (!pDataMember || !pDataMember->getSerialize()) + { + continue; + } + if ((columns.count() > 0) && (!columns.contains(pDataMember->getKey()))) + { + continue; + } + if (obj.contains(pDataMember->getKey())) + { + pDataMember->fromJson(pOwner, obj.value(pDataMember->getKey()), format); + } + } + + qx::QxSqlRelationLinked::type_lst_relation_linked allSubRelationsLinked = pRelationLinked->getRelationLinkedX(); + qx::QxSqlRelationLinked::type_ptr pSubRelationLinked = allSubRelationsLinked.value(pRelation->getKey()); + if (!pSubRelationLinked) + { + return; + } + qx::QxSqlRelationLinked::type_lst_relation pSubRelations = pSubRelationLinked->getRelationX(); + for (auto itr = pSubRelations.begin(); itr != pSubRelations.end(); ++itr) + { + const qx::QxSqlRelationLinked::type_relation &item = itr->second; + qx::IxSqlRelation *pSubRelation = std::get<1>(item); + if (!pSubRelation) + { + continue; + } + pDataMember = pSubRelation->getDataMember(); + if (!pDataMember || !pDataMember->getSerialize()) + { + continue; + } + qx::serialization::helper::QxSerializeCheckInstance::type_hierarchy nextHierarchy = qMakePair(pSubRelationLinked, itr->first); + qx::serialization::helper::QxSerializeCheckInstance::setHierarchy(nextHierarchy); + if (obj.contains(pDataMember->getKey())) + { + pDataMember->fromJson(pOwner, obj.value(pDataMember->getKey()), format); + } + qx::serialization::helper::QxSerializeCheckInstance::setHierarchy(currHierarchy); + } + } + else + { + qAssertMsg(false, "QxSerializeJsonRegistered_loadHelper_WithFilter", "unknown error"); + } + } + + } // namespace detail + } // namespace cvt +} // namespace qx + +#endif // _QX_NO_JSON diff --git a/src/QxSerialize/Qt/QxSerialize_QBrush.cpp b/src/QxSerialize/Qt/QxSerialize_QBrush.cpp new file mode 100644 index 0000000..5c24381 --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QBrush.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#ifdef _QX_ENABLE_QT_GUI + +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QBrush &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + int iStyle = static_cast(t.style()); + QColor color(t.color()); + QPixmap texture(t.texture()); + + ar << boost::serialization::make_nvp("style", iStyle); + ar << boost::serialization::make_nvp("color", color); + ar << boost::serialization::make_nvp("texture", texture); + } + + template + inline void qx_load(Archive &ar, QBrush &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + int iStyle = 0; + QColor color; + QPixmap texture; + + ar >> boost::serialization::make_nvp("style", iStyle); + ar >> boost::serialization::make_nvp("color", color); + ar >> boost::serialization::make_nvp("texture", texture); + + t.setStyle(static_cast(iStyle)); + ((iStyle == Qt::TexturePattern) ? t.setTexture(texture) : t.setColor(color)); + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QBrush) + +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QByteArray.cpp b/src/QxSerialize/Qt/QxSerialize_QByteArray.cpp new file mode 100644 index 0000000..cd6d27f --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QByteArray.cpp @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QByteArray &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = t.count(); + std::string data(t.constData(), lCount); + ar << boost::serialization::make_nvp("size", lCount); + ar << boost::serialization::make_nvp("data", data); + } + + template + inline void qx_load(Archive &ar, QByteArray &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = 0; + std::string data; + t.clear(); + ar >> boost::serialization::make_nvp("size", lCount); + ar >> boost::serialization::make_nvp("data", data); + if (lCount > 0) + { + t = QByteArray(data.c_str(), lCount); + } + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QByteArray) + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QColor.cpp b/src/QxSerialize/Qt/QxSerialize_QColor.cpp new file mode 100644 index 0000000..f8ecb6c --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QColor.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#ifdef _QX_ENABLE_QT_GUI + +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QColor &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QColor clr = t.toRgb(); + int iR(clr.red()), iG(clr.green()), iB(clr.blue()), iA(t.alpha()); + + ar << boost::serialization::make_nvp("R", iR); + ar << boost::serialization::make_nvp("G", iG); + ar << boost::serialization::make_nvp("B", iB); + ar << boost::serialization::make_nvp("A", iA); + } + + template + inline void qx_load(Archive &ar, QColor &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + int iR(0), iG(0), iB(0), iA(0); + + ar >> boost::serialization::make_nvp("R", iR); + ar >> boost::serialization::make_nvp("G", iG); + ar >> boost::serialization::make_nvp("B", iB); + ar >> boost::serialization::make_nvp("A", iA); + + t.setRed(iR); + t.setGreen(iG); + t.setBlue(iB); + t.setAlpha(iA); + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QColor) + +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QDate.cpp b/src/QxSerialize/Qt/QxSerialize_QDate.cpp new file mode 100644 index 0000000..ceddf63 --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QDate.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QDate &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QString sDate = t.toString(QX_SERIALIZE_QDATE_FORMAT); + ar << boost::serialization::make_nvp(NULL, sDate); + } + + template + inline void qx_load(Archive &ar, QDate &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QString sDate; + ar >> boost::serialization::make_nvp(NULL, sDate); + t = QDate::fromString(sDate, QX_SERIALIZE_QDATE_FORMAT); + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QDate) + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QDateTime.cpp b/src/QxSerialize/Qt/QxSerialize_QDateTime.cpp new file mode 100644 index 0000000..dc79a80 --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QDateTime.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QDateTime &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QString sDateTime = t.toString(QX_SERIALIZE_QDATETIME_FORMAT); + ar << boost::serialization::make_nvp(NULL, sDateTime); + } + + template + inline void qx_load(Archive &ar, QDateTime &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QString sDateTime; + ar >> boost::serialization::make_nvp(NULL, sDateTime); + t = QDateTime::fromString(sDateTime, QX_SERIALIZE_QDATETIME_FORMAT); + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QDateTime) + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QFont.cpp b/src/QxSerialize/Qt/QxSerialize_QFont.cpp new file mode 100644 index 0000000..9949b89 --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QFont.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#ifdef _QX_ENABLE_QT_GUI + +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QFont &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QString sFontAttr = t.toString(); + ar << boost::serialization::make_nvp(NULL, sFontAttr); + } + + template + inline void qx_load(Archive &ar, QFont &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QString sFontAttr; + ar >> boost::serialization::make_nvp(NULL, sFontAttr); + t.fromString(sFontAttr); + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QFont) + +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QImage.cpp b/src/QxSerialize/Qt/QxSerialize_QImage.cpp new file mode 100644 index 0000000..bd4faff --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QImage.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#ifdef _QX_ENABLE_QT_GUI + +#include + +#include + +QX_BOOST_EXPORT_SERIALIZATION_FAST_COMPIL_CPP(QImage) + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QImage &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + bool bIsNull = t.isNull(); + ar << boost::serialization::make_nvp("null", bIsNull); + if (bIsNull) + { + return; + } + + QByteArray bytes; + QBuffer buffer(&bytes); + buffer.open(QIODevice::ReadWrite); + t.save(&buffer, "PNG"); + ar << boost::serialization::make_nvp("bytes", bytes); + } + + template + inline void qx_load(Archive &ar, QImage &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + t = QImage(); + bool bIsNull(false); + ar >> boost::serialization::make_nvp("null", bIsNull); + if (bIsNull) + { + return; + } + + QByteArray bytes; + ar >> boost::serialization::make_nvp("bytes", bytes); + t.loadFromData(bytes); + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QImage) + +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QMatrix.cpp b/src/QxSerialize/Qt/QxSerialize_QMatrix.cpp new file mode 100644 index 0000000..b5d8017 --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QMatrix.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#ifdef _QX_ENABLE_QT_GUI + +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QMatrix &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + double m11(t.m11()), m12(t.m12()), m21(t.m21()), m22(t.m22()), dx(t.dx()), dy(t.dy()); + + ar << boost::serialization::make_nvp("m11", m11); + ar << boost::serialization::make_nvp("m12", m12); + ar << boost::serialization::make_nvp("m21", m21); + ar << boost::serialization::make_nvp("m22", m22); + ar << boost::serialization::make_nvp("dx", dx); + ar << boost::serialization::make_nvp("dy", dy); + } + + template + inline void qx_load(Archive &ar, QMatrix &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + double m11(0.0), m12(0.0), m21(0.0), m22(0.0), dx(0.0), dy(0.0); + + ar >> boost::serialization::make_nvp("m11", m11); + ar >> boost::serialization::make_nvp("m12", m12); + ar >> boost::serialization::make_nvp("m21", m21); + ar >> boost::serialization::make_nvp("m22", m22); + ar >> boost::serialization::make_nvp("dx", dx); + ar >> boost::serialization::make_nvp("dy", dy); + + t.setMatrix(m11, m12, m21, m22, dx, dy); + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QMatrix) + +#endif // _QX_ENABLE_QT_GUI +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QObject.cpp b/src/QxSerialize/Qt/QxSerialize_QObject.cpp new file mode 100644 index 0000000..fb2ea0b --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QObject.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#include + +#include + +QX_BOOST_EXPORT_SERIALIZATION_FAST_COMPIL_CPP(QObject) + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QObject &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QList propertyKeyList = t.dynamicPropertyNames(); + int iPropertyCount = propertyKeyList.count(); + std::pair pair_string_variant; + ar << boost::serialization::make_nvp("propertyCount", iPropertyCount); + + for (long l = 0; l < iPropertyCount; l++) + { + pair_string_variant.first = propertyKeyList.at(l).constData(); + pair_string_variant.second = t.property(pair_string_variant.first.c_str()); + ar << boost::serialization::make_nvp("property", pair_string_variant); + } + } + + template + inline void qx_load(Archive &ar, QObject &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + int iPropertyCount = 0; + std::pair pair_string_variant; + ar >> boost::serialization::make_nvp("propertyCount", iPropertyCount); + + for (long l = 0; l < iPropertyCount; l++) + { + ar >> boost::serialization::make_nvp("property", pair_string_variant); + t.setProperty(pair_string_variant.first.c_str(), pair_string_variant.second); + } + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QObject) +QX_REGISTER_BOOST_SERIALIZE_HELPER_CPP(QObject) + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QPicture.cpp b/src/QxSerialize/Qt/QxSerialize_QPicture.cpp new file mode 100644 index 0000000..ed14fd2 --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QPicture.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#ifdef _QX_ENABLE_QT_GUI + +#include + +#include + +QX_BOOST_EXPORT_SERIALIZATION_FAST_COMPIL_CPP(QPicture) + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QPicture &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + bool bIsNull = t.isNull(); + ar << boost::serialization::make_nvp("null", bIsNull); + if (bIsNull) + { + return; + } + + QByteArray bytes; + QBuffer buffer(&bytes); + QPicture *pTmp = const_cast(&t); + buffer.open(QIODevice::ReadWrite); + pTmp->save(&buffer, "PNG"); + ar << boost::serialization::make_nvp("bytes", bytes); + } + + template + inline void qx_load(Archive &ar, QPicture &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + t = QPicture(); + bool bIsNull(false); + ar >> boost::serialization::make_nvp("null", bIsNull); + if (bIsNull) + { + return; + } + + QByteArray bytes; + QBuffer buffer(&bytes); + buffer.open(QIODevice::ReadWrite); + ar >> boost::serialization::make_nvp("bytes", bytes); + t.load(&buffer, "PNG"); + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QPicture) + +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QPixmap.cpp b/src/QxSerialize/Qt/QxSerialize_QPixmap.cpp new file mode 100644 index 0000000..cc5e1ec --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QPixmap.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#ifdef _QX_ENABLE_QT_GUI + +#include + +#include + +QX_BOOST_EXPORT_SERIALIZATION_FAST_COMPIL_CPP(QPixmap) + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QPixmap &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + bool bIsNull = t.isNull(); + ar << boost::serialization::make_nvp("null", bIsNull); + if (bIsNull) + { + return; + } + + QByteArray bytes; + QBuffer buffer(&bytes); + buffer.open(QIODevice::ReadWrite); + t.save(&buffer, "PNG"); + ar << boost::serialization::make_nvp("bytes", bytes); + } + + template + inline void qx_load(Archive &ar, QPixmap &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + t = QPixmap(); + bool bIsNull(false); + ar >> boost::serialization::make_nvp("null", bIsNull); + if (bIsNull) + { + return; + } + + QByteArray bytes; + ar >> boost::serialization::make_nvp("bytes", bytes); + t.loadFromData(bytes); + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QPixmap) + +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QPoint.cpp b/src/QxSerialize/Qt/QxSerialize_QPoint.cpp new file mode 100644 index 0000000..e6d1de4 --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QPoint.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void qx_serialize(Archive &ar, QPoint &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("x", t.rx()); + ar &boost::serialization::make_nvp("y", t.ry()); + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SERIALIZE_CPP(QPoint) + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QRect.cpp b/src/QxSerialize/Qt/QxSerialize_QRect.cpp new file mode 100644 index 0000000..f84a554 --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QRect.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QRect &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + int iLeft(t.left()), iRight(t.right()), iTop(t.top()), iBottom(t.bottom()); + + ar << boost::serialization::make_nvp("left", iLeft); + ar << boost::serialization::make_nvp("right", iRight); + ar << boost::serialization::make_nvp("top", iTop); + ar << boost::serialization::make_nvp("bottom", iBottom); + } + + template + inline void qx_load(Archive &ar, QRect &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + int iLeft(0), iRight(0), iTop(0), iBottom(0); + + ar >> boost::serialization::make_nvp("left", iLeft); + ar >> boost::serialization::make_nvp("right", iRight); + ar >> boost::serialization::make_nvp("top", iTop); + ar >> boost::serialization::make_nvp("bottom", iBottom); + + t.setTop(iTop); + t.setBottom(iBottom); + t.setLeft(iLeft); + t.setRight(iRight); + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QRect) + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QRegExp.cpp b/src/QxSerialize/Qt/QxSerialize_QRegExp.cpp new file mode 100644 index 0000000..4d6b2b9 --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QRegExp.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QRegExp &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QString sPattern = t.pattern(); + int iCaseSensitivity = static_cast(t.caseSensitivity()); + int iPatternSyntax = static_cast(t.patternSyntax()); + bool bMinimal = t.isMinimal(); + + ar << boost::serialization::make_nvp("pattern", sPattern); + ar << boost::serialization::make_nvp("caseSensitivity", iCaseSensitivity); + ar << boost::serialization::make_nvp("patternSyntax", iPatternSyntax); + ar << boost::serialization::make_nvp("minimal", bMinimal); + } + + template + inline void qx_load(Archive &ar, QRegExp &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QString sPattern; + bool bMinimal(false); + int iCaseSensitivity(0), iPatternSyntax(0); + + ar >> boost::serialization::make_nvp("pattern", sPattern); + ar >> boost::serialization::make_nvp("caseSensitivity", iCaseSensitivity); + ar >> boost::serialization::make_nvp("patternSyntax", iPatternSyntax); + ar >> boost::serialization::make_nvp("minimal", bMinimal); + + t.setPattern(sPattern); + t.setCaseSensitivity(static_cast(iCaseSensitivity)); + t.setPatternSyntax(static_cast(iPatternSyntax)); + t.setMinimal(bMinimal); + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QRegExp) + +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QRegion.cpp b/src/QxSerialize/Qt/QxSerialize_QRegion.cpp new file mode 100644 index 0000000..b9a09d3 --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QRegion.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#ifdef _QX_ENABLE_QT_GUI + +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QRegion &t, const unsigned int file_version) + { + Q_UNUSED(file_version); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + QVector rectList(t.begin(), t.end()); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + QVector rectList = t.rects(); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + ar << boost::serialization::make_nvp("rectList", rectList); + } + + template + inline void qx_load(Archive &ar, QRegion &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QVector rectList; + ar >> boost::serialization::make_nvp("rectList", rectList); + t.setRects(rectList.data(), rectList.count()); + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QRegion) + +#endif // _QX_ENABLE_QT_GUI +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QSize.cpp b/src/QxSerialize/Qt/QxSerialize_QSize.cpp new file mode 100644 index 0000000..d6c89b8 --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QSize.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void qx_serialize(Archive &ar, QSize &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + ar &boost::serialization::make_nvp("width", t.rwidth()); + ar &boost::serialization::make_nvp("height", t.rheight()); + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SERIALIZE_CPP(QSize) + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QSqlError.cpp b/src/QxSerialize/Qt/QxSerialize_QSqlError.cpp new file mode 100644 index 0000000..ae7c62b --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QSqlError.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QSqlError &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QString sDatabaseText = t.databaseText(); + QString sDriverText = t.driverText(); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + int iNumber = t.nativeErrorCode().toInt(); + QString sNativeErrorCode = t.nativeErrorCode(); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + int iNumber = t.number(); + QString sNativeErrorCode = ""; +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + int iType = static_cast(t.type()); + + ar << boost::serialization::make_nvp("database_text", sDatabaseText); + ar << boost::serialization::make_nvp("driver_text", sDriverText); + ar << boost::serialization::make_nvp("number", iNumber); + ar << boost::serialization::make_nvp("type", iType); + ar << boost::serialization::make_nvp("native_error_code", sNativeErrorCode); + } + + template + inline void qx_load(Archive &ar, QSqlError &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QString sDatabaseText; + QString sDriverText; + int iNumber(0); + int iType(0); + QString sNativeErrorCode; + + ar >> boost::serialization::make_nvp("database_text", sDatabaseText); + ar >> boost::serialization::make_nvp("driver_text", sDriverText); + ar >> boost::serialization::make_nvp("number", iNumber); + ar >> boost::serialization::make_nvp("type", iType); + ar >> boost::serialization::make_nvp("native_error_code", sNativeErrorCode); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + t = QSqlError(sDriverText, sDatabaseText, static_cast(iType), sNativeErrorCode); +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + t.setDatabaseText(sDatabaseText); + t.setDriverText(sDriverText); + t.setNumber(iNumber); + t.setType(static_cast(iType)); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QSqlError) + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QString.cpp b/src/QxSerialize/Qt/QxSerialize_QString.cpp new file mode 100644 index 0000000..d55d2df --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QString.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#include + +#include +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + struct qx_serialize_qstring + { + + template + static inline void save(Archive &ar, const QString &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + char cell, row; + long lSize = t.size(); + ar << boost::serialization::make_nvp("size", lSize); + const QChar *qcharPtr = t.constData(); + for (long l = 0; l < lSize; ++l) + { + cell = static_cast(qcharPtr[l].cell()); + row = static_cast(qcharPtr[l].row()); + ar << boost::serialization::make_nvp(NULL, cell); + ar << boost::serialization::make_nvp(NULL, row); + } + } + + template + static inline void load(Archive &ar, QString &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + char cell, row; + long lSize = 0; + ar >> boost::serialization::make_nvp("size", lSize); + t.clear(); + t.reserve(lSize); + for (long l = 0; l < lSize; ++l) + { + ar >> boost::serialization::make_nvp(NULL, cell); + ar >> boost::serialization::make_nvp(NULL, row); + t.append(QChar(cell, row)); + } + } + }; + + template <> + struct qx_serialize_qstring + { + + template + static inline void save(Archive &ar, const QString &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + typedef typename qx::trait::archive_wide_traits::type_string qx_type_string_tmp; + qx_type_string_tmp str; + qx::trait::archive_wide_traits::fromQString(t, str); + ar << boost::serialization::make_nvp(NULL, str); + } + + template + static inline void load(Archive &ar, QString &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + typedef typename qx::trait::archive_wide_traits::type_string qx_type_string_tmp; + qx_type_string_tmp str; + ar >> boost::serialization::make_nvp(NULL, str); + t = qx::trait::archive_wide_traits::toQString(str); + } + }; + + template + inline void qx_save(Archive &ar, const QString &t, const unsigned int file_version) + { + qx_serialize_qstring::value>::save(ar, t, file_version); + } + + template + inline void qx_load(Archive &ar, QString &t, const unsigned int file_version) + { + qx_serialize_qstring::value>::load(ar, t, file_version); + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QString) + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QStringList.cpp b/src/QxSerialize/Qt/QxSerialize_QStringList.cpp new file mode 100644 index 0000000..1471a98 --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QStringList.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QStringList &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = t.count(); + ar << boost::serialization::make_nvp("count", lCount); + + for (long l = 0; l < lCount; l++) + ar << boost::serialization::make_nvp("item", t.at(l)); + } + + template + inline void qx_load(Archive &ar, QStringList &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + long lCount = 0; + ar >> boost::serialization::make_nvp("count", lCount); + + t.clear(); + QString item; + + for (long l = 0; l < lCount; l++) + { + ar >> boost::serialization::make_nvp("item", item); + t.append(item); + } + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QStringList) + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QTime.cpp b/src/QxSerialize/Qt/QxSerialize_QTime.cpp new file mode 100644 index 0000000..d69047f --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QTime.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QTime &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QString sTime = t.toString(QX_SERIALIZE_QTIME_FORMAT); + ar << boost::serialization::make_nvp(NULL, sTime); + } + + template + inline void qx_load(Archive &ar, QTime &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QString sTime; + ar >> boost::serialization::make_nvp(NULL, sTime); + t = QTime::fromString(sTime, QX_SERIALIZE_QTIME_FORMAT); + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QTime) + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QUrl.cpp b/src/QxSerialize/Qt/QxSerialize_QUrl.cpp new file mode 100644 index 0000000..c4c0e11 --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QUrl.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QUrl &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QString sUrl = t.toString(); + ar << boost::serialization::make_nvp(NULL, sUrl); + } + + template + inline void qx_load(Archive &ar, QUrl &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QString sUrl; + ar >> boost::serialization::make_nvp(NULL, sUrl); + t = QUrl(sUrl); + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QUrl) + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QUuid.cpp b/src/QxSerialize/Qt/QxSerialize_QUuid.cpp new file mode 100644 index 0000000..13a8633 --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QUuid.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#include +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QUuid &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QString sUuid = t.toString(); + ar << boost::serialization::make_nvp("uuid", sUuid); + } + + template + inline void qx_load(Archive &ar, QUuid &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QString sUuid; + ar >> boost::serialization::make_nvp("uuid", sUuid); + t = QUuid(sUuid); + } + + } // namespace boost +} // namespace serialization + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QUuid) + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/Qt/QxSerialize_QVariant.cpp b/src/QxSerialize/Qt/QxSerialize_QVariant.cpp new file mode 100644 index 0000000..15f4585 --- /dev/null +++ b/src/QxSerialize/Qt/QxSerialize_QVariant.cpp @@ -0,0 +1,463 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#include + +#include + +#include + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const QVariant &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + int iType = static_cast(t.type()); + ar << boost::serialization::make_nvp("type", iType); + const char *sTag = "value"; + + if (iType == QVariant::Bool) + { + bool b(false); + b = t.toBool(); + ar << boost::serialization::make_nvp(sTag, b); + } + else if (iType == QVariant::Int) + { + int i(0); + i = t.toInt(); + ar << boost::serialization::make_nvp(sTag, i); + } + else if (iType == QVariant::UInt) + { + uint ui(0); + ui = t.toUInt(); + ar << boost::serialization::make_nvp(sTag, ui); + } + else if (iType == QVariant::LongLong) + { + long l(0); + l = static_cast(t.toLongLong()); + ar << boost::serialization::make_nvp(sTag, l); + } + else if (iType == QVariant::ULongLong) + { + unsigned long ul(0); + ul = static_cast(t.toULongLong()); + ar << boost::serialization::make_nvp(sTag, ul); + } + else if (iType == QVariant::Double) + { + double d(0.0); + d = t.toDouble(); + ar << boost::serialization::make_nvp(sTag, d); + } + else if (iType == QVariant::String) + { + QString s; + s = t.toString(); + ar << boost::serialization::make_nvp(sTag, s); + } + else if (iType == QVariant::Date) + { + QDate qdate; + qdate = t.toDate(); + ar << boost::serialization::make_nvp(sTag, qdate); + } + else if (iType == QVariant::DateTime) + { + QDateTime qdatetime; + qdatetime = t.toDateTime(); + ar << boost::serialization::make_nvp(sTag, qdatetime); + } + else if (iType == QVariant::Time) + { + QTime qtime; + qtime = t.toTime(); + ar << boost::serialization::make_nvp(sTag, qtime); + } + else if (iType == QVariant::ByteArray) + { + QByteArray qbytearray; + qbytearray = t.toByteArray(); + ar << boost::serialization::make_nvp(sTag, qbytearray); + } + else if (iType == QVariant::Point) + { + QPoint qpoint; + qpoint = t.toPoint(); + ar << boost::serialization::make_nvp(sTag, qpoint); + } + else if (iType == QVariant::Rect) + { + QRect qrect; + qrect = t.toRect(); + ar << boost::serialization::make_nvp(sTag, qrect); + } + else if (iType == QVariant::Size) + { + QSize qsize; + qsize = t.toSize(); + ar << boost::serialization::make_nvp(sTag, qsize); + } +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + else if (iType == QVariant::RegExp) + { + QRegExp qregexp; + qregexp = t.toRegExp(); + ar << boost::serialization::make_nvp(sTag, qregexp); + } +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + else if (iType == QVariant::Url) + { + QUrl qurl; + qurl = t.toUrl(); + ar << boost::serialization::make_nvp(sTag, qurl); + } +#ifdef _QX_ENABLE_QT_GUI + else if (iType == QVariant::Brush) + { + QBrush qbrush; + qbrush = t.value(); + ar << boost::serialization::make_nvp(sTag, qbrush); + } /* if you crash on this line, please check in your main() function if you have created a QApplication and not a QCoreApplication ! */ + else if (iType == QVariant::Color) + { + QColor qcolor; + qcolor = t.value(); + ar << boost::serialization::make_nvp(sTag, qcolor); + } /* if you crash on this line, please check in your main() function if you have created a QApplication and not a QCoreApplication ! */ + else if (iType == QVariant::Font) + { + QFont qfont; + qfont = t.value(); + ar << boost::serialization::make_nvp(sTag, qfont); + } /* if you crash on this line, please check in your main() function if you have created a QApplication and not a QCoreApplication ! */ + else if (iType == QVariant::Image) + { + QImage qimage; + qimage = t.value(); + ar << boost::serialization::make_nvp(sTag, qimage); + } /* if you crash on this line, please check in your main() function if you have created a QApplication and not a QCoreApplication ! */ + else if (iType == QVariant::Pixmap) + { + QPixmap qpixmap; + qpixmap = t.value(); + ar << boost::serialization::make_nvp(sTag, qpixmap); + } /* if you crash on this line, please check in your main() function if you have created a QApplication and not a QCoreApplication ! */ + else if (iType == QVariant::Region) + { + QRegion qregion; + qregion = t.value(); + ar << boost::serialization::make_nvp(sTag, qregion); + } /* if you crash on this line, please check in your main() function if you have created a QApplication and not a QCoreApplication ! */ +#endif // _QX_ENABLE_QT_GUI + else if (iType == QVariant::Invalid) + { + QString sInvalid; + ar << boost::serialization::make_nvp(sTag, sInvalid); + } + else if (iType == QMetaType::Long) + { + long l(0); + l = static_cast(t.toLongLong()); + ar << boost::serialization::make_nvp(sTag, l); + } + else if (iType == QMetaType::Short) + { + int i(0); + i = t.toInt(); + ar << boost::serialization::make_nvp(sTag, i); + } + else if (iType == QMetaType::Char) + { + int i(0); + i = t.toInt(); + ar << boost::serialization::make_nvp(sTag, i); + } + else if (iType == QMetaType::ULong) + { + unsigned long ul(0); + ul = static_cast(t.toULongLong()); + ar << boost::serialization::make_nvp(sTag, ul); + } + else if (iType == QMetaType::UShort) + { + uint ui(0); + ui = t.toUInt(); + ar << boost::serialization::make_nvp(sTag, ui); + } + else if (iType == QMetaType::UChar) + { + uint ui(0); + ui = t.toUInt(); + ar << boost::serialization::make_nvp(sTag, ui); + } + else if (iType == QMetaType::Float) + { + double d(0.0); + d = t.toDouble(); + ar << boost::serialization::make_nvp(sTag, d); + } + else + { + QString sUserType("unknown variant serialize type"); + qx::QxClassX::type_fct_save_qvariant_usertype fct; + fct = qx::QxClassX::getFctSaveQVariantUserType(); + if (fct) + { + sUserType = fct(t, iType, file_version); + } + ar << boost::serialization::make_nvp(sTag, sUserType); + } + } + + template + inline void qx_load(Archive &ar, QVariant &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + int iType = 0; + ar >> boost::serialization::make_nvp("type", iType); + const char *sTag = "value"; + + if (iType == QVariant::Bool) + { + bool b(false); + ar >> boost::serialization::make_nvp(sTag, b); + t = QVariant(b); + } + else if (iType == QVariant::Int) + { + int i(0); + ar >> boost::serialization::make_nvp(sTag, i); + t = QVariant(i); + } + else if (iType == QVariant::UInt) + { + uint ui(0); + ar >> boost::serialization::make_nvp(sTag, ui); + t = QVariant(ui); + } + else if (iType == QVariant::LongLong) + { + long l(0); + ar >> boost::serialization::make_nvp(sTag, l); + t = QVariant(static_cast(l)); + } + else if (iType == QVariant::ULongLong) + { + unsigned long ul(0); + ar >> boost::serialization::make_nvp(sTag, ul); + t = QVariant(static_cast(ul)); + } + else if (iType == QVariant::Double) + { + double d(0.0); + ar >> boost::serialization::make_nvp(sTag, d); + t = QVariant(d); + } + else if (iType == QVariant::String) + { + QString s; + ar >> boost::serialization::make_nvp(sTag, s); + t = QVariant(s); + } + else if (iType == QVariant::Date) + { + QDate qdate; + ar >> boost::serialization::make_nvp(sTag, qdate); + t = QVariant(qdate); + } + else if (iType == QVariant::DateTime) + { + QDateTime qdatetime; + ar >> boost::serialization::make_nvp(sTag, qdatetime); + t = QVariant(qdatetime); + } + else if (iType == QVariant::Time) + { + QTime qtime; + ar >> boost::serialization::make_nvp(sTag, qtime); + t = QVariant(qtime); + } + else if (iType == QVariant::ByteArray) + { + QByteArray qbytearray; + ar >> boost::serialization::make_nvp(sTag, qbytearray); + t = QVariant(qbytearray); + } + else if (iType == QVariant::Point) + { + QPoint qpoint; + ar >> boost::serialization::make_nvp(sTag, qpoint); + t = QVariant(qpoint); + } + else if (iType == QVariant::Rect) + { + QRect qrect; + ar >> boost::serialization::make_nvp(sTag, qrect); + t = QVariant(qrect); + } + else if (iType == QVariant::Size) + { + QSize qsize; + ar >> boost::serialization::make_nvp(sTag, qsize); + t = QVariant(qsize); + } + else if (iType == QVariant::RegExp) + { + QRegExp qregexp; + ar >> boost::serialization::make_nvp(sTag, qregexp); + t = QVariant(qregexp); + } + else if (iType == QVariant::Url) + { + QUrl qurl; + ar >> boost::serialization::make_nvp(sTag, qurl); + t = QVariant(qurl); + } +#ifdef _QX_ENABLE_QT_GUI + else if (iType == QVariant::Brush) + { + QBrush qbrush; + ar >> boost::serialization::make_nvp(sTag, qbrush); + t = qbrush; + } /* if you crash on this line, please check in your main() function if you have created a QApplication and not a QCoreApplication ! */ + else if (iType == QVariant::Color) + { + QColor qcolor; + ar >> boost::serialization::make_nvp(sTag, qcolor); + t = qcolor; + } /* if you crash on this line, please check in your main() function if you have created a QApplication and not a QCoreApplication ! */ + else if (iType == QVariant::Font) + { + QFont qfont; + ar >> boost::serialization::make_nvp(sTag, qfont); + t = qfont; + } /* if you crash on this line, please check in your main() function if you have created a QApplication and not a QCoreApplication ! */ + else if (iType == QVariant::Image) + { + QImage qimage; + ar >> boost::serialization::make_nvp(sTag, qimage); + t = qimage; + } /* if you crash on this line, please check in your main() function if you have created a QApplication and not a QCoreApplication ! */ + else if (iType == QVariant::Pixmap) + { + QPixmap qpixmap; + ar >> boost::serialization::make_nvp(sTag, qpixmap); + t = qpixmap; + } /* if you crash on this line, please check in your main() function if you have created a QApplication and not a QCoreApplication ! */ + else if (iType == QVariant::Region) + { + QRegion qregion; + ar >> boost::serialization::make_nvp(sTag, qregion); + t = qregion; + } /* if you crash on this line, please check in your main() function if you have created a QApplication and not a QCoreApplication ! */ +#endif // _QX_ENABLE_QT_GUI + else if (iType == QVariant::Invalid) + { + QString sInvalid; + ar >> boost::serialization::make_nvp(sTag, sInvalid); + t = QVariant(); + } + else if (iType == QMetaType::Long) + { + long l(0); + ar >> boost::serialization::make_nvp(sTag, l); + t = QVariant(static_cast(l)); + } + else if (iType == QMetaType::Short) + { + int i(0); + ar >> boost::serialization::make_nvp(sTag, i); + t = QVariant(i); + } + else if (iType == QMetaType::Char) + { + int i(0); + ar >> boost::serialization::make_nvp(sTag, i); + t = QVariant(i); + } + else if (iType == QMetaType::ULong) + { + unsigned long ul(0); + ar >> boost::serialization::make_nvp(sTag, ul); + t = QVariant(static_cast(ul)); + } + else if (iType == QMetaType::UShort) + { + uint ui(0); + ar >> boost::serialization::make_nvp(sTag, ui); + t = QVariant(ui); + } + else if (iType == QMetaType::UChar) + { + uint ui(0); + ar >> boost::serialization::make_nvp(sTag, ui); + t = QVariant(ui); + } + else if (iType == QMetaType::Float) + { + double d(0.0); + ar >> boost::serialization::make_nvp(sTag, d); + t = QVariant(d); + } + else + { + QString sUserType; + qx::QxClassX::type_fct_load_qvariant_usertype fct; + ar >> boost::serialization::make_nvp(sTag, sUserType); + fct = qx::QxClassX::getFctLoadQVariantUserType(); + if (fct) + { + t = fct(sUserType, iType, file_version); + } + else + { + t = QVariant(); + } + } + } + + } // namespace serialization +} // namespace boost + +QX_SERIALIZE_FAST_COMPIL_SAVE_LOAD_CPP(QVariant) + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/QxBoostSerializeHelper/IxBoostSerializeRegisterHelper.cpp b/src/QxSerialize/QxBoostSerializeHelper/IxBoostSerializeRegisterHelper.cpp new file mode 100644 index 0000000..fcae11a --- /dev/null +++ b/src/QxSerialize/QxBoostSerializeHelper/IxBoostSerializeRegisterHelper.cpp @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#include +#include + +#include + +namespace qx +{ + + IxBoostSerializeRegisterHelper::IxBoostSerializeRegisterHelper(const QString &sKey) : m_sKey(sKey) + { + QxBoostSerializeRegisterHelperX::getSingleton()->add(m_sKey, this); + } + + IxBoostSerializeRegisterHelper::~IxBoostSerializeRegisterHelper() + { + if (!QxBoostSerializeRegisterHelperX::isSingletonNull()) + QxBoostSerializeRegisterHelperX::getSingleton()->remove(m_sKey); + } + +} // namespace qx + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeRegisterHelperX.cpp b/src/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeRegisterHelperX.cpp new file mode 100644 index 0000000..ad77971 --- /dev/null +++ b/src/QxSerialize/QxBoostSerializeHelper/QxBoostSerializeRegisterHelperX.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#include + +#include + +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::QxBoostSerializeRegisterHelperX) + +namespace qx +{ + + void QxBoostSerializeRegisterHelperX::add(const QString &sKey, IxBoostSerializeRegisterHelper *p) + { + QMutexLocker locker(&m_oMutexHelperX); + qAssert(!sKey.isEmpty() && !m_colHelperX.exist(sKey)); + + if (!p || sKey.isEmpty() || m_colHelperX.exist(sKey)) + return; + + m_colHelperX.insert(sKey, p); + } + + void QxBoostSerializeRegisterHelperX::remove(const QString &sKey) + { + QMutexLocker locker(&m_oMutexHelperX); + m_colHelperX.removeByKey(sKey); + } + +} // namespace qx + +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxSerialize/QxSerializeCheckInstance.cpp b/src/QxSerialize/QxSerializeCheckInstance.cpp new file mode 100644 index 0000000..97c57ff --- /dev/null +++ b/src/QxSerialize/QxSerializeCheckInstance.cpp @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +#include + +#include + +namespace qx +{ + namespace serialization + { + namespace helper + { + + QSet>> QxSerializeCheckInstance::m_lstInstanceByThread; + QHash QxSerializeCheckInstance::m_hashLevelByThread; + QHash QxSerializeCheckInstance::m_hashHierarchyByThread; + QMutex QxSerializeCheckInstance::m_mutex; + + QxSerializeCheckInstance::QxSerializeCheckInstance(const void *pInstance, qx::IxClass *pClass) : m_pInstance(0), m_lThreadId(0), m_pClass(pClass) + { + QMutexLocker locker(&m_mutex); + m_pInstance = reinterpret_cast(const_cast(pInstance)); + m_lThreadId = QThread::currentThreadId(); + qAssert(m_pInstance != 0); + m_lstInstanceByThread.insert(qMakePair(m_lThreadId, qMakePair(m_pInstance, m_pClass))); + int iLevel = (m_hashLevelByThread.value(m_lThreadId, 0) + 1); + m_hashLevelByThread.insert(m_lThreadId, iLevel); + } + + QxSerializeCheckInstance::~QxSerializeCheckInstance() + { + QMutexLocker locker(&m_mutex); + m_lstInstanceByThread.remove(qMakePair(m_lThreadId, qMakePair(m_pInstance, m_pClass))); + int iLevel = (m_hashLevelByThread.value(m_lThreadId, 0) - 1); + m_hashLevelByThread.insert(m_lThreadId, iLevel); + if (iLevel <= 0) + { + m_hashHierarchyByThread.remove(m_lThreadId); + } + } + + bool QxSerializeCheckInstance::contains(const void *pInstance, qx::IxClass *pClass) + { + QMutexLocker locker(&m_mutex); + Qt::HANDLE lCurrThreadId = QThread::currentThreadId(); + qptrdiff iInstance = reinterpret_cast(const_cast(pInstance)); + return m_lstInstanceByThread.contains(qMakePair(lCurrThreadId, qMakePair(iInstance, pClass))); + } + + bool QxSerializeCheckInstance::isRoot() + { + QMutexLocker locker(&m_mutex); + Qt::HANDLE lThreadId = QThread::currentThreadId(); + int iLevel = m_hashLevelByThread.value(lThreadId, 0); + qAssert(iLevel >= 0); + return (iLevel == 0); + } + + QxSerializeCheckInstance::type_hierarchy QxSerializeCheckInstance::getHierarchy() + { + QMutexLocker locker(&m_mutex); + Qt::HANDLE lThreadId = QThread::currentThreadId(); + return m_hashHierarchyByThread.value(lThreadId); + } + + void QxSerializeCheckInstance::setHierarchy(const QxSerializeCheckInstance::type_hierarchy &hierarchy) + { + QMutexLocker locker(&m_mutex); + Qt::HANDLE lThreadId = QThread::currentThreadId(); + m_hashHierarchyByThread.insert(lThreadId, hierarchy); + } + + } // namespace helper + } // namespace serialization +} // namespace qx diff --git a/src/QxSerialize/boost/QxExportDllBoostArchive.cpp b/src/QxSerialize/boost/QxExportDllBoostArchive.cpp new file mode 100644 index 0000000..43aab8d --- /dev/null +++ b/src/QxSerialize/boost/QxExportDllBoostArchive.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +#include + +#if _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON + +#include + +#include + +#include + +#if (BOOST_VERSION > 103800) +#define QX_BOOST_EXPORT_ARCHIVE_SERIALIZER_MAP_SINGLETON_CPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_CPP(class, boost::serialization::singleton, boost::archive::detail::archive_serializer_map, Archive) +#else // (BOOST_VERSION > 103800) +#define QX_BOOST_EXPORT_ARCHIVE_SERIALIZER_MAP_SINGLETON_CPP(Archive) /* Nothing */ +#endif // (BOOST_VERSION > 103800) + +#if (BOOST_VERSION > 103800) +#define QX_BOOST_EXPORT_ARCHIVE_SERIALIZER_MAP_CPP(ArchiveIn, ArchiveOut) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(class, boost::archive::detail::archive_serializer_map, ArchiveIn) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(class, boost::archive::detail::archive_serializer_map, ArchiveOut) +#else // (BOOST_VERSION > 103800) +#define QX_BOOST_EXPORT_ARCHIVE_SERIALIZER_MAP_CPP(ArchiveIn, ArchiveOut) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(class, boost::archive::detail::archive_pointer_iserializer, ArchiveIn) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(class, boost::archive::detail::archive_pointer_oserializer, ArchiveOut) +#endif // (BOOST_VERSION > 103800) + +#if (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_SAVE_POINTER_TYPE_CPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, boost::archive::detail::save_pointer_type, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_CPP(class, boost::serialization::singleton, boost::archive::detail::save_pointer_type, Archive) +#else // (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_SAVE_POINTER_TYPE_CPP(Archive) /* Nothing */ +#endif // (BOOST_VERSION >= 104100) + +#if (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_SAVE_NON_POINTER_TYPE_CPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, boost::archive::detail::save_non_pointer_type, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_CPP(class, boost::serialization::singleton, boost::archive::detail::save_non_pointer_type, Archive) +#else // (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_SAVE_NON_POINTER_TYPE_CPP(Archive) /* Nothing */ +#endif // (BOOST_VERSION >= 104100) + +#if (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_SAVE_ENUM_TYPE_CPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, boost::archive::detail::save_enum_type, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_CPP(class, boost::serialization::singleton, boost::archive::detail::save_enum_type, Archive) +#else // (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_SAVE_ENUM_TYPE_CPP(Archive) /* Nothing */ +#endif // (BOOST_VERSION >= 104100) + +#if (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_SAVE_ARRAY_TYPE_CPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, boost::archive::detail::save_array_type, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_CPP(class, boost::serialization::singleton, boost::archive::detail::save_array_type, Archive) +#else // (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_SAVE_ARRAY_TYPE_CPP(Archive) /* Nothing */ +#endif // (BOOST_VERSION >= 104100) + +#if (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_LOAD_POINTER_TYPE_CPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, boost::archive::detail::load_pointer_type, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_CPP(class, boost::serialization::singleton, boost::archive::detail::load_pointer_type, Archive) +#else // (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_LOAD_POINTER_TYPE_CPP(Archive) /* Nothing */ +#endif // (BOOST_VERSION >= 104100) + +#if (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_LOAD_NON_POINTER_TYPE_CPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, boost::archive::detail::load_non_pointer_type, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_CPP(class, boost::serialization::singleton, boost::archive::detail::load_non_pointer_type, Archive) +#else // (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_LOAD_NON_POINTER_TYPE_CPP(Archive) /* Nothing */ +#endif // (BOOST_VERSION >= 104100) + +#if (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_LOAD_ENUM_TYPE_CPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, boost::archive::detail::load_enum_type, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_CPP(class, boost::serialization::singleton, boost::archive::detail::load_enum_type, Archive) +#else // (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_LOAD_ENUM_TYPE_CPP(Archive) /* Nothing */ +#endif // (BOOST_VERSION >= 104100) + +#if (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_LOAD_ARRAY_TYPE_CPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(struct, boost::archive::detail::load_array_type, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_CPP(class, boost::serialization::singleton, boost::archive::detail::load_array_type, Archive) +#else // (BOOST_VERSION >= 104100) +#define QX_BOOST_EXPORT_ARCHIVE_LOAD_ARRAY_TYPE_CPP(Archive) /* Nothing */ +#endif // (BOOST_VERSION >= 104100) + +#define QX_BOOST_EXPORT_ARCHIVE_INTERFACE_IARCHIVE_CPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(class, boost::archive::detail::interface_iarchive, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_CPP(class, boost::serialization::singleton, boost::archive::detail::interface_iarchive, Archive) + +#define QX_BOOST_EXPORT_ARCHIVE_INTERFACE_OARCHIVE_CPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(class, boost::archive::detail::interface_oarchive, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_CPP(class, boost::serialization::singleton, boost::archive::detail::interface_oarchive, Archive) + +#define QX_BOOST_EXPORT_ARCHIVE_COMMON_IARCHIVE_CPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(class, boost::archive::detail::common_iarchive, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_CPP(class, boost::serialization::singleton, boost::archive::detail::common_iarchive, Archive) + +#define QX_BOOST_EXPORT_ARCHIVE_COMMON_OARCHIVE_CPP(Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_P1_CPP(class, boost::archive::detail::common_oarchive, Archive) \ + QX_DLL_EXPORT_TEMPLATE_T_U_P1_CPP(class, boost::serialization::singleton, boost::archive::detail::common_oarchive, Archive) + +#define QX_BOOST_EXPORT_ARCHIVE_INTERFACE_CPP(ArchiveIn, ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_INTERFACE_IARCHIVE_CPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_INTERFACE_OARCHIVE_CPP(ArchiveOut) + +#define QX_BOOST_EXPORT_ARCHIVE_COMMON_CPP(ArchiveIn, ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_COMMON_IARCHIVE_CPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_COMMON_OARCHIVE_CPP(ArchiveOut) + +#define QX_BOOST_EXPORT_ARCHIVE_CPP(ArchiveIn, ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_SERIALIZER_MAP_SINGLETON_CPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_SERIALIZER_MAP_SINGLETON_CPP(ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_SAVE_NON_POINTER_TYPE_CPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_SAVE_NON_POINTER_TYPE_CPP(ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_SAVE_POINTER_TYPE_CPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_SAVE_POINTER_TYPE_CPP(ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_SAVE_ENUM_TYPE_CPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_SAVE_ENUM_TYPE_CPP(ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_SAVE_ARRAY_TYPE_CPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_SAVE_ARRAY_TYPE_CPP(ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_LOAD_NON_POINTER_TYPE_CPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_LOAD_NON_POINTER_TYPE_CPP(ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_LOAD_POINTER_TYPE_CPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_LOAD_POINTER_TYPE_CPP(ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_LOAD_ENUM_TYPE_CPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_LOAD_ENUM_TYPE_CPP(ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_LOAD_ARRAY_TYPE_CPP(ArchiveIn) \ + QX_BOOST_EXPORT_ARCHIVE_LOAD_ARRAY_TYPE_CPP(ArchiveOut) + +#define QX_BOOST_EXPORT_ARCHIVE_ALL_CPP(ArchiveIn, ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_CPP(ArchiveIn, ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_INTERFACE_CPP(ArchiveIn, ArchiveOut) \ + QX_BOOST_EXPORT_ARCHIVE_COMMON_CPP(ArchiveIn, ArchiveOut) + +#if _QX_SERIALIZE_POLYMORPHIC +QX_BOOST_EXPORT_ARCHIVE_CPP(boost::archive::polymorphic_iarchive, boost::archive::polymorphic_oarchive) +QX_BOOST_EXPORT_ARCHIVE_CPP(boost::archive::polymorphic_binary_iarchive, boost::archive::polymorphic_binary_oarchive) +QX_BOOST_EXPORT_ARCHIVE_CPP(boost::archive::polymorphic_text_iarchive, boost::archive::polymorphic_text_oarchive) +QX_BOOST_EXPORT_ARCHIVE_CPP(boost::archive::polymorphic_xml_iarchive, boost::archive::polymorphic_xml_oarchive) +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY +QX_BOOST_EXPORT_ARCHIVE_ALL_CPP(boost::archive::binary_iarchive, boost::archive::binary_oarchive) +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT +QX_BOOST_EXPORT_ARCHIVE_ALL_CPP(boost::archive::text_iarchive, boost::archive::text_oarchive) +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML +QX_BOOST_EXPORT_ARCHIVE_ALL_CPP(boost::archive::xml_iarchive, boost::archive::xml_oarchive) +#endif // _QX_SERIALIZE_XML + +#if _QX_SERIALIZE_PORTABLE_BINARY +QX_BOOST_EXPORT_ARCHIVE_SERIALIZER_MAP_CPP(eos::portable_iarchive, eos::portable_oarchive) +QX_BOOST_EXPORT_ARCHIVE_ALL_CPP(eos::portable_iarchive, eos::portable_oarchive) +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#if _QX_SERIALIZE_WIDE_BINARY +QX_BOOST_EXPORT_ARCHIVE_ALL_CPP(boost::archive::binary_wiarchive, boost::archive::binary_woarchive) +#endif // _QX_SERIALIZE_WIDE_BINARY + +#if _QX_SERIALIZE_WIDE_TEXT +QX_BOOST_EXPORT_ARCHIVE_ALL_CPP(boost::archive::text_wiarchive, boost::archive::text_woarchive) +#endif // _QX_SERIALIZE_WIDE_TEXT + +#if _QX_SERIALIZE_WIDE_XML +QX_BOOST_EXPORT_ARCHIVE_ALL_CPP(boost::archive::xml_wiarchive, boost::archive::xml_woarchive) +#endif // _QX_SERIALIZE_WIDE_XML + +#endif // _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON +#endif // _QX_ENABLE_BOOST_SERIALIZATION diff --git a/src/QxService/IxParameter.cpp b/src/QxService/IxParameter.cpp new file mode 100644 index 0000000..f66d0de --- /dev/null +++ b/src/QxService/IxParameter.cpp @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK + +#include + +#include + +#include + +QX_REGISTER_INTERNAL_HELPER_START_FILE_CPP(qx::service::IxParameter) + +namespace qx +{ + namespace service + { + + IxParameter::IxParameter() { ; } + + IxParameter::~IxParameter() { ; } + + void IxParameter::registerClass() const { qAssertMsg(false, "[QxOrm] qx::service::IxParameter::registerClass()", "need to override this method with 'qx::service::QxConnect::serialization_qt' or 'qx::service::QxConnect::serialization_json' (use QX_SERVICE_IX_PARAMETER_SERIALIZATION_HPP and QX_SERVICE_IX_PARAMETER_SERIALIZATION_CPP macro to override)"); } + + QString IxParameter::getClassName() const + { + qAssertMsg(false, "[QxOrm] qx::service::IxParameter::getClassName()", "need to override this method with 'qx::service::QxConnect::serialization_qt' or 'qx::service::QxConnect::serialization_json' (use QX_SERVICE_IX_PARAMETER_SERIALIZATION_HPP and QX_SERVICE_IX_PARAMETER_SERIALIZATION_CPP macro to override)"); + return ""; + } + + void IxParameter::save(QDataStream &stream) const + { + qAssertMsg(false, "[QxOrm] qx::service::IxParameter::save()", "need to override this method with 'qx::service::QxConnect::serialization_qt' (use QX_SERVICE_IX_PARAMETER_SERIALIZATION_HPP and QX_SERVICE_IX_PARAMETER_SERIALIZATION_CPP macro to override)"); + Q_UNUSED(stream); + } + + void IxParameter::load(QDataStream &stream) + { + qAssertMsg(false, "[QxOrm] qx::service::IxParameter::load()", "need to override this method with 'qx::service::QxConnect::serialization_qt' (use QX_SERVICE_IX_PARAMETER_SERIALIZATION_HPP and QX_SERVICE_IX_PARAMETER_SERIALIZATION_CPP macro to override)"); + Q_UNUSED(stream); + } + +#ifndef _QX_NO_JSON + + QJsonValue IxParameter::saveToJson() const + { + qAssertMsg(false, "[QxOrm] qx::service::IxParameter::saveToJson()", "need to override this method with 'qx::service::QxConnect::serialization_json' (use QX_SERVICE_IX_PARAMETER_SERIALIZATION_HPP and QX_SERVICE_IX_PARAMETER_SERIALIZATION_CPP macro to override)"); + return QJsonValue(); + } + + qx_bool IxParameter::loadFromJson(const QJsonValue &val) + { + qAssertMsg(false, "[QxOrm] qx::service::IxParameter::loadFromJson()", "need to override this method with 'qx::service::QxConnect::serialization_json' (use QX_SERVICE_IX_PARAMETER_SERIALIZATION_HPP and QX_SERVICE_IX_PARAMETER_SERIALIZATION_CPP macro to override)"); + Q_UNUSED(val); + return qx_bool(); + } + +#endif // _QX_NO_JSON + + } // namespace service +} // namespace qx + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const qx::service::IxParameter &t, const unsigned int file_version) + { + Q_UNUSED(ar); + Q_UNUSED(t); + Q_UNUSED(file_version); + } + + template + inline void qx_load(Archive &ar, qx::service::IxParameter &t, const unsigned int file_version) + { + Q_UNUSED(ar); + Q_UNUSED(t); + Q_UNUSED(file_version); + } + + } // namespace serialization +} // namespace boost + +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +QX_REGISTER_INTERNAL_HELPER_END_FILE_CPP(qx::service::IxParameter) + +QDataStream &operator<<(QDataStream &stream, const qx::service::IxParameter &t) +{ + Q_UNUSED(t); + return stream; +} + +QDataStream &operator>>(QDataStream &stream, qx::service::IxParameter &t) +{ + Q_UNUSED(t); + return stream; +} + +#endif // _QX_ENABLE_QT_NETWORK diff --git a/src/QxService/IxService.cpp b/src/QxService/IxService.cpp new file mode 100644 index 0000000..fd82b5b --- /dev/null +++ b/src/QxService/IxService.cpp @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK + +#include + +#include +#include + +#include + +#include + +QX_REGISTER_INTERNAL_HELPER_START_FILE_CPP(qx::service::IxService) + +namespace qx +{ + namespace service + { + + IxService::IxService() { ; } + IxService::IxService(const QString &sServiceName) : m_sServiceName(sServiceName) { qAssert(!m_sServiceName.isEmpty()); } + IxService::~IxService() { ; } + + std::shared_ptr IxService::getTransaction() const { return m_pTransaction; } + void IxService::setTransaction(const std::shared_ptr &p) { m_pTransaction = p; } + + } // namespace service +} // namespace qx + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const qx::service::IxService &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + + QString sServiceName = t.getServiceName(); + qx::service::IxParameter_ptr pInputParam = t.getInputParameter_BaseClass(); + qx::service::IxParameter_ptr pOutputParam = t.getOutputParameter_BaseClass(); + qx_bool bMsgReturn = t.getMessageReturn(); + + ar << boost::serialization::make_nvp("service_name", sServiceName); + ar << boost::serialization::make_nvp("input_param", pInputParam); + ar << boost::serialization::make_nvp("output_param", pOutputParam); + ar << boost::serialization::make_nvp("msg_return", bMsgReturn); + } + + template + inline void qx_load(Archive &ar, qx::service::IxService &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QString sServiceName; + qx::service::IxParameter_ptr pInputParam; + qx::service::IxParameter_ptr pOutputParam; + qx_bool bMsgReturn; + + ar >> boost::serialization::make_nvp("service_name", sServiceName); + ar >> boost::serialization::make_nvp("input_param", pInputParam); + ar >> boost::serialization::make_nvp("output_param", pOutputParam); + ar >> boost::serialization::make_nvp("msg_return", bMsgReturn); + + t.setServiceName(sServiceName); + t.setInputParameter(pInputParam); + t.setOutputParameter(pOutputParam); + t.setMessageReturn(bMsgReturn); + } + + } // namespace serialization +} // namespace boost + +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +QX_REGISTER_INTERNAL_HELPER_END_FILE_CPP(qx::service::IxService) + +QDataStream &operator<<(QDataStream &stream, const qx::service::IxService &t) +{ + stream << t.m_sServiceName; + stream << t.m_sServiceMethodName; + stream << t.m_bMessageReturn; + + qint16 iIsNull = (t.m_pInputParameter ? 0 : 1); + stream << iIsNull; + if (t.m_pInputParameter) + { + t.m_pInputParameter->registerClass(); + QString sClassName = t.m_pInputParameter->getClassName(); + stream << sClassName; + t.m_pInputParameter->save(stream); + } + + iIsNull = (t.m_pOutputParameter ? 0 : 1); + stream << iIsNull; + if (t.m_pOutputParameter) + { + t.m_pOutputParameter->registerClass(); + QString sClassName = t.m_pOutputParameter->getClassName(); + stream << sClassName; + t.m_pOutputParameter->save(stream); + } + + return stream; +} + +QDataStream &operator>>(QDataStream &stream, qx::service::IxService &t) +{ + stream >> t.m_sServiceName; + stream >> t.m_sServiceMethodName; + stream >> t.m_bMessageReturn; + + qint16 iIsNull = 0; + stream >> iIsNull; + if (iIsNull) + { + t.m_pInputParameter.reset(); + } + else + { + QString sClassName; + stream >> sClassName; + qx::service::IxParameter *ptr = qx::create_nude_ptr(sClassName); + if (!ptr) + { + qAssertMsg(false, "[QxOrm] qx::service::IxService, loading QDataStream", "unable to create nude pointer for input parameter"); + } + else + { + ptr->registerClass(); + ptr->load(stream); + } + t.m_pInputParameter.reset(ptr); + } + + iIsNull = 0; + stream >> iIsNull; + if (iIsNull) + { + t.m_pOutputParameter.reset(); + } + else + { + QString sClassName; + stream >> sClassName; + qx::service::IxParameter *ptr = qx::create_nude_ptr(sClassName); + if (!ptr) + { + qAssertMsg(false, "[QxOrm] qx::service::IxService, loading QDataStream", "unable to create nude pointer for output parameter"); + } + else + { + ptr->registerClass(); + ptr->load(stream); + } + t.m_pOutputParameter.reset(ptr); + } + + return stream; +} + +#endif // _QX_ENABLE_QT_NETWORK diff --git a/src/QxService/QxConnect.cpp b/src/QxService/QxConnect.cpp new file mode 100644 index 0000000..34daa20 --- /dev/null +++ b/src/QxService/QxConnect.cpp @@ -0,0 +1,442 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK + +#include + +#include + +#include + +#if _QX_SERIALIZE_BINARY +#define QX_SERVICE_DEFAULT_SERIALIZATION_TYPE QxConnect::serialization_binary +#elif _QX_SERIALIZE_WIDE_BINARY +#define QX_SERVICE_DEFAULT_SERIALIZATION_TYPE QxConnect::serialization_wide_binary +#elif _QX_SERIALIZE_PORTABLE_BINARY +#define QX_SERVICE_DEFAULT_SERIALIZATION_TYPE QxConnect::serialization_portable_binary +#elif _QX_SERIALIZE_POLYMORPHIC +#define QX_SERVICE_DEFAULT_SERIALIZATION_TYPE QxConnect::serialization_polymorphic_binary +#elif _QX_SERIALIZE_TEXT +#define QX_SERVICE_DEFAULT_SERIALIZATION_TYPE QxConnect::serialization_text +#elif _QX_SERIALIZE_XML +#define QX_SERVICE_DEFAULT_SERIALIZATION_TYPE QxConnect::serialization_xml +#elif _QX_SERIALIZE_WIDE_TEXT +#define QX_SERVICE_DEFAULT_SERIALIZATION_TYPE QxConnect::serialization_wide_text +#elif _QX_SERIALIZE_WIDE_XML +#define QX_SERVICE_DEFAULT_SERIALIZATION_TYPE QxConnect::serialization_wide_xml +#endif // _QX_SERIALIZE_BINARY + +#ifndef QX_SERVICE_DEFAULT_SERIALIZATION_TYPE +#define QX_SERVICE_DEFAULT_SERIALIZATION_TYPE QxConnect::serialization_qt +#endif // QX_SERVICE_DEFAULT_SERIALIZATION_TYPE + +#define QX_SERVICE_DEFAULT_ENCRYPT_KEY Q_UINT64_C(0x0f2aac3b24358a1a) + +#ifndef QT_NO_SSL +#define QX_CONSTRUCT_QX_SERVICE_CONNECT_SSL() \ + , m_sslEnabled(false), m_sslProtocol(QSsl::SecureProtocols), m_sslPeerVerifyMode(QSslSocket::VerifyNone), m_sslPeerVerifyDepth(0) +#else // QT_NO_SSL +#define QX_CONSTRUCT_QX_SERVICE_CONNECT_SSL() /* Nothing */ +#endif // QT_NO_SSL + +#define QX_CONSTRUCT_QX_SERVICE_CONNECT() \ + m_lPort(7832), m_eSerializationType(QX_SERVICE_DEFAULT_SERIALIZATION_TYPE), m_lThreadCount(30), \ + m_iMaxWait(30000), m_bCompressData(false), m_bEncryptData(false), m_lKeepAlive(0), m_bModeHTTP(false), m_lSessionTimeOut(86400000) QX_CONSTRUCT_QX_SERVICE_CONNECT_SSL() + +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::service::QxConnect) + +namespace qx +{ + namespace service + { + + struct Q_DECL_HIDDEN QxConnect::QxConnectImpl + { + + QMutex m_mutex; //!< Mutex => 'QxConnect' is thread-safe + QString m_sIp; //!< Ip address + long m_lPort; //!< Port number + QxConnect::serialization_type m_eSerializationType; //!< Serialization type to send data over network + long m_lThreadCount; //!< Thread count to execute all transactions (cf. 'QxThreadPool') + int m_iMaxWait; //!< Max wait in milliseconds for network processes + bool m_bCompressData; //!< Compress data over network + bool m_bEncryptData; //!< Encrypt data before transfering it over network + quint64 m_uiEncryptKey; //!< 64 bit key to encrypt/decrypt data + long m_lKeepAlive; //!< Keep socket opened during X milliseconds (-1 means never disconnect) + bool m_bModeHTTP; //!< Put QxService module in mode HTTP (see QxHttpServer module) + qlonglong m_lSessionTimeOut; //!< HTTP session time-out (expiration) in milliseconds + +#ifndef QT_NO_SSL + bool m_sslEnabled; //!< Is secure connection enabled + QSslConfiguration m_sslConfig; //!< Global SSL configuration + QList m_sslCACertificates; //!< CA certificates + QSslCertificate m_sslLocalCertificate; //!< Server certificate (public part) + QSslKey m_sslPrivateKey; //!< Server certificate private key + QList m_sslIgnoreErrors; //!< List of SSL errors to ignore + QSsl::SslProtocol m_sslProtocol; //!< List of supported protocols (SSL, TLS) + QString m_sslPeerVerifyName; //!< Peer name for certificate validation + QSslSocket::PeerVerifyMode m_sslPeerVerifyMode; //!< Peer mode for certificate validation + int m_sslPeerVerifyDepth; //!< Peer depth level for certificate validation +#endif // QT_NO_SSL + + QxConnectImpl() : QX_CONSTRUCT_QX_SERVICE_CONNECT() + { + m_uiEncryptKey = QX_SERVICE_DEFAULT_ENCRYPT_KEY; + ignoreAllSSLErrors(); + } + ~QxConnectImpl() { ; } + + void ignoreAllSSLErrors() + { +#ifndef QT_NO_SSL + QSslError err = QSslError(QSslError::UnableToGetIssuerCertificate); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::UnableToDecryptCertificateSignature); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::UnableToDecodeIssuerPublicKey); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::CertificateSignatureFailed); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::CertificateNotYetValid); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::CertificateExpired); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::InvalidNotBeforeField); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::InvalidNotAfterField); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::SelfSignedCertificate); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::SelfSignedCertificateInChain); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::UnableToGetLocalIssuerCertificate); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::UnableToVerifyFirstCertificate); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::CertificateRevoked); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::InvalidCaCertificate); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::PathLengthExceeded); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::InvalidPurpose); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::CertificateUntrusted); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::CertificateRejected); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::SubjectIssuerMismatch); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::AuthorityIssuerSerialNumberMismatch); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::NoPeerCertificate); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::HostNameMismatch); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::UnspecifiedError); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::NoSslSupport); + m_sslIgnoreErrors.append(err); + err = QSslError(QSslError::CertificateBlacklisted); + m_sslIgnoreErrors.append(err); +#endif // QT_NO_SSL + } + }; + + QxConnect::QxConnect() : qx::QxSingleton("qx::service::QxConnect"), m_pImpl(new QxConnectImpl()) { ; } + + QxConnect::~QxConnect() { ; } + + QString QxConnect::getIp() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_sIp; + } + + long QxConnect::getPort() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_lPort; + } + + QxConnect::serialization_type QxConnect::getSerializationType() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_eSerializationType; + } + + long QxConnect::getThreadCount() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_lThreadCount; + } + + int QxConnect::getMaxWait() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_iMaxWait; + } + + bool QxConnect::getCompressData() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_bCompressData; + } + + bool QxConnect::getEncryptData() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_bEncryptData; + } + + quint64 QxConnect::getEncryptKey() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_uiEncryptKey; + } + + long QxConnect::getKeepAlive() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_lKeepAlive; + } + + bool QxConnect::getModeHTTP() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_bModeHTTP; + } + + qlonglong QxConnect::getSessionTimeOut() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_lSessionTimeOut; + } + +#ifndef QT_NO_SSL + + bool QxConnect::getSSLEnabled() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_sslEnabled; + } + + QSslConfiguration QxConnect::getSSLConfiguration() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_sslConfig; + } + + QList QxConnect::getSSLCACertificates() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_sslCACertificates; + } + + QSslCertificate QxConnect::getSSLLocalCertificate() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_sslLocalCertificate; + } + + QSslKey QxConnect::getSSLPrivateKey() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_sslPrivateKey; + } + + QList QxConnect::getSSLIgnoreErrors() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_sslIgnoreErrors; + } + + QSsl::SslProtocol QxConnect::getSSLProtocol() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_sslProtocol; + } + + QString QxConnect::getSSLPeerVerifyName() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_sslPeerVerifyName; + } + + QSslSocket::PeerVerifyMode QxConnect::getSSLPeerVerifyMode() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_sslPeerVerifyMode; + } + + int QxConnect::getSSLPeerVerifyDepth() + { + QMutexLocker locker(&m_pImpl->m_mutex); + return m_pImpl->m_sslPeerVerifyDepth; + } + +#endif // QT_NO_SSL + + void QxConnect::setIp(const QString &s) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_sIp = s; + } + + void QxConnect::setPort(long l) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_lPort = l; + } + + void QxConnect::setSerializationType(QxConnect::serialization_type e) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_eSerializationType = e; + } + + void QxConnect::setThreadCount(long l) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_lThreadCount = l; + qAssert(l > 0); + } + + void QxConnect::setMaxWait(int i) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_iMaxWait = i; + qAssert(i > 0); + } + + void QxConnect::setCompressData(bool b) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_bCompressData = b; + } + + void QxConnect::setEncryptData(bool b, quint64 key /* = 0 */) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_bEncryptData = b; + if (key != 0) + { + m_pImpl->m_uiEncryptKey = key; + } + } + + void QxConnect::setKeepAlive(long l) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_lKeepAlive = l; + } + + void QxConnect::setModeHTTP(bool b) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_bModeHTTP = b; + } + + void QxConnect::setSessionTimeOut(qlonglong l) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_lSessionTimeOut = l; + } + +#ifndef QT_NO_SSL + + void QxConnect::setSSLEnabled(bool b) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_sslEnabled = b; + } + + void QxConnect::setSSLConfiguration(QSslConfiguration cfg) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_sslConfig = cfg; + } + + void QxConnect::setSSLCACertificates(QList lst) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_sslCACertificates = lst; + } + + void QxConnect::setSSLLocalCertificate(QSslCertificate cert) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_sslLocalCertificate = cert; + } + + void QxConnect::setSSLPrivateKey(QSslKey key) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_sslPrivateKey = key; + } + + void QxConnect::setSSLIgnoreErrors(QList lst) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_sslIgnoreErrors = lst; + } + + void QxConnect::setSSLProtocol(QSsl::SslProtocol e) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_sslProtocol = e; + } + + void QxConnect::setSSLPeerVerifyName(const QString &s) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_sslPeerVerifyName = s; + } + + void QxConnect::setSSLPeerVerifyMode(QSslSocket::PeerVerifyMode e) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_sslPeerVerifyMode = e; + } + + void QxConnect::setSSLPeerVerifyDepth(int i) + { + QMutexLocker locker(&m_pImpl->m_mutex); + m_pImpl->m_sslPeerVerifyDepth = i; + } + +#endif // QT_NO_SSL + + } // namespace service +} // namespace qx + +#endif // _QX_ENABLE_QT_NETWORK diff --git a/src/QxService/QxServer.cpp b/src/QxService/QxServer.cpp new file mode 100644 index 0000000..716de01 --- /dev/null +++ b/src/QxService/QxServer.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK + +#include + +#include +#include +#include +#include +#include + +#include + +namespace qx +{ + namespace service + { + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + void QxServer::incomingConnection(qintptr socketDescriptor) +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + void QxServer::incomingConnection(int socketDescriptor) +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + { + QMutexLocker locker(&m_mutex); + QxThread *pThread = getAvailable(); + if (m_pThreadPool && m_pThreadPool->isStopped()) + { + return; + } + if (!pThread) + { + if (m_pThreadPool) + { + m_pThreadPool->raiseError("[QxOrm] no service available : cannot accept incoming connection (increase thread count value)", QxTransaction_ptr()); + } + return; + } + pThread->execute(socketDescriptor); + } + + QxThread *QxServer::getAvailable() const + { + if (!m_pThreadPool) + { + qAssert(false); + return NULL; + } + if (m_pThreadPool->isStopped()) + { + return NULL; + } + QxThread *pThread = m_pThreadPool->getAvailable(); + if (pThread) + { + return pThread; + } + qDebug("[QxOrm] qx::service::QxServer no service available : %s", "need to wait (try to increase thread count value)"); + + int iCurrRetryCount = 0; + int iMaxRetryCount = QxConnect::getSingleton()->getMaxWait(); + while ((!pThread) && (iCurrRetryCount < iMaxRetryCount)) + { + if (m_pThreadPool->isStopped()) + { + return NULL; + } + qx::service::QxThreadPool::sleepThread(1); + pThread = m_pThreadPool->getAvailable(); + iCurrRetryCount++; + } + + return pThread; + } + + } // namespace service +} // namespace qx + +#endif // _QX_ENABLE_QT_NETWORK diff --git a/src/QxService/QxThread.cpp b/src/QxService/QxThread.cpp new file mode 100644 index 0000000..2cc7f66 --- /dev/null +++ b/src/QxService/QxThread.cpp @@ -0,0 +1,405 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK + +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#ifndef QT_NO_SSL +#ifndef QT_NO_OPENSSL +#define QX_THREAD_SSL +#endif // QT_NO_OPENSSL +#endif // QT_NO_SSL +#ifndef QX_THREAD_SSL +#define QX_THREAD_NO_SSL +#endif // QX_THREAD_SSL + +namespace qx +{ + namespace service + { + + void QxThread::init() + { + QObject::connect(this, SIGNAL(incomingConnection()), this, SLOT(onIncomingConnection())); + } + + bool QxThread::isAvailable() + { + QMutexLocker locker(&m_mutex); + return ((m_pTransaction.get() == NULL) && (m_iSocketDescriptor == 0)); + } + + void QxThread::stop() + { + QMutexLocker locker(&m_mutex); + m_bIsStopped = true; + if (m_iSocketDescriptor == 0) + { + quit(); + } + } + + void QxThread::wait() + { + if (m_pThread) + { + m_pThread->wait(); + } + } + + void QxThread::quit() + { + Q_EMIT finished(); + if (m_pThread) + { + m_pThread->quit(); + } + } + + bool QxThread::hasBeenStopped() + { + QMutexLocker locker(&m_mutex); + return m_bIsStopped; + } + + void QxThread::execute(QX_TYPE_SOCKET_DESC socketDescriptor) + { + QMutexLocker locker(&m_mutex); + if (m_bIsStopped) + { + return; + } + if (m_iSocketDescriptor != 0) + { + qAssert(false); + return; + } + m_iSocketDescriptor = socketDescriptor; + if (m_iSocketDescriptor != 0) + { + Q_EMIT incomingConnection(); + } + } + + QX_TYPE_SOCKET_DESC QxThread::getSocketDescriptor() + { + QMutexLocker locker(&m_mutex); + return m_iSocketDescriptor; + } + + void QxThread::onIncomingConnection() + { + if (getSocketDescriptor() == 0) + { + qAssert(false); + return; + } + if (hasBeenStopped()) + { + quit(); + return; + } + std::unique_ptr socket; + m_bIsDisconnected = false; + +#ifndef QX_THREAD_NO_SSL + bool bSSLEnabled = QxConnect::getSingleton()->getSSLEnabled(); + if (bSSLEnabled) + { + socket.reset(initSocketSSL()); + } + else + { + socket.reset(new QTcpSocket()); + } +#else // QX_THREAD_NO_SSL + socket.reset(new QTcpSocket()); +#endif // QX_THREAD_NO_SSL + + QObject::connect(socket.get(), SIGNAL(disconnected()), this, SLOT(onSocketDisconnected())); + QObject::connect(socket.get(), SIGNAL(readyRead()), this, SLOT(onSocketReadyRead())); + + if (socket->setSocketDescriptor(getSocketDescriptor())) + { +#ifndef QX_THREAD_NO_SSL + if ((!bSSLEnabled) || (checkSocketSSLEncrypted(socket.get()))) + { +#endif // QX_THREAD_NO_SSL + do + { + doProcess(*socket); + } while (checkKeepAlive(*socket) && (!hasBeenStopped()) && (!m_bIsDisconnected)); + socket->disconnectFromHost(); + if ((!m_bIsDisconnected) && (socket->state() != QAbstractSocket::UnconnectedState)) + { + socket->waitForDisconnected(QxConnect::getSingleton()->getMaxWait()); + } +#ifndef QX_THREAD_NO_SSL + } + else + { + Q_EMIT error("[QxOrm] SSL socket encrypted error : " + socket->errorString(), QxTransaction_ptr()); + } +#endif // QX_THREAD_NO_SSL + } + else + { + Q_EMIT error("[QxOrm] invalid socket descriptor : cannot start transaction (" + socket->errorString() + ")", QxTransaction_ptr()); + } + + clearData(); + if (m_pThreadPool) + { + m_pThreadPool->setAvailable(this); + } + if (hasBeenStopped()) + { + quit(); + } + } + + bool QxThread::checkKeepAlive(QTcpSocket &socket) + { + if (m_pTransaction && (m_pTransaction->getForceConnectionStatus() == qx::service::QxTransaction::conn_close)) + { + return false; + } + long lKeepAlive = QxConnect::getSingleton()->getKeepAlive(); + if (lKeepAlive == 0) + { + return false; + } + + long lCurrRetry = 0; + long lMaxRetry = lKeepAlive; + do + { + if (socket.waitForReadyRead(1)) + { + return true; + } + if (socket.bytesAvailable() > 0) + { + return true; + } + if (hasBeenStopped() || m_bIsDisconnected) + { + return false; + } + QCoreApplication::processEvents(); + lCurrRetry++; + } while ((lMaxRetry == -1) || (lCurrRetry < lMaxRetry)); + return false; + } + +#ifndef QX_THREAD_NO_SSL + + bool QxThread::checkSocketSSLEncrypted(QTcpSocket *socket) + { + if (!QxConnect::getSingleton()->getSSLEnabled()) + { + qAssert(false); + return false; + } + QSslSocket *ssl = static_cast(socket); + ssl->startServerEncryption(); + return ssl->waitForEncrypted(QxConnect::getSingleton()->getMaxWait()); + } + + QSslSocket *QxThread::initSocketSSL() + { + QSslSocket *socket = new QSslSocket(); + QxConnect *settings = QxConnect::getSingleton(); + QSslConfiguration config = settings->getSSLConfiguration(); + if (config.isNull()) + { + config = QSslConfiguration::defaultConfiguration(); + } + QList allCACertificates = settings->getSSLCACertificates(); + config.setCaCertificates(allCACertificates); // because QSslSocket::setCaCertificates() is obsolete + + QObject::connect(socket, SIGNAL(encrypted()), this, SLOT(onSocketSSLEncrypted())); + QObject::connect(socket, SIGNAL(sslErrors(const QList &)), this, SLOT(onSocketSSLErrors(const QList &))); + QObject::connect(socket, SIGNAL(peerVerifyError(const QSslError &)), this, SLOT(onSocketSSLPeerVerifyError(const QSslError &))); + + socket->setSslConfiguration(config); + socket->ignoreSslErrors(settings->getSSLIgnoreErrors()); + socket->setProtocol(settings->getSSLProtocol()); + socket->setPeerVerifyName(settings->getSSLPeerVerifyName()); + socket->setPeerVerifyMode(settings->getSSLPeerVerifyMode()); + socket->setPeerVerifyDepth(settings->getSSLPeerVerifyDepth()); + socket->setPrivateKey(settings->getSSLPrivateKey()); + socket->setLocalCertificate(settings->getSSLLocalCertificate()); + + return socket; + } + +#endif // QX_THREAD_NO_SSL + + void QxThread::clearData() + { + QMutexLocker locker(&m_mutex); + m_pTransaction.reset(); + m_iSocketDescriptor = 0; + m_bIsDisconnected = false; + } + + void QxThread::doProcess(QTcpSocket &socket) + { + long lMaxWait = QxConnect::getSingleton()->getMaxWait(); + bool bModeHTTP = QxConnect::getSingleton()->getModeHTTP(); + if (bModeHTTP) + { + m_pTransaction = std::make_shared(); + } + else + { + m_pTransaction = std::make_shared(); + } + QObject::connect(m_pTransaction.get(), SIGNAL(onCustomRequestHandler()), this, SLOT(onCustomRequestHandler()), Qt::DirectConnection); + + qx_bool bReadOk = m_pTransaction->readSocketServer(socket); + if (!bReadOk) + { + Q_EMIT error(QString("[QxOrm] unable to read request from socket : '") + bReadOk.getDesc() + QString("'"), QxTransaction_ptr()); + return; + } + if (hasBeenStopped() || m_bIsDisconnected) + { + return; + } + socket.readAll(); + + Q_EMIT transactionStarted(m_pTransaction); + try + { + m_pTransaction->executeServer(); + } + catch (const qx::exception &x) + { + qx_bool xb = x.toQxBool(); + m_pTransaction->setMessageReturn(xb); + } + catch (const std::exception &e) + { + m_pTransaction->setMessageReturn(qx_bool(QX_ERROR_UNKNOWN, e.what())); + } + catch (...) + { + m_pTransaction->setMessageReturn(qx_bool(QX_ERROR_UNKNOWN, "unknown error")); + } + if (hasBeenStopped() || m_bIsDisconnected) + { + return; + } + + qx_bool bWriteOk = m_pTransaction->writeSocketServer(socket); + if (!bWriteOk) + { + Q_EMIT error(QString("[QxOrm] unable to write reply to socket : '") + bWriteOk.getDesc() + QString("'"), m_pTransaction); + } + + long lCurrRetry = 0; + while ((socket.bytesToWrite() > 0) && ((lMaxWait == -1) || (lCurrRetry < lMaxWait)) && (!hasBeenStopped()) && (!m_bIsDisconnected)) + { + socket.waitForBytesWritten(1); + QCoreApplication::processEvents(); + lCurrRetry++; + } + Q_EMIT transactionFinished(m_pTransaction); + } + + void QxThread::onCustomRequestHandler() + { + if (m_pTransaction) + { + Q_EMIT customRequestHandler(m_pTransaction); + } + else + { + qAssert(false); + } + } + + void QxThread::onSocketDisconnected() + { + m_bIsDisconnected = true; + } + + void QxThread::onSocketReadyRead() + { + /* Nothing here */ + } + +#ifndef QX_THREAD_NO_SSL + + void QxThread::onSocketSSLEncrypted() + { + /* Nothing here */ + } + + void QxThread::onSocketSSLErrors(const QList &errors) + { + for (int i = 0; i < errors.count(); i++) + { + QSslError err = errors.at(i); + QString msg = err.errorString(); + qDebug("[QxOrm] qx::service::QxThread::onSocketSSLErrors() : %s", qPrintable(msg)); + } + } + + void QxThread::onSocketSSLPeerVerifyError(const QSslError &error) + { + QString msg = error.errorString(); + qDebug("[QxOrm] qx::service::QxThread::onSocketSSLPeerVerifyError() : %s", qPrintable(msg)); + } + +#endif // QX_THREAD_NO_SSL + + } // namespace service +} // namespace qx + +#endif // _QX_ENABLE_QT_NETWORK diff --git a/src/QxService/QxThreadPool.cpp b/src/QxService/QxThreadPool.cpp new file mode 100644 index 0000000..ba38d54 --- /dev/null +++ b/src/QxService/QxThreadPool.cpp @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK + +#include + +#include + +#include +#include +#include + +#include + +#include + +namespace qx +{ + namespace service + { + + bool QxThreadPool::isStopped() const { return m_bIsStopped; } + + QxThread *QxThreadPool::getAvailable() + { + if (m_bIsStopped) + { + return NULL; + } + QMutexLocker locker(&m_mutex); + QxThread *p = (m_lstAvailable.isEmpty() ? NULL : m_lstAvailable.dequeue()); + if (p) + { + qAssert(p->isAvailable()); + } + return ((p && p->isAvailable()) ? p : NULL); + } + + void QxThreadPool::setAvailable(QxThread *p) + { + if (m_bIsStopped) + { + return; + } + QMutexLocker locker(&m_mutex); + if ((p == NULL) || (!p->isAvailable())) + { + qAssert(false); + return; + } + for (long l = 0; l < m_lstAvailable.count(); l++) + { + if (m_lstAvailable.at(l) == p) + { + qAssert(false); + return; + } + } + m_lstAvailable.enqueue(p); + } + + void QxThreadPool::raiseError(const QString &err, QxTransaction_ptr transaction) + { + qAssert(!err.isEmpty()); + if (!err.isEmpty()) + { + Q_EMIT error(err, transaction); + } + } + + void QxThreadPool::run() + { + // To be sure that HTTP session manager event loop is running on this thread + qx::QxHttpSessionManager::deleteSingleton(); + qx::QxHttpSessionManager::getSingleton(); + + initServices(); + runServer(); + m_bIsStopped = true; + clearServices(); + qx::QxHttpSessionManager::deleteSingleton(); + } + + void QxThreadPool::runServer() + { + QxServer server(this); + server.setMaxPendingConnections(QxConnect::getSingleton()->getThreadCount()); + quint16 serverPort = (quint16)(QxConnect::getSingleton()->getPort()); + if (!server.listen(QHostAddress::Any, serverPort)) + { + raiseError(QString("[QxOrm] cannot run server : '") + server.errorString() + QString("'"), QxTransaction_ptr()); + return; + } + Q_EMIT serverIsRunning(true, (&server)); + exec(); + Q_EMIT serverIsRunning(false, NULL); + } + + void QxThreadPool::initServices() + { + QMutexLocker locker(&m_mutex); + qRegisterMetaType("qx::service::QxTransaction_ptr"); + qRegisterMetaType("QxTransaction_ptr"); + for (long l = 0; l < QxConnect::getSingleton()->getThreadCount(); l++) + { + QThread *pThread = new QThread(); + QxThread *pWorker = new QxThread(this, pThread); + pWorker->moveToThread(pThread); + QObject::connect(pWorker, SIGNAL(error(const QString &, qx::service::QxTransaction_ptr)), this, SIGNAL(error(const QString &, qx::service::QxTransaction_ptr))); + QObject::connect(pWorker, SIGNAL(transactionStarted(qx::service::QxTransaction_ptr)), this, SIGNAL(transactionStarted(qx::service::QxTransaction_ptr))); + QObject::connect(pWorker, SIGNAL(transactionFinished(qx::service::QxTransaction_ptr)), this, SIGNAL(transactionFinished(qx::service::QxTransaction_ptr))); + QObject::connect(pWorker, SIGNAL(customRequestHandler(qx::service::QxTransaction_ptr)), this, SIGNAL(customRequestHandler(qx::service::QxTransaction_ptr)), Qt::DirectConnection); + QObject::connect(pWorker, SIGNAL(finished()), pThread, SLOT(quit())); + QObject::connect(pThread, SIGNAL(finished()), pThread, SLOT(deleteLater())); + m_lstAllServices.append(pWorker); + m_lstAvailable.enqueue(pWorker); + pWorker->init(); + pThread->start(); + } + } + + void QxThreadPool::clearServices() + { + QMutexLocker locker(&m_mutex); + for (long l = 0; l < m_lstAllServices.count(); l++) + { + m_lstAllServices.at(l)->stop(); + } + for (long l = 0; l < m_lstAllServices.count(); l++) + { + m_lstAllServices.at(l)->wait(); + } + for (long l = 0; l < m_lstAllServices.count(); l++) + { + delete m_lstAllServices.at(l); + } + m_lstAllServices.clear(); + m_lstAvailable.clear(); + } + + } // namespace service +} // namespace qx + +#endif // _QX_ENABLE_QT_NETWORK diff --git a/src/QxService/QxTools.cpp b/src/QxService/QxTools.cpp new file mode 100644 index 0000000..f1bcd10 --- /dev/null +++ b/src/QxService/QxTools.cpp @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK + +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#define QX_SERVICE_TOOLS_HEADER_SIZE (sizeof(quint32) + sizeof(quint16) + sizeof(quint16) + sizeof(quint16)) // (serialized data size) + (serialization type) + (compress data) + (encrypt data) +#define QX_SERVICE_MIN_SIZE_TO_COMPRESS_DATA 2000 + +namespace qx +{ + namespace service + { + + qx_bool QxTools::readSocket(QTcpSocket &socket, QxTransaction &transaction, quint32 &size) + { + while (socket.bytesAvailable() < (qint64)(QX_SERVICE_TOOLS_HEADER_SIZE)) + { + if (!socket.waitForReadyRead(QxConnect::getSingleton()->getMaxWait())) + { + return qx_bool(QX_ERROR_SERVICE_READ_ERROR, "invalid bytes count available to retrieve transaction header (" + socket.errorString() + ")"); + } + } + + quint32 uiSerializedSize = 0; + quint16 uiSerializationType(0), uiCompressData(0), uiEncryptData(0); + QByteArray dataHeader = socket.read((qint64)(QX_SERVICE_TOOLS_HEADER_SIZE)); + qAssert(dataHeader.size() == (int)(QX_SERVICE_TOOLS_HEADER_SIZE)); + QDataStream in(&dataHeader, QIODevice::ReadOnly); + in.setVersion(QDataStream::Qt_4_5); + in >> uiSerializedSize; + in >> uiSerializationType; + in >> uiCompressData; + in >> uiEncryptData; + + while (socket.bytesAvailable() < (qint64)(uiSerializedSize)) + { + if (!socket.waitForReadyRead(QxConnect::getSingleton()->getMaxWait())) + { + return qx_bool(QX_ERROR_SERVICE_READ_ERROR, "invalid bytes count available to retrieve transaction serialized data (" + socket.errorString() + ")"); + } + } + + QByteArray dataSerialized = socket.read((qint64)(uiSerializedSize)); + qAssert(dataSerialized.size() == (int)(uiSerializedSize)); + size = (quint32)(QX_SERVICE_TOOLS_HEADER_SIZE + uiSerializedSize); + + if (uiEncryptData != 0) + { + QxSimpleCrypt crypto(QxConnect::getSingleton()->getEncryptKey()); + QByteArray decrypted = crypto.decryptToByteArray(dataSerialized); + if ((crypto.lastError() != QxSimpleCrypt::ErrorNoError) || decrypted.isEmpty()) + { + return qx_bool(QX_ERROR_UNKNOWN, "an error occured during decryption of data"); + } + dataSerialized = decrypted; + } + + if (uiCompressData != 0) + { + QByteArray uncompressed = qUncompress(dataSerialized); + if (!uncompressed.isEmpty()) + { + dataSerialized = uncompressed; + } + } + + qx_bool bDeserializeOk; + switch (static_cast(uiSerializationType)) + { +#if _QX_SERIALIZE_BINARY + case QxConnect::serialization_binary: + bDeserializeOk = qx::serialization::binary::from_byte_array(transaction, dataSerialized); + break; +#endif // _QX_SERIALIZE_BINARY +#if _QX_SERIALIZE_XML + case QxConnect::serialization_xml: + bDeserializeOk = qx::serialization::xml::from_byte_array(transaction, dataSerialized); + break; +#endif // _QX_SERIALIZE_XML +#if _QX_SERIALIZE_TEXT + case QxConnect::serialization_text: + bDeserializeOk = qx::serialization::text::from_byte_array(transaction, dataSerialized); + break; +#endif // _QX_SERIALIZE_TEXT +#if _QX_SERIALIZE_PORTABLE_BINARY + case QxConnect::serialization_portable_binary: + bDeserializeOk = qx::serialization::portable_binary::from_byte_array(transaction, dataSerialized, 0); + break; +#endif // _QX_SERIALIZE_PORTABLE_BINARY +#if _QX_SERIALIZE_WIDE_BINARY + case QxConnect::serialization_wide_binary: + bDeserializeOk = qx::serialization::wide::binary::from_byte_array(transaction, dataSerialized); + break; +#endif // _QX_SERIALIZE_WIDE_BINARY +#if _QX_SERIALIZE_WIDE_XML + case QxConnect::serialization_wide_xml: + bDeserializeOk = qx::serialization::wide::xml::from_byte_array(transaction, dataSerialized); + break; +#endif // _QX_SERIALIZE_WIDE_XML +#if _QX_SERIALIZE_WIDE_TEXT + case QxConnect::serialization_wide_text: + bDeserializeOk = qx::serialization::wide::text::from_byte_array(transaction, dataSerialized); + break; +#endif // _QX_SERIALIZE_WIDE_TEXT +#if _QX_SERIALIZE_POLYMORPHIC + case QxConnect::serialization_polymorphic_binary: + bDeserializeOk = qx::serialization::polymorphic_binary::from_byte_array(transaction, dataSerialized); + break; + case QxConnect::serialization_polymorphic_xml: + bDeserializeOk = qx::serialization::polymorphic_xml::from_byte_array(transaction, dataSerialized); + break; + case QxConnect::serialization_polymorphic_text: + bDeserializeOk = qx::serialization::polymorphic_text::from_byte_array(transaction, dataSerialized); + break; +#endif // _QX_SERIALIZE_POLYMORPHIC + case QxConnect::serialization_qt: + bDeserializeOk = qx::serialization::qt::from_byte_array(transaction, dataSerialized); + break; +#ifndef _QX_NO_JSON + case QxConnect::serialization_json: + bDeserializeOk = qx::serialization::json::from_byte_array(transaction, dataSerialized); + break; +#endif // _QX_NO_JSON + default: + return qx_bool(QX_ERROR_UNKNOWN, "unknown serialization type to read data from socket"); + } + + return bDeserializeOk; + } + + qx_bool QxTools::writeSocket(QTcpSocket &socket, QxTransaction &transaction, quint32 &size) + { + QByteArray dataSerialized; + std::string owner; + Q_UNUSED(owner); + std::wstring w_owner; + Q_UNUSED(w_owner); + switch (QxConnect::getSingleton()->getSerializationType()) + { +#if _QX_SERIALIZE_BINARY + case QxConnect::serialization_binary: + dataSerialized = qx::serialization::binary::to_byte_array(transaction, (&owner)); + break; +#endif // _QX_SERIALIZE_BINARY +#if _QX_SERIALIZE_XML + case QxConnect::serialization_xml: + dataSerialized = qx::serialization::xml::to_byte_array(transaction, (&owner)); + break; +#endif // _QX_SERIALIZE_XML +#if _QX_SERIALIZE_TEXT + case QxConnect::serialization_text: + dataSerialized = qx::serialization::text::to_byte_array(transaction, (&owner)); + break; +#endif // _QX_SERIALIZE_TEXT +#if _QX_SERIALIZE_PORTABLE_BINARY + case QxConnect::serialization_portable_binary: + dataSerialized = qx::serialization::portable_binary::to_byte_array(transaction, (&owner), 0); + break; +#endif // _QX_SERIALIZE_PORTABLE_BINARY +#if _QX_SERIALIZE_WIDE_BINARY + case QxConnect::serialization_wide_binary: + dataSerialized = qx::serialization::wide::binary::to_byte_array(transaction, (&w_owner)); + break; +#endif // _QX_SERIALIZE_WIDE_BINARY +#if _QX_SERIALIZE_WIDE_XML + case QxConnect::serialization_wide_xml: + dataSerialized = qx::serialization::wide::xml::to_byte_array(transaction, (&w_owner)); + break; +#endif // _QX_SERIALIZE_WIDE_XML +#if _QX_SERIALIZE_WIDE_TEXT + case QxConnect::serialization_wide_text: + dataSerialized = qx::serialization::wide::text::to_byte_array(transaction, (&w_owner)); + break; +#endif // _QX_SERIALIZE_WIDE_TEXT +#if _QX_SERIALIZE_POLYMORPHIC + case QxConnect::serialization_polymorphic_binary: + dataSerialized = qx::serialization::polymorphic_binary::to_byte_array(transaction, (&owner)); + break; + case QxConnect::serialization_polymorphic_xml: + dataSerialized = qx::serialization::polymorphic_xml::to_byte_array(transaction, (&owner)); + break; + case QxConnect::serialization_polymorphic_text: + dataSerialized = qx::serialization::polymorphic_text::to_byte_array(transaction, (&owner)); + break; +#endif // _QX_SERIALIZE_POLYMORPHIC + case QxConnect::serialization_qt: + dataSerialized = qx::serialization::qt::to_byte_array(transaction, (&owner)); + break; +#ifndef _QX_NO_JSON + case QxConnect::serialization_json: + dataSerialized = qx::serialization::json::to_byte_array(transaction, (&owner)); + break; +#endif // _QX_NO_JSON + default: + return qx_bool(QX_ERROR_UNKNOWN, "unknown serialization type to write data to socket"); + } + + if (dataSerialized.isEmpty()) + { + return qx_bool(QX_ERROR_UNKNOWN, "an error occured during serialization of data"); + } + + quint16 uiCompressData = 0; + if (QxConnect::getSingleton()->getCompressData() && (dataSerialized.size() > QX_SERVICE_MIN_SIZE_TO_COMPRESS_DATA)) + { + QByteArray compressed = qCompress(dataSerialized, -1); + if (!compressed.isEmpty()) + { + dataSerialized = compressed; + uiCompressData = 1; + } + } + + quint16 uiEncryptData = 0; + if (QxConnect::getSingleton()->getEncryptData()) + { + QxSimpleCrypt crypto(QxConnect::getSingleton()->getEncryptKey()); + crypto.setCompressionMode(QxSimpleCrypt::CompressionNever); + crypto.setIntegrityProtectionMode(QxSimpleCrypt::ProtectionChecksum); + QByteArray encrypted = crypto.encryptToByteArray(dataSerialized); + if ((crypto.lastError() != QxSimpleCrypt::ErrorNoError) || encrypted.isEmpty()) + { + return qx_bool(QX_ERROR_UNKNOWN, "an error occured during encryption of data"); + } + dataSerialized = encrypted; + uiEncryptData = 1; + } + + QByteArray dataHeader; + QDataStream out(&dataHeader, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_5); + out << (quint32)(dataSerialized.size()); + out << (quint16)(QxConnect::getSingleton()->getSerializationType()); + out << (quint16)(uiCompressData); + out << (quint16)(uiEncryptData); + qAssert(dataHeader.size() == (int)(QX_SERVICE_TOOLS_HEADER_SIZE)); + + qint64 iTotalWritten = 0; + qint64 iTotalToWrite = (qint64)(dataHeader.size()); + const char *pDataHeader = dataHeader.constData(); + while (iTotalWritten < iTotalToWrite) + { + qint64 iWritten = socket.write((pDataHeader + iTotalWritten), (iTotalToWrite - iTotalWritten)); + if (iWritten == -1) + { + break; + } + iTotalWritten += iWritten; + } + + if (iTotalWritten != iTotalToWrite) + { + return qx_bool(QX_ERROR_SERVICE_WRITE_ERROR, "unable to write all data bytes (header) to socket (" + socket.errorString() + ")"); + } + + iTotalWritten = 0; + iTotalToWrite = (qint64)(dataSerialized.size()); + const char *pDataSerialized = dataSerialized.constData(); + while (iTotalWritten < iTotalToWrite) + { + qint64 iWritten = socket.write((pDataSerialized + iTotalWritten), (iTotalToWrite - iTotalWritten)); + if (iWritten == -1) + { + break; + } + iTotalWritten += iWritten; + } + + size = (quint32)(dataHeader.size() + dataSerialized.size()); + return ((iTotalWritten == iTotalToWrite) ? qx_bool(true) : qx_bool(QX_ERROR_SERVICE_WRITE_ERROR, "unable to write all data bytes (serialized data) to socket (" + socket.errorString() + ")")); + } + + } // namespace service +} // namespace qx + +#endif // _QX_ENABLE_QT_NETWORK diff --git a/src/QxService/QxTransaction.cpp b/src/QxService/QxTransaction.cpp new file mode 100644 index 0000000..d0e9dc9 --- /dev/null +++ b/src/QxService/QxTransaction.cpp @@ -0,0 +1,612 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifdef _QX_ENABLE_QT_NETWORK + +#include + +#include + +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +#include +#include + +#include + +QX_REGISTER_INTERNAL_HELPER_START_FILE_CPP(qx::service::QxTransaction) + +namespace qx +{ + namespace service + { + + void QxTransaction::clear() + { + m_sTransactionId = QString(); + m_uiInputTransactionSize = 0; + m_uiOutputTransactionSize = 0; + m_dtTransactionBegin = QDateTime(); + m_dtTransactionRequestSent = QDateTime(); + m_dtTransactionRequestReceived = QDateTime(); + m_dtTransactionReplySent = QDateTime(); + m_dtTransactionReplyReceived = QDateTime(); + m_dtTransactionEnd = QDateTime(); + m_sIpSource = QString(); + m_sIpTarget = QString(); + m_lPortSource = 0; + m_lPortTarget = 0; + m_sServiceName = QString(); + m_sServiceMethod = QString(); + m_bMessageReturn = qx_bool(); + m_pInputParameter.reset(); + m_pOutputParameter.reset(); + m_pServiceInstance.reset(); + } + + void QxTransaction::executeServer() + { + if (m_sServiceName.isEmpty()) + { + m_bMessageReturn = qx_bool(QX_ERROR_SERVICE_NOT_SPECIFIED, "[QxOrm] empty service name => cannot instantiate service and execute process"); + return; + } + if (m_sServiceMethod.isEmpty()) + { + m_bMessageReturn = qx_bool(QX_ERROR_SERVICE_NOT_SPECIFIED, "[QxOrm] empty service method => cannot execute process"); + return; + } + + qx::service::IxService *ptr = qx::create_nude_ptr(m_sServiceName); + if (ptr == NULL) + { + m_bMessageReturn = qx_bool(QX_ERROR_SERVICE_INVALID, "[QxOrm] invalid service name => cannot instantiate service and execute process"); + return; + } + m_pServiceInstance = IxService_ptr(ptr); + m_pServiceInstance->registerClass(); + m_pServiceInstance->setInputParameter(m_pInputParameter); + m_pServiceInstance->setServiceMethodName(m_sServiceMethod); + + try + { + m_pServiceInstance->onBeforeProcess(); + qx_bool bInvokeOk = qx::QxClassX::invoke(m_sServiceName, m_sServiceMethod, (*m_pServiceInstance)); + if (!bInvokeOk) + { + m_bMessageReturn = qx_bool(QX_ERROR_SERVICE_INVALID, "[QxOrm] invalid service method => cannot execute process"); + return; + } + m_pOutputParameter = m_pServiceInstance->getOutputParameter_BaseClass(); + m_bMessageReturn = m_pServiceInstance->getMessageReturn(); + m_pServiceInstance->onAfterProcess(); + } + catch (const qx::exception &x) + { + m_bMessageReturn = x.toQxBool(); + } + catch (const std::exception &e) + { + QString msg(e.what()); + if (msg.isEmpty()) + { + msg = "[QxOrm] unexpected error occured executing service method"; + }; + m_bMessageReturn = qx_bool(QX_ERROR_UNKNOWN, msg); + } + catch (...) + { + m_bMessageReturn = qx_bool(QX_ERROR_UNKNOWN, "[QxOrm] unknown error occured executing service method"); + } + m_pServiceInstance.reset(); + } + + qx_bool QxTransaction::writeSocketServer(QTcpSocket &socket) + { + quint32 uiTransactionSize = 0; + IxParameter_ptr pInputBackup = getInputParameter(); + setInputParameter(IxParameter_ptr()); + setTransactionReplySent(QDateTime::currentDateTime()); + qx_bool bWriteOk = QxTools::writeSocket(socket, (*this), uiTransactionSize); + setInputParameter(pInputBackup); + setOutputTransactionSize(uiTransactionSize); + return bWriteOk; + } + + qx_bool QxTransaction::readSocketServer(QTcpSocket &socket) + { + quint32 uiTransactionSize = 0; + qx_bool bReadOk = QxTools::readSocket(socket, (*this), uiTransactionSize); + if (!bReadOk) + { + return bReadOk; + } + setInputTransactionSize(uiTransactionSize); + setTransactionRequestReceived(QDateTime::currentDateTime()); + return bReadOk; + } + + void QxTransaction::executeClient(IxService *pService, const QString &sMethod) + { + if ((pService == NULL) || sMethod.isEmpty()) + { + qAssert(false); + return; + } + if (pService->getServiceName().isEmpty()) + { + pService->setMessageReturn(qx_bool(QX_ERROR_SERVICE_NOT_SPECIFIED, "[QxOrm] empty service name")); + return; + } + std::unique_ptr socket; + pService->registerClass(); + +#ifndef QT_NO_SSL + bool bSSLEnabled = QxConnect::getSingleton()->getSSLEnabled(); + if (bSSLEnabled) + { + socket.reset(initSocketSSL()); + } + else + { + socket.reset(new QTcpSocket()); + } +#else // QT_NO_SSL + socket.reset(new QTcpSocket()); +#endif // QT_NO_SSL + + QString serverName = QxConnect::getSingleton()->getIp(); + long serverPort = QxConnect::getSingleton()->getPort(); + +#ifndef QT_NO_SSL + if (bSSLEnabled) + { + static_cast(socket.get())->connectToHostEncrypted(serverName, serverPort); + } + else + { + socket->connectToHost(serverName, serverPort); + } + if (bSSLEnabled && (!checkSocketSSLEncrypted(socket.get()))) + { + pService->setMessageReturn(qx_bool(QX_ERROR_SERVICE_SSL_ENCRYPTED, "[QxOrm] SSL socket encrypted error : " + socket->errorString())); + return; + } +#else // QT_NO_SSL + socket->connectToHost(serverName, serverPort); +#endif // QT_NO_SSL + + if (!socket->waitForConnected(QxConnect::getSingleton()->getMaxWait())) + { + pService->setMessageReturn(qx_bool(QX_ERROR_SERVER_NOT_FOUND, "[QxOrm] unable to connect to server : " + socket->errorString())); + return; + } + + if (m_sTransactionId.isEmpty()) + { + setTransactionId(QUuid::createUuid().toString()); + } + + setIpSource(socket->localAddress().toString()); + setPortSource(socket->localPort()); + setIpTarget(serverName); + setPortTarget(serverPort); + setServiceName(pService->getServiceName()); + setServiceMethod(sMethod); + setTransactionBegin(QDateTime::currentDateTime()); + setInputParameter(pService->getInputParameter_BaseClass()); + + qx_bool bWriteOk = writeSocketClient(*socket); + if (!bWriteOk) + { + pService->setMessageReturn(qx_bool(QX_ERROR_SERVICE_WRITE_ERROR, QString("[QxOrm] unable to write request to socket : '") + bWriteOk.getDesc() + QString("'"))); + return; + } + qx_bool bReadOk = readSocketClient(*socket); + if (!bReadOk) + { + pService->setMessageReturn(qx_bool(QX_ERROR_SERVICE_READ_ERROR, QString("[QxOrm] unable to read reply from socket : '") + bReadOk.getDesc() + QString("'"))); + return; + } + + pService->setOutputParameter(getOutputParameter()); + pService->setMessageReturn(getMessageReturn()); + setTransactionEnd(QDateTime::currentDateTime()); + socket->disconnectFromHost(); + if (socket->state() != QAbstractSocket::UnconnectedState) + { + socket->waitForDisconnected(QxConnect::getSingleton()->getMaxWait()); + } + } + + qx_bool QxTransaction::writeSocketClient(QTcpSocket &socket) + { + quint32 uiTransactionSize = 0; + qx_bool bWriteOk = QxTools::writeSocket(socket, (*this), uiTransactionSize); + if (!bWriteOk) + { + return bWriteOk; + } + setTransactionRequestSent(QDateTime::currentDateTime()); + setInputTransactionSize(uiTransactionSize); + return bWriteOk; + } + + qx_bool QxTransaction::readSocketClient(QTcpSocket &socket) + { + QxTransaction tmp; + quint32 uiTransactionSize = 0; + qx_bool bReadOk = QxTools::readSocket(socket, tmp, uiTransactionSize); + if (!bReadOk) + { + return bReadOk; + } + setTransactionReplyReceived(QDateTime::currentDateTime()); + setTransactionRequestReceived(tmp.getTransactionRequestReceived()); + setTransactionReplySent(tmp.getTransactionReplySent()); + setOutputParameter(tmp.getOutputParameter()); + setMessageReturn(tmp.getMessageReturn()); + setOutputTransactionSize(uiTransactionSize); + return bReadOk; + } + + QString QxTransaction::getInfos() const + { + QString infos; +#ifndef _QX_NO_JSON + infos += "transaction_content (JSON format) :\n" + qx::serialization::json::to_string(*this) + "\n"; +#else // _QX_NO_JSON + infos += "transaction_id :\t\t" + m_sTransactionId + "\n"; + infos += "input_transaction_size :\t\t" + QString::number(m_uiInputTransactionSize) + "\n"; + infos += "output_transaction_size :\t\t" + QString::number(m_uiOutputTransactionSize) + "\n"; + infos += "dt_transaction_begin :\t\t" + m_dtTransactionBegin.toString("yyyyMMddhhmmsszzz") + "\n"; + infos += "dt_transaction_request_sent :\t\t" + m_dtTransactionRequestSent.toString("yyyyMMddhhmmsszzz") + "\n"; + infos += "dt_transaction_request_received :\t\t" + m_dtTransactionRequestReceived.toString("yyyyMMddhhmmsszzz") + "\n"; + infos += "dt_transaction_reply_sent :\t\t" + m_dtTransactionReplySent.toString("yyyyMMddhhmmsszzz") + "\n"; + infos += "dt_transaction_reply_received :\t\t" + m_dtTransactionReplyReceived.toString("yyyyMMddhhmmsszzz") + "\n"; + infos += "dt_transaction_end :\t\t" + m_dtTransactionEnd.toString("yyyyMMddhhmmsszzz") + "\n"; + infos += "ip_source :\t\t" + m_sIpSource + "\n"; + infos += "ip_target :\t\t" + m_sIpTarget + "\n"; + infos += "port_source :\t\t" + QString::number(m_lPortSource) + "\n"; + infos += "port_target :\t\t" + QString::number(m_lPortTarget) + "\n"; + infos += "service_name :\t\t" + m_sServiceName + "\n"; + infos += "service_method :\t\t" + m_sServiceMethod + "\n"; + infos += "message_return :\t\t" + (m_bMessageReturn ? QString("1") : QString("0")) + (m_bMessageReturn.getDesc().isEmpty() ? QString() : (QString(", ") + m_bMessageReturn.getDesc())) + ((m_bMessageReturn.getValue() == 0) ? QString() : (QString(", value=") + QString::number(m_bMessageReturn.getValue()))) + "\n"; +#endif // _QX_NO_JSON + return infos; + } + +#ifndef QT_NO_SSL + + QSslSocket *QxTransaction::initSocketSSL() + { + QSslSocket *socket = new QSslSocket(); + QxConnect *settings = QxConnect::getSingleton(); + QSslConfiguration config = settings->getSSLConfiguration(); + if (config.isNull()) + { + config = QSslConfiguration::defaultConfiguration(); + } + QList allCACertificates = settings->getSSLCACertificates(); + config.setCaCertificates(allCACertificates); // because QSslSocket::setCaCertificates() is obsolete + + socket->setSslConfiguration(config); + socket->ignoreSslErrors(settings->getSSLIgnoreErrors()); + socket->setProtocol(settings->getSSLProtocol()); + socket->setPeerVerifyName(settings->getSSLPeerVerifyName()); + socket->setPeerVerifyMode(settings->getSSLPeerVerifyMode()); + socket->setPeerVerifyDepth(settings->getSSLPeerVerifyDepth()); + socket->setPrivateKey(settings->getSSLPrivateKey()); + socket->setLocalCertificate(settings->getSSLLocalCertificate()); + + return socket; + } + + bool QxTransaction::checkSocketSSLEncrypted(QTcpSocket *socket) + { + if (!QxConnect::getSingleton()->getSSLEnabled()) + { + qAssert(false); + return false; + } + QSslSocket *ssl = static_cast(socket); + return ssl->waitForEncrypted(QxConnect::getSingleton()->getMaxWait()); + } + +#endif // QT_NO_SSL + + void execute_client(IxService *pService, const QString &sMethod) + { + if (pService == NULL) + { + qAssert(false); + return; + } + if (sMethod.isEmpty()) + { + qAssert(false); + return; + } + QxTransaction_ptr pTransaction; + pTransaction = std::make_shared(); + pService->setTransaction(pTransaction); + pTransaction->executeClient(pService, sMethod); + pTransaction->setMessageReturn(pService->getMessageReturn()); + } + + } // namespace service +} // namespace qx + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + +namespace boost +{ + namespace serialization + { + + template + inline void qx_save(Archive &ar, const qx::service::QxTransaction &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + + QString sTransactionId = t.getTransactionId(); + quint32 uiInputTransactionSize = t.getInputTransactionSize(); + quint32 uiOutputTransactionSize = t.getOutputTransactionSize(); + QDateTime dtTransactionBegin = t.getTransactionBegin(); + QDateTime dtTransactionRequestSent = t.getTransactionRequestSent(); + QDateTime dtTransactionRequestReceived = t.getTransactionRequestReceived(); + QDateTime dtTransactionReplySent = t.getTransactionReplySent(); + QDateTime dtTransactionReplyReceived = t.getTransactionReplyReceived(); + QDateTime dtTransactionEnd = t.getTransactionEnd(); + QString sIpSource = t.getIpSource(); + QString sIpTarget = t.getIpTarget(); + long lPortSource = t.getPortSource(); + long lPortTarget = t.getPortTarget(); + QString sServiceName = t.getServiceName(); + QString sServiceMethod = t.getServiceMethod(); + qx_bool bMessageReturn = t.getMessageReturn(); + qx::service::IxParameter_ptr pInputParameter = t.getInputParameter(); + qx::service::IxParameter_ptr pOutputParameter = t.getOutputParameter(); + + ar << boost::serialization::make_nvp("transaction_id", sTransactionId); + ar << boost::serialization::make_nvp("input_transaction_size", uiInputTransactionSize); + ar << boost::serialization::make_nvp("output_transaction_size", uiOutputTransactionSize); + ar << boost::serialization::make_nvp("dt_transaction_begin", dtTransactionBegin); + ar << boost::serialization::make_nvp("dt_transaction_request_sent", dtTransactionRequestSent); + ar << boost::serialization::make_nvp("dt_transaction_request_received", dtTransactionRequestReceived); + ar << boost::serialization::make_nvp("dt_transaction_reply_sent", dtTransactionReplySent); + ar << boost::serialization::make_nvp("dt_transaction_reply_received", dtTransactionReplyReceived); + ar << boost::serialization::make_nvp("dt_transaction_end", dtTransactionEnd); + ar << boost::serialization::make_nvp("ip_source", sIpSource); + ar << boost::serialization::make_nvp("ip_target", sIpTarget); + ar << boost::serialization::make_nvp("port_source", lPortSource); + ar << boost::serialization::make_nvp("port_target", lPortTarget); + ar << boost::serialization::make_nvp("service_name", sServiceName); + ar << boost::serialization::make_nvp("service_method", sServiceMethod); + ar << boost::serialization::make_nvp("message_return", bMessageReturn); + ar << boost::serialization::make_nvp("input_parameter", pInputParameter); + ar << boost::serialization::make_nvp("output_parameter", pOutputParameter); + } + + template + inline void qx_load(Archive &ar, qx::service::QxTransaction &t, const unsigned int file_version) + { + Q_UNUSED(file_version); + QString sTransactionId; + quint32 uiInputTransactionSize(0); + quint32 uiOutputTransactionSize(0); + QDateTime dtTransactionBegin; + QDateTime dtTransactionRequestSent; + QDateTime dtTransactionRequestReceived; + QDateTime dtTransactionReplySent; + QDateTime dtTransactionReplyReceived; + QDateTime dtTransactionEnd; + QString sIpSource; + QString sIpTarget; + long lPortSource(0); + long lPortTarget(0); + QString sServiceName; + QString sServiceMethod; + qx_bool bMessageReturn; + qx::service::IxParameter_ptr pInputParameter; + qx::service::IxParameter_ptr pOutputParameter; + + ar >> boost::serialization::make_nvp("transaction_id", sTransactionId); + ar >> boost::serialization::make_nvp("input_transaction_size", uiInputTransactionSize); + ar >> boost::serialization::make_nvp("output_transaction_size", uiOutputTransactionSize); + ar >> boost::serialization::make_nvp("dt_transaction_begin", dtTransactionBegin); + ar >> boost::serialization::make_nvp("dt_transaction_request_sent", dtTransactionRequestSent); + ar >> boost::serialization::make_nvp("dt_transaction_request_received", dtTransactionRequestReceived); + ar >> boost::serialization::make_nvp("dt_transaction_reply_sent", dtTransactionReplySent); + ar >> boost::serialization::make_nvp("dt_transaction_reply_received", dtTransactionReplyReceived); + ar >> boost::serialization::make_nvp("dt_transaction_end", dtTransactionEnd); + ar >> boost::serialization::make_nvp("ip_source", sIpSource); + ar >> boost::serialization::make_nvp("ip_target", sIpTarget); + ar >> boost::serialization::make_nvp("port_source", lPortSource); + ar >> boost::serialization::make_nvp("port_target", lPortTarget); + ar >> boost::serialization::make_nvp("service_name", sServiceName); + ar >> boost::serialization::make_nvp("service_method", sServiceMethod); + ar >> boost::serialization::make_nvp("message_return", bMessageReturn); + ar >> boost::serialization::make_nvp("input_parameter", pInputParameter); + ar >> boost::serialization::make_nvp("output_parameter", pOutputParameter); + + t.setTransactionId(sTransactionId); + t.setInputTransactionSize(uiInputTransactionSize); + t.setOutputTransactionSize(uiOutputTransactionSize); + t.setTransactionBegin(dtTransactionBegin); + t.setTransactionRequestSent(dtTransactionRequestSent); + t.setTransactionRequestReceived(dtTransactionRequestReceived); + t.setTransactionReplySent(dtTransactionReplySent); + t.setTransactionReplyReceived(dtTransactionReplyReceived); + t.setTransactionEnd(dtTransactionEnd); + t.setIpSource(sIpSource); + t.setIpTarget(sIpTarget); + t.setPortSource(lPortSource); + t.setPortTarget(lPortTarget); + t.setServiceName(sServiceName); + t.setServiceMethod(sServiceMethod); + t.setMessageReturn(bMessageReturn); + t.setInputParameter(pInputParameter); + t.setOutputParameter(pOutputParameter); + } + + } // namespace serialization +} // namespace boost + +#endif // _QX_ENABLE_BOOST_SERIALIZATION + +QX_REGISTER_INTERNAL_HELPER_END_FILE_CPP(qx::service::QxTransaction) + +QDataStream &operator<<(QDataStream &stream, const qx::service::QxTransaction &t) +{ + stream << t.m_sTransactionId; + stream << t.m_uiInputTransactionSize; + stream << t.m_uiOutputTransactionSize; + stream << t.m_dtTransactionBegin; + stream << t.m_dtTransactionRequestSent; + stream << t.m_dtTransactionRequestReceived; + stream << t.m_dtTransactionReplySent; + stream << t.m_dtTransactionReplyReceived; + stream << t.m_dtTransactionEnd; + stream << t.m_sIpSource; + stream << t.m_sIpTarget; + stream << (qint64)(t.m_lPortSource); + stream << (qint64)(t.m_lPortTarget); + stream << t.m_sServiceName; + stream << t.m_sServiceMethod; + stream << t.m_bMessageReturn; + + qint16 iIsNull = (t.m_pInputParameter ? 0 : 1); + stream << iIsNull; + if (t.m_pInputParameter) + { + t.m_pInputParameter->registerClass(); + QString sClassName = t.m_pInputParameter->getClassName(); + stream << sClassName; + t.m_pInputParameter->save(stream); + } + + iIsNull = (t.m_pOutputParameter ? 0 : 1); + stream << iIsNull; + if (t.m_pOutputParameter) + { + t.m_pOutputParameter->registerClass(); + QString sClassName = t.m_pOutputParameter->getClassName(); + stream << sClassName; + t.m_pOutputParameter->save(stream); + } + + return stream; +} + +QDataStream &operator>>(QDataStream &stream, qx::service::QxTransaction &t) +{ + qint64 iTemp = 0; + stream >> t.m_sTransactionId; + stream >> t.m_uiInputTransactionSize; + stream >> t.m_uiOutputTransactionSize; + stream >> t.m_dtTransactionBegin; + stream >> t.m_dtTransactionRequestSent; + stream >> t.m_dtTransactionRequestReceived; + stream >> t.m_dtTransactionReplySent; + stream >> t.m_dtTransactionReplyReceived; + stream >> t.m_dtTransactionEnd; + stream >> t.m_sIpSource; + stream >> t.m_sIpTarget; + stream >> iTemp; + t.m_lPortSource = static_cast(iTemp); + stream >> iTemp; + t.m_lPortTarget = static_cast(iTemp); + stream >> t.m_sServiceName; + stream >> t.m_sServiceMethod; + stream >> t.m_bMessageReturn; + + qint16 iIsNull = 0; + stream >> iIsNull; + if (iIsNull) + { + t.m_pInputParameter.reset(); + } + else + { + QString sClassName; + stream >> sClassName; + qx::service::IxParameter *ptr = qx::create_nude_ptr(sClassName); + if (!ptr) + { + qAssertMsg(false, "[QxOrm] qx::service::QxTransaction, loading QDataStream", "unable to create nude pointer for input parameter"); + } + else + { + ptr->registerClass(); + ptr->load(stream); + } + t.m_pInputParameter.reset(ptr); + } + + iIsNull = 0; + stream >> iIsNull; + if (iIsNull) + { + t.m_pOutputParameter.reset(); + } + else + { + QString sClassName; + stream >> sClassName; + qx::service::IxParameter *ptr = qx::create_nude_ptr(sClassName); + if (!ptr) + { + qAssertMsg(false, "[QxOrm] qx::service::QxTransaction, loading QDataStream", "unable to create nude pointer for output parameter"); + } + else + { + ptr->registerClass(); + ptr->load(stream); + } + t.m_pOutputParameter.reset(ptr); + } + + return stream; +} + +#endif // _QX_ENABLE_QT_NETWORK diff --git a/src/QxSingleton/IxSingleton.cpp b/src/QxSingleton/IxSingleton.cpp new file mode 100644 index 0000000..67cef3b --- /dev/null +++ b/src/QxSingleton/IxSingleton.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include +#include + +#include + +namespace qx +{ + + IxSingleton::IxSingleton(const QString &sKey) : m_sKeySingleton(sKey) + { +#ifdef _QX_TRACE_CONSTRUCTOR_DESTRUCTOR + qDebug("[QxOrm] qx::IxSingleton constructor '%s'", qPrintable(m_sKeySingleton)); +#endif // _QX_TRACE_CONSTRUCTOR_DESTRUCTOR + + QxSingletonX::addSingleton(m_sKeySingleton, this); + } + + IxSingleton::~IxSingleton() + { +#ifdef _QX_TRACE_CONSTRUCTOR_DESTRUCTOR + qDebug("[QxOrm] qx::IxSingleton destructor '%s'", qPrintable(m_sKeySingleton)); +#endif // _QX_TRACE_CONSTRUCTOR_DESTRUCTOR + + QxSingletonX::removeSingleton(m_sKeySingleton); + } + + void IxSingleton::initQxSingletonX() + { + static bool bDone = false; + if (!bDone && QCoreApplication::instance()) + { + bDone = true; + QxSingletonX::getSingleton(); + } + } + +} // namespace qx diff --git a/src/QxSingleton/QxSingletonInit.cpp b/src/QxSingleton/QxSingletonInit.cpp new file mode 100644 index 0000000..49c6860 --- /dev/null +++ b/src/QxSingleton/QxSingletonInit.cpp @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::QxClass) +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::QxDataMemberX) + +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::QxClass) +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::QxDataMemberX) + +#ifdef _QX_ENABLE_QT_NETWORK + +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::QxClass) +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::QxDataMemberX) + +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::QxClass) +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::QxDataMemberX) + +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::QxClass) +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::QxDataMemberX) + +#endif // _QX_ENABLE_QT_NETWORK diff --git a/src/QxSingleton/QxSingletonX.cpp b/src/QxSingleton/QxSingletonX.cpp new file mode 100644 index 0000000..a781fc7 --- /dev/null +++ b/src/QxSingleton/QxSingletonX.cpp @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +#include + +#include + +#ifdef _QX_STATIC_BUILD +#undef _QX_USE_QX_SINGLETON_X +#define _QX_USE_QX_SINGLETON_X 0 +#endif // _QX_STATIC_BUILD + +QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::QxSingletonX) + +namespace qx +{ + + /* -- We replaced these static class members by static class methods to avoid (with some compilers) the 'static initialization order fiasco' -- + QHash QxSingletonX::m_mapSingletonX; // replaced by 'getMapSingletonX()' + QMutex QxSingletonX::m_oMutexSingletonX; // replaced by 'getMutexSingletonX()' + bool QxSingletonX::m_bOnClearSingletonX = false; // replaced by 'getOnClearSingletonX()' + */ + + QxSingletonX::QxSingletonX() : QxSingleton("qx::QxSingletonX") + { +#if _QX_USE_QX_SINGLETON_X + int iResult = std::atexit(&QxSingletonX::deleteAllSingleton); + Q_UNUSED(iResult); + QString sAssertMsg = QString("cannot register 'qx::QxSingletonX::deleteAllSingleton()' function at exit program (using 'std::atexit')"); + Q_UNUSED(sAssertMsg); + qAssertMsg((iResult == 0), "[QxOrm] qx::QxSingletonX() constructor", qPrintable(sAssertMsg)); +#endif // _QX_USE_QX_SINGLETON_X + } + + QHash &QxSingletonX::getMapSingletonX() + { + // There is a 'very small' memory leak here : this is to avoid (with some compilers) the 'static initialization order fiasco' + // More details here : https://isocpp.org/wiki/faq/ctors#static-init-order + static QHash *p = new QHash(); + return (*p); + } + + QMutex *QxSingletonX::getMutexSingletonX() + { + // There is a 'very small' memory leak here : this is to avoid (with some compilers) the 'static initialization order fiasco' + // More details here : https://isocpp.org/wiki/faq/ctors#static-init-order + static QMutex *p = new QMutex(); + return p; + } + + bool &QxSingletonX::getOnClearSingletonX() + { + // There is a 'very small' memory leak here : this is to avoid (with some compilers) the 'static initialization order fiasco' + // More details here : https://isocpp.org/wiki/faq/ctors#static-init-order + static bool *p = new bool(false); + return (*p); + } + + bool QxSingletonX::addSingleton(const QString &sKey, IxSingleton *pSingleton) + { +#if _QX_USE_QX_SINGLETON_X +#ifdef _QX_TRACE_CONSTRUCTOR_DESTRUCTOR + qDebug("[QxOrm] qx::QxSingletonX::addSingleton() : '%s'", qPrintable(sKey)); +#endif // _QX_TRACE_CONSTRUCTOR_DESTRUCTOR + QMutex *pMutex = (QCoreApplication::instance() ? getMutexSingletonX() : NULL); + QMutexLocker locker(pMutex); + bool bExist = getMapSingletonX().contains(sKey); + QString sAssertMsg = QString("singleton key '%1' already exists or is empty").arg(sKey); + Q_UNUSED(sAssertMsg); + qAssertMsg((!bExist && !sKey.isEmpty()), "[QxOrm] qx::QxSingletonX::addSingleton()", qPrintable(sAssertMsg)); + if (!pSingleton || bExist || sKey.isEmpty()) + { + return false; + } + getMapSingletonX().insert(sKey, pSingleton); + return true; +#else // _QX_USE_QX_SINGLETON_X + Q_UNUSED(sKey); + Q_UNUSED(pSingleton); + return false; +#endif // _QX_USE_QX_SINGLETON_X + } + + bool QxSingletonX::removeSingleton(const QString &sKey) + { +#if _QX_USE_QX_SINGLETON_X + bool &bOnClearSingletonX = getOnClearSingletonX(); + if (bOnClearSingletonX) + { + return false; + } +#ifdef _QX_TRACE_CONSTRUCTOR_DESTRUCTOR + qDebug("[QxOrm] qx::QxSingletonX::removeSingleton() : '%s'", qPrintable(sKey)); +#endif // _QX_TRACE_CONSTRUCTOR_DESTRUCTOR + QMutex *pMutex = (QCoreApplication::instance() ? getMutexSingletonX() : NULL); + QMutexLocker locker(pMutex); + QString sAssertMsg = QString("singleton key '%1' doesn't exist in the singleton manager").arg(sKey); + Q_UNUSED(sAssertMsg); + qAssertMsg((getMapSingletonX().contains(sKey)), "[QxOrm] qx::QxSingletonX::removeSingleton()", qPrintable(sAssertMsg)); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + bool bRemoveOk = getMapSingletonX().remove(sKey); +#else // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + bool bRemoveOk = (getMapSingletonX().remove(sKey) > 0); +#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + return bRemoveOk; +#else // _QX_USE_QX_SINGLETON_X + Q_UNUSED(sKey); + return false; +#endif // _QX_USE_QX_SINGLETON_X + } + + void QxSingletonX::deleteAllSingleton() + { +#if _QX_USE_QX_SINGLETON_X +#ifdef _QX_TRACE_CONSTRUCTOR_DESTRUCTOR + qDebug("[QxOrm] qx::QxSingletonX : %s", "execute deleteAllSingleton() function"); +#endif // _QX_TRACE_CONSTRUCTOR_DESTRUCTOR + QMutex *pMutex = (QCoreApplication::instance() ? getMutexSingletonX() : NULL); + QMutexLocker locker(pMutex); + bool &bOnClearSingletonX = getOnClearSingletonX(); + bOnClearSingletonX = true; + + QHash &mapSingletonX = getMapSingletonX(); + QHashIterator itr(mapSingletonX); + while (itr.hasNext()) + { + itr.next(); + IxSingleton *pSingleton = itr.value(); + if (pSingleton && (pSingleton != QxSingletonX::getSingleton())) + pSingleton->deleteInstance(); + } + + QxSingletonX::deleteSingleton(); + mapSingletonX.clear(); + bOnClearSingletonX = false; +#endif // _QX_USE_QX_SINGLETON_X + } + +} // namespace qx diff --git a/src/QxTraits/unit_test_is_container.cpp b/src/QxTraits/unit_test_is_container.cpp new file mode 100644 index 0000000..4775183 --- /dev/null +++ b/src/QxTraits/unit_test_is_container.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_MODE_RELEASE + +#include + +#include +#include +#include + +#include + +#include + +namespace qx +{ + namespace unit_test + { + + void unit_test_is_container() + { + static_assert(!qx::trait::is_container::value, "unit_test_is_container"); + static_assert(!qx::trait::is_container::value, "unit_test_is_container"); + static_assert(!qx::trait::is_container::value, "unit_test_is_container"); + static_assert(!qx::trait::is_container::value, "unit_test_is_container"); + + typedef qx::QxCollection qx_coll_string_to_double; + typedef std::vector std_vector_object; + typedef QHash qt_hash_double_to_string; + typedef std::set std_set_int; + + static_assert(qx::trait::is_container::value, "unit_test_is_container"); + static_assert(qx::trait::is_container::value, "unit_test_is_container"); + static_assert(qx::trait::is_container::value, "unit_test_is_container"); + static_assert(qx::trait::is_container::value, "unit_test_is_container"); + + static_assert(qx::trait::is_std_vector>::value, "unit_test_is_container"); + static_assert(!qx::trait::is_qt_vector>::value, "unit_test_is_container"); + + static_assert(qx::trait::is_container_to_pod::value, "unit_test_is_container"); + static_assert(qx::trait::is_container_key_value::value, "unit_test_is_container"); + +#ifdef _QX_ENABLE_BOOST + + typedef boost::unordered_map boost_unordered_map_string_to_object_ptr; + + static_assert(!qx::trait::is_boost_unordered_map>::value, "unit_test_is_container"); + static_assert(qx::trait::is_container_key_value::value, "unit_test_is_container"); + +#endif // _QX_ENABLE_BOOST + } + + } // namespace unit_test +} // namespace qx + +#endif // _QX_MODE_RELEASE diff --git a/src/QxTraits/unit_test_is_smart_ptr.cpp b/src/QxTraits/unit_test_is_smart_ptr.cpp new file mode 100644 index 0000000..2dafebb --- /dev/null +++ b/src/QxTraits/unit_test_is_smart_ptr.cpp @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_MODE_RELEASE + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +namespace qx +{ + namespace unit_test + { + + void unit_test_is_smart_ptr() + { +#ifdef _QX_ENABLE_BOOST + + static_assert(!qx::trait::is_boost_intrusive_ptr::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_boost_intrusive_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_boost_intrusive_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_boost_intrusive_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_boost_intrusive_ptr>::value, "unit_test_is_smart_ptr"); + + static_assert(!qx::trait::is_boost_scoped_ptr::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_boost_scoped_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_boost_scoped_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_boost_scoped_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_boost_scoped_ptr>::value, "unit_test_is_smart_ptr"); + + static_assert(!qx::trait::is_boost_shared_ptr::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_boost_shared_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_boost_shared_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_boost_shared_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_boost_shared_ptr>::value, "unit_test_is_smart_ptr"); + + static_assert(!qx::trait::is_boost_weak_ptr::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_boost_weak_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_boost_weak_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_boost_weak_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_boost_weak_ptr>::value, "unit_test_is_smart_ptr"); + + static_assert(!qx::trait::is_qt_shared_data_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_qt_shared_data_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_qt_shared_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_qt_shared_ptr>::value, "unit_test_is_smart_ptr"); + + static_assert(!qx::trait::is_qt_weak_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_qt_weak_ptr>::value, "unit_test_is_smart_ptr"); + + static_assert(qx::trait::is_smart_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_smart_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_smart_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_smart_ptr>::value, "unit_test_is_smart_ptr"); + + static_assert(!qx::trait::is_boost_shared_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_boost_scoped_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_boost_weak_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_boost_intrusive_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_boost_shared_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_boost_weak_ptr>::value, "unit_test_is_smart_ptr"); + +#endif // _QX_ENABLE_BOOST + + static_assert(!qx::trait::is_qt_shared_data_ptr::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_qt_shared_data_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_qt_shared_data_ptr>::value, "unit_test_is_smart_ptr"); + + static_assert(!qx::trait::is_qt_shared_ptr::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_qt_shared_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_qt_shared_ptr>::value, "unit_test_is_smart_ptr"); + + static_assert(!qx::trait::is_qt_weak_ptr::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_qt_weak_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_qt_weak_ptr>::value, "unit_test_is_smart_ptr"); + + static_assert(!qx::trait::is_smart_ptr::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_smart_ptr::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_smart_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_smart_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_smart_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_smart_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_smart_ptr>::value, "unit_test_is_smart_ptr"); + + static_assert(!qx::trait::is_smart_ptr_to_pod>::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_smart_ptr_to_pod>::value, "unit_test_is_smart_ptr"); + + static_assert(qx::trait::is_std_shared_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_std_weak_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_smart_ptr>::value, "unit_test_is_smart_ptr"); + + static_assert(!qx::trait::is_qt_shared_data_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_std_weak_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(qx::trait::is_smart_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_smart_ptr_to_pod>::value, "unit_test_is_smart_ptr"); + + static_assert(qx::trait::is_std_unique_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_qt_shared_data_ptr>::value, "unit_test_is_smart_ptr"); + static_assert(!qx::trait::is_qt_shared_ptr>::value, "unit_test_is_smart_ptr"); + } + + } // namespace unit_test +} // namespace qx + +#endif // _QX_MODE_RELEASE diff --git a/src/QxValidator/IxValidator.cpp b/src/QxValidator/IxValidator.cpp new file mode 100644 index 0000000..bad2814 --- /dev/null +++ b/src/QxValidator/IxValidator.cpp @@ -0,0 +1,323 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + +#include +#include + +#include + +#include + +#include + +namespace qx +{ + + IxValidator::IxValidator(IxValidator::validator_type type) : QxPropertyBag(), m_type(type), m_pDataMember(NULL) { initDefaultMessage(); } + + IxValidator::~IxValidator() { ; } + + IxValidator::validator_type IxValidator::getType() const { return m_type; } + + QString IxValidator::getMessage() const { return m_message; } + + QString IxValidator::getGroup() const { return m_group; } + + QVariant IxValidator::getConstraint() const { return ((m_Constraints.count() > 0) ? m_Constraints.at(0) : QVariant()); } + + QVariantList IxValidator::getConstraints() const { return m_Constraints; } + + IxDataMember *IxValidator::getDataMember() const { return m_pDataMember; } + + void IxValidator::setMessage(const QString &s) { m_message = s; } + + void IxValidator::setGroup(const QString &s) { m_group = s; } + + void IxValidator::setConstraint(const QVariant &v) + { + m_Constraints.clear(); + m_Constraints.append(v); + } + + void IxValidator::setConstraints(const QVariantList &lst) { m_Constraints = lst; } + + void IxValidator::setDataMember(IxDataMember *p) { m_pDataMember = p; } + + void IxValidator::validate(void *pOwner, QxInvalidValueX &lstInvalidValues) const + { + if (!pOwner) + { + qAssert(false); + return; + } + if (!m_pDataMember) + { + return; + } + QVariant val = m_pDataMember->toVariant(pOwner); + + switch (m_type) + { + case not_null: + validateNotNull(val, lstInvalidValues); + break; + case not_empty: + validateNotEmpty(val, lstInvalidValues); + break; + case min_value: + validateMinValue(val, lstInvalidValues); + break; + case max_value: + validateMaxValue(val, lstInvalidValues); + break; + case min_length: + validateMinLength(val, lstInvalidValues); + break; + case max_length: + validateMaxLength(val, lstInvalidValues); + break; + case date_past: + validateDatePast(val, lstInvalidValues); + break; + case date_future: + validateDateFuture(val, lstInvalidValues); + break; + case min_decimal: + validateMinDecimal(val, lstInvalidValues); + break; + case max_decimal: + validateMaxDecimal(val, lstInvalidValues); + break; + case regular_expression: + validateRegularExpression(val, lstInvalidValues); + break; + case e_mail: + validateEMail(val, lstInvalidValues); + break; + default: + break; + } + } + + void IxValidator::initDefaultMessage() + { + QHash *lstMessage = QxClassX::getAllValidatorMessage(); + if (!lstMessage) + { + qAssert(false); + return; + } + + switch (m_type) + { + case not_null: + m_message = lstMessage->value("not_null"); + break; + case not_empty: + m_message = lstMessage->value("not_empty"); + break; + case min_value: + m_message = lstMessage->value("min_value"); + break; + case max_value: + m_message = lstMessage->value("max_value"); + break; + case min_length: + m_message = lstMessage->value("min_length"); + break; + case max_length: + m_message = lstMessage->value("max_length"); + break; + case date_past: + m_message = lstMessage->value("date_past"); + break; + case date_future: + m_message = lstMessage->value("date_future"); + break; + case min_decimal: + m_message = lstMessage->value("min_decimal"); + break; + case max_decimal: + m_message = lstMessage->value("max_decimal"); + break; + case regular_expression: + m_message = lstMessage->value("regular_expression"); + break; + case e_mail: + m_message = lstMessage->value("e_mail"); + break; + default: + m_message = ""; + break; + } + } + + void IxValidator::validateNotNull(const QVariant &v, QxInvalidValueX &lstInvalidValues) const + { + if (v.isNull()) + { + lstInvalidValues.insert(this); + } + } + + void IxValidator::validateNotEmpty(const QVariant &v, QxInvalidValueX &lstInvalidValues) const + { + QString s = v.toString(); + if (s.size() <= 0) + { + lstInvalidValues.insert(this); + } + } + + void IxValidator::validateMinValue(const QVariant &v, QxInvalidValueX &lstInvalidValues) const + { + long d = (long)v.toLongLong(); + long constraint = (long)getConstraint().toLongLong(); + if (d < constraint) + { + lstInvalidValues.insert(this); + } + } + + void IxValidator::validateMaxValue(const QVariant &v, QxInvalidValueX &lstInvalidValues) const + { + long d = (long)v.toLongLong(); + long constraint = (long)getConstraint().toLongLong(); + if (d > constraint) + { + lstInvalidValues.insert(this); + } + } + + void IxValidator::validateMinDecimal(const QVariant &v, QxInvalidValueX &lstInvalidValues) const + { + double d = v.toDouble(); + double constraint = getConstraint().toDouble(); + if (d < constraint) + { + lstInvalidValues.insert(this); + } + } + + void IxValidator::validateMaxDecimal(const QVariant &v, QxInvalidValueX &lstInvalidValues) const + { + double d = v.toDouble(); + double constraint = getConstraint().toDouble(); + if (d > constraint) + { + lstInvalidValues.insert(this); + } + } + + void IxValidator::validateMinLength(const QVariant &v, QxInvalidValueX &lstInvalidValues) const + { + QString s = v.toString(); + long constraint = (long)getConstraint().toLongLong(); + if (s.size() < constraint) + { + lstInvalidValues.insert(this); + } + } + + void IxValidator::validateMaxLength(const QVariant &v, QxInvalidValueX &lstInvalidValues) const + { + QString s = v.toString(); + long constraint = (long)getConstraint().toLongLong(); + if (s.size() > constraint) + { + lstInvalidValues.insert(this); + } + } + + void IxValidator::validateDatePast(const QVariant &v, QxInvalidValueX &lstInvalidValues) const + { + QDateTime dt = v.toDateTime(); + if (!dt.isValid() || (dt > QDateTime::currentDateTime())) + { + lstInvalidValues.insert(this); + } + } + + void IxValidator::validateDateFuture(const QVariant &v, QxInvalidValueX &lstInvalidValues) const + { + QDateTime dt = v.toDateTime(); + if (!dt.isValid() || (dt < QDateTime::currentDateTime())) + { + lstInvalidValues.insert(this); + } + } + + void IxValidator::validateRegularExpression(const QVariant &v, QxInvalidValueX &lstInvalidValues) const + { + QString s = v.toString(); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + QRegularExpression constraint(getConstraint().toString()); + if (!constraint.match(s).hasMatch()) + { + lstInvalidValues.insert(this); + } +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + QRegExp constraint(getConstraint().toString()); + if (!constraint.exactMatch(s)) + { + lstInvalidValues.insert(this); + } +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + } + + void IxValidator::validateEMail(const QVariant &v, QxInvalidValueX &lstInvalidValues) const + { + QString s = v.toString(); + QString pattern = "\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b"; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + QRegularExpression constraint(pattern, QRegularExpression::CaseInsensitiveOption); + if (!constraint.match(s).hasMatch()) + { + lstInvalidValues.insert(this); + } +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + QRegExp constraint(pattern, Qt::CaseInsensitive); + if (!constraint.exactMatch(s)) + { + lstInvalidValues.insert(this); + } +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + } + +} // namespace qx diff --git a/src/QxValidator/IxValidatorX.cpp b/src/QxValidator/IxValidatorX.cpp new file mode 100644 index 0000000..93392e4 --- /dev/null +++ b/src/QxValidator/IxValidatorX.cpp @@ -0,0 +1,292 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include +#include + +#include + +#include +#include + +#include + +namespace qx +{ + + IxValidatorX::IxValidatorX() : m_pClass(NULL) { ; } + + IxValidatorX::~IxValidatorX() { ; } + + void IxValidatorX::setClass(IxClass *p) { m_pClass = p; } + + IxDataMember *IxValidatorX::getDataMember(const QString &sPropertyKey) const + { + if (sPropertyKey.isEmpty() || !m_pClass) + { + return NULL; + } + IxDataMember *pDataMember = QxClassX::getDataMember(m_pClass->getKey(), sPropertyKey, true); + if (!pDataMember) + { + qDebug("[QxOrm] qx::IxValidatorX::getDataMember() : '%s'", "property key not found"); + qAssert(false); + return NULL; + } + return pDataMember; + } + + QStringList IxValidatorX::getAllGroup() const + { + QStringList lstGroup; + for (long l = 0; l < m_lstValidatorByGroup.count(); l++) + { + QString sGroup = m_lstValidatorByGroup.getKeyByIndex(l); + lstGroup.append(sGroup); + } + return lstGroup; + } + + QList IxValidatorX::getAllValidatorByGroup(const QString &group) const + { + if (!m_lstValidatorByGroup.exist(group)) + { + return QList(); + } + type_lst_validator_ptr lst = m_lstValidatorByGroup.getByKey(group); + return (*lst); + } + + QxInvalidValueX IxValidatorX::validate(void *pOwner, const QString &sGroup /* = QString() */) const + { + QxInvalidValueX invalidValues; + if (!m_pClass) + { + qAssert(false); + return invalidValues; + } + + if (m_pClass->getBaseClass()) + { + IxValidatorX *pAllValidator = m_pClass->getBaseClass()->getAllValidator(); + if (pAllValidator) + { + invalidValues.insert(pAllValidator->validate(pOwner, sGroup)); + } + } + + if (!m_lstValidatorByGroup.exist(sGroup)) + { + return invalidValues; + } + type_lst_validator_ptr lstValidator = m_lstValidatorByGroup.getByKey(sGroup); + if (!lstValidator) + { + return invalidValues; + } + + for (long l = 0; l < lstValidator->count(); l++) + { + IxValidator_ptr validator = lstValidator->at(l); + if (validator) + { + validator->validate(pOwner, invalidValues); + } + } + + return invalidValues; + } + + void IxValidatorX::insertIntoGroup(IxValidator_ptr pValidator, const QString &sGroup) + { + if (!pValidator) + { + qAssert(false); + return; + } + + if (!m_lstValidatorByGroup.exist(sGroup)) + { + type_lst_validator_ptr newLstValidator; + newLstValidator = std::make_shared(); + m_lstValidatorByGroup.insert(sGroup, newLstValidator); + } + + type_lst_validator_ptr lstValidator = m_lstValidatorByGroup.getByKey(sGroup); + lstValidator->append(pValidator); + } + + IxValidator_ptr IxValidatorX::createValidator(IxValidator::validator_type type, const QString &sPropertyKey, const QString &sMessage, const QString &sGroup) + { + IxValidator_ptr pValidator; + pValidator = std::make_shared(type); + if (!sMessage.isEmpty()) + { + pValidator->setMessage(sMessage); + } + if (!sGroup.isEmpty()) + { + pValidator->setGroup(sGroup); + } + pValidator->setDataMember(getDataMember(sPropertyKey)); + return pValidator; + } + + IxValidator *IxValidatorX::add_NotNull(const QString &sPropertyKey, const QString &sMessage /* = QString() */, const QString &sGroup /* = QString() */) + { + IxValidator_ptr pValidator = createValidator(IxValidator::not_null, sPropertyKey, sMessage, sGroup); + insertIntoGroup(pValidator, sGroup); + return pValidator.get(); + } + + IxValidator *IxValidatorX::add_NotEmpty(const QString &sPropertyKey, const QString &sMessage /* = QString() */, const QString &sGroup /* = QString() */) + { + IxValidator_ptr pValidator = createValidator(IxValidator::not_empty, sPropertyKey, sMessage, sGroup); + insertIntoGroup(pValidator, sGroup); + return pValidator.get(); + } + + IxValidator *IxValidatorX::add_MinValue(const QString &sPropertyKey, long lMinValue, const QString &sMessage /* = QString() */, const QString &sGroup /* = QString() */) + { + IxValidator_ptr pValidator = createValidator(IxValidator::min_value, sPropertyKey, sMessage, sGroup); + pValidator->setConstraint((qlonglong)lMinValue); + insertIntoGroup(pValidator, sGroup); + return pValidator.get(); + } + + IxValidator *IxValidatorX::add_MaxValue(const QString &sPropertyKey, long lMaxValue, const QString &sMessage /* = QString() */, const QString &sGroup /* = QString() */) + { + IxValidator_ptr pValidator = createValidator(IxValidator::max_value, sPropertyKey, sMessage, sGroup); + pValidator->setConstraint((qlonglong)lMaxValue); + insertIntoGroup(pValidator, sGroup); + return pValidator.get(); + } + + IxValidator *IxValidatorX::add_Range(const QString &sPropertyKey, long lMinValue, long lMaxValue, const QString &sMessage /* = QString() */, const QString &sGroup /* = QString() */) + { + IxValidator_ptr pValidator = createValidator(IxValidator::min_value, sPropertyKey, sMessage, sGroup); + pValidator->setConstraint((qlonglong)lMinValue); + insertIntoGroup(pValidator, sGroup); + + pValidator = createValidator(IxValidator::max_value, sPropertyKey, sMessage, sGroup); + pValidator->setConstraint((qlonglong)lMaxValue); + insertIntoGroup(pValidator, sGroup); + return pValidator.get(); + } + + IxValidator *IxValidatorX::add_MinDecimal(const QString &sPropertyKey, double dMinValue, const QString &sMessage /* = QString() */, const QString &sGroup /* = QString() */) + { + IxValidator_ptr pValidator = createValidator(IxValidator::min_decimal, sPropertyKey, sMessage, sGroup); + pValidator->setConstraint(dMinValue); + insertIntoGroup(pValidator, sGroup); + return pValidator.get(); + } + + IxValidator *IxValidatorX::add_MaxDecimal(const QString &sPropertyKey, double dMaxValue, const QString &sMessage /* = QString() */, const QString &sGroup /* = QString() */) + { + IxValidator_ptr pValidator = createValidator(IxValidator::max_decimal, sPropertyKey, sMessage, sGroup); + pValidator->setConstraint(dMaxValue); + insertIntoGroup(pValidator, sGroup); + return pValidator.get(); + } + + IxValidator *IxValidatorX::add_RangeDecimal(const QString &sPropertyKey, double dMinValue, double dMaxValue, const QString &sMessage /* = QString() */, const QString &sGroup /* = QString() */) + { + IxValidator_ptr pValidator = createValidator(IxValidator::min_decimal, sPropertyKey, sMessage, sGroup); + pValidator->setConstraint(dMinValue); + insertIntoGroup(pValidator, sGroup); + + pValidator = createValidator(IxValidator::max_decimal, sPropertyKey, sMessage, sGroup); + pValidator->setConstraint(dMaxValue); + insertIntoGroup(pValidator, sGroup); + return pValidator.get(); + } + + IxValidator *IxValidatorX::add_MinLength(const QString &sPropertyKey, long lMinLength, const QString &sMessage /* = QString() */, const QString &sGroup /* = QString() */) + { + IxValidator_ptr pValidator = createValidator(IxValidator::min_length, sPropertyKey, sMessage, sGroup); + pValidator->setConstraint((qlonglong)lMinLength); + insertIntoGroup(pValidator, sGroup); + return pValidator.get(); + } + + IxValidator *IxValidatorX::add_MaxLength(const QString &sPropertyKey, long lMaxLength, const QString &sMessage /* = QString() */, const QString &sGroup /* = QString() */) + { + IxValidator_ptr pValidator = createValidator(IxValidator::max_length, sPropertyKey, sMessage, sGroup); + pValidator->setConstraint((qlonglong)lMaxLength); + insertIntoGroup(pValidator, sGroup); + return pValidator.get(); + } + + IxValidator *IxValidatorX::add_Size(const QString &sPropertyKey, long lMinLength, long lMaxLength, const QString &sMessage /* = QString() */, const QString &sGroup /* = QString() */) + { + IxValidator_ptr pValidator = createValidator(IxValidator::min_length, sPropertyKey, sMessage, sGroup); + pValidator->setConstraint((qlonglong)lMinLength); + insertIntoGroup(pValidator, sGroup); + + pValidator = createValidator(IxValidator::max_length, sPropertyKey, sMessage, sGroup); + pValidator->setConstraint((qlonglong)lMaxLength); + insertIntoGroup(pValidator, sGroup); + return pValidator.get(); + } + + IxValidator *IxValidatorX::add_DatePast(const QString &sPropertyKey, const QString &sMessage /* = QString() */, const QString &sGroup /* = QString() */) + { + IxValidator_ptr pValidator = createValidator(IxValidator::date_past, sPropertyKey, sMessage, sGroup); + insertIntoGroup(pValidator, sGroup); + return pValidator.get(); + } + + IxValidator *IxValidatorX::add_DateFuture(const QString &sPropertyKey, const QString &sMessage /* = QString() */, const QString &sGroup /* = QString() */) + { + IxValidator_ptr pValidator = createValidator(IxValidator::date_future, sPropertyKey, sMessage, sGroup); + insertIntoGroup(pValidator, sGroup); + return pValidator.get(); + } + + IxValidator *IxValidatorX::add_RegExp(const QString &sPropertyKey, const QString &sPattern, const QString &sMessage /* = QString() */, const QString &sGroup /* = QString() */) + { + IxValidator_ptr pValidator = createValidator(IxValidator::regular_expression, sPropertyKey, sMessage, sGroup); + pValidator->setConstraint(sPattern); + insertIntoGroup(pValidator, sGroup); + return pValidator.get(); + } + + IxValidator *IxValidatorX::add_EMail(const QString &sPropertyKey, const QString &sMessage /* = QString() */, const QString &sGroup /* = QString() */) + { + IxValidator_ptr pValidator = createValidator(IxValidator::e_mail, sPropertyKey, sMessage, sGroup); + insertIntoGroup(pValidator, sGroup); + return pValidator.get(); + } + +} // namespace qx diff --git a/src/QxValidator/QxInvalidValue.cpp b/src/QxValidator/QxInvalidValue.cpp new file mode 100644 index 0000000..366c567 --- /dev/null +++ b/src/QxValidator/QxInvalidValue.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include +#include +#include + +#include + +#include +#include + +#include + +namespace qx +{ + + QxInvalidValue::QxInvalidValue() : QxPropertyBag(), m_pValidator(NULL) { ; } + + QxInvalidValue::~QxInvalidValue() { ; } + + const IxValidator *QxInvalidValue::getValidator() const { return m_pValidator; } + + void QxInvalidValue::setValidator(const IxValidator *p) { m_pValidator = p; } + + QString QxInvalidValue::getFullName() const + { + QString sResult = m_sPath; + sResult += ((!m_sPath.isEmpty() && !m_sPropertyName.isEmpty()) ? QString(".") : QString("")); + sResult += m_sPropertyName; + return sResult; + } + +} // namespace qx + +QDataStream &operator<<(QDataStream &stream, const qx::QxInvalidValue &t) +{ + stream << t.m_sMessage; + stream << t.m_sPropertyName; + stream << t.m_sPath; + stream << t.m_lstPropertyBag; + return stream; +} + +QDataStream &operator>>(QDataStream &stream, qx::QxInvalidValue &t) +{ + stream >> t.m_sMessage; + stream >> t.m_sPropertyName; + stream >> t.m_sPath; + stream >> t.m_lstPropertyBag; + return stream; +} diff --git a/src/QxValidator/QxInvalidValueX.cpp b/src/QxValidator/QxInvalidValueX.cpp new file mode 100644 index 0000000..28cee5e --- /dev/null +++ b/src/QxValidator/QxInvalidValueX.cpp @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include +#include + +#include + +#include + +namespace qx +{ + + QxInvalidValueX::QxInvalidValueX() { ; } + + QxInvalidValueX::~QxInvalidValueX() { ; } + + QString QxInvalidValueX::getCurrentPath() const { return m_sCurrentPath; } + + void QxInvalidValueX::setCurrentPath(const QString &s) { m_sCurrentPath = s; } + + long QxInvalidValueX::count() const { return m_lstInvalidValues.count(); } + + void QxInvalidValueX::dump() const + { + QString s = text(); + qDebug("[QxOrm] %s", qPrintable(s)); + } + + QxInvalidValue QxInvalidValueX::at(long l) const { return (((l < 0) || (l >= m_lstInvalidValues.count())) ? QxInvalidValue() : m_lstInvalidValues.at(l)); } + + QString QxInvalidValueX::text() const + { + if (m_lstInvalidValues.count() <= 0) + { + return "There is no invalid value"; + } + QString sResult = "List of invalid values (" + QString::number(m_lstInvalidValues.count()) + ") :"; + + for (long l = 0; l < m_lstInvalidValues.count(); l++) + { + QxInvalidValue invalidValue = m_lstInvalidValues.at(l); + sResult += "\n\t- " + invalidValue.getMessage(); + sResult += " (" + invalidValue.getFullName() + ")"; + } + + return sResult; + } + + void QxInvalidValueX::insert(const IxValidator *pValidator) + { + if (!pValidator) + { + return; + } + IxDataMember *pDataMember = pValidator->getDataMember(); + QString sMessage = pValidator->getMessage(); + sMessage.replace("%NAME%", (pDataMember ? pDataMember->getName() : QString())); + sMessage.replace("%CONSTRAINT%", pValidator->getConstraint().toString()); + QVariantList lstConstraints = pValidator->getConstraints(); + for (long l = 0; l < lstConstraints.count(); l++) + { + sMessage.replace("%CONSTRAINT_" + QString::number(l) + "%", lstConstraints.at(l).toString()); + } + if (sMessage.isEmpty()) + { + qAssert(false); + return; + } + + QxInvalidValue invalidValue; + invalidValue.setValidator(pValidator); + invalidValue.setMessage(sMessage); + invalidValue.setPropertyName(pDataMember ? pDataMember->getName() : QString()); + invalidValue.setPath(m_sCurrentPath); + m_lstInvalidValues.append(invalidValue); + } + + void QxInvalidValueX::insert(const QString &sMessage) + { + if (sMessage.isEmpty()) + { + qAssert(false); + return; + } + QxInvalidValue invalidValue; + invalidValue.setMessage(sMessage); + invalidValue.setPath(m_sCurrentPath); + m_lstInvalidValues.append(invalidValue); + } + + void QxInvalidValueX::insert(const QxInvalidValue &invalidValue) + { + if (invalidValue.getMessage().isEmpty()) + { + qAssert(false); + return; + } + QxInvalidValue clone = invalidValue; + clone.setPath(m_sCurrentPath); + m_lstInvalidValues.append(clone); + } + + void QxInvalidValueX::insert(const QxInvalidValueX &other) + { + for (long l = 0; l < other.count(); l++) + { + QxInvalidValue invalidValue = other.at(l); + if (invalidValue.getMessage().isEmpty()) + { + qAssert(false); + continue; + } + QString sOtherPath = invalidValue.getPath(); + QString sCurrPath = m_sCurrentPath; + sCurrPath += ((!m_sCurrentPath.isEmpty() && !sOtherPath.isEmpty()) ? QString(".") : QString("")); + sCurrPath += sOtherPath; + invalidValue.setPath(sCurrPath); + m_lstInvalidValues.append(invalidValue); + } + } + +} // namespace qx + +QDataStream &operator<<(QDataStream &stream, const qx::QxInvalidValueX &t) +{ + stream << t.m_lstInvalidValues; + stream << t.m_sCurrentPath; + return stream; +} + +QDataStream &operator>>(QDataStream &stream, qx::QxInvalidValueX &t) +{ + stream >> t.m_lstInvalidValues; + stream >> t.m_sCurrentPath; + return stream; +} diff --git a/src/QxXml/QxXmlReader.cpp b/src/QxXml/QxXmlReader.cpp new file mode 100644 index 0000000..0e2021a --- /dev/null +++ b/src/QxXml/QxXmlReader.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +namespace qx +{ + + void QxXmlReader::addBinaryData(const QxXmlReader::type_hash_bin_data &other, bool bClear) + { + if (bClear) + { + m_mapBinaryData.clear(); + } + QxXmlReader::type_hash_bin_data_itr itr(other); + + while (itr.hasNext()) + { + itr.next(); + addBinaryData(itr.key(), itr.value()); + } + } + + void QxXmlReader::addBinaryData(const QString &sKey, QxXmlReader::type_byte_arr_ptr pData) + { + if (sKey.isEmpty()) + { + return; + } + if (m_mapBinaryData.contains(sKey)) + { + qAssert(false); + return; + } + + m_mapBinaryData.insert(sKey, pData); + } + + void QxXmlReader::removeBinaryData(const QString &sKey) + { + m_mapBinaryData.remove(sKey); + } + + void QxXmlReader::removeAllBinaryData() + { + m_mapBinaryData.clear(); + } + + bool QxXmlReader::isStartBinaryData() const + { + if (!isStartElement()) + { + return false; + } + if (!attributes().hasAttribute(QX_XML_ATTRIBUTE_IS_BINARY_DATA)) + { + return false; + } + if (attributes().value(QX_XML_ATTRIBUTE_IS_BINARY_DATA) != "1") + { + return false; + } + + return true; + } + + QxXmlReader::type_byte_arr_ptr QxXmlReader::readBinaryData() + { + if (!isStartBinaryData()) + { + return QxXmlReader::type_byte_arr_ptr(); + } + + QString sKey = readElementText(); + if (sKey.isEmpty() || !m_mapBinaryData.contains(sKey)) + { + return QxXmlReader::type_byte_arr_ptr(); + } + + return m_mapBinaryData.value(sKey); + } + +} // namespace qx + +QDataStream &operator>>(QDataStream &stream, qx::QxXmlReader &xmlReader) +{ + QString sXml; + stream >> sXml; + qint32 lBinDataCount; + stream >> lBinDataCount; + + xmlReader.addData(sXml); + + for (qint32 l = 0; l < lBinDataCount; l++) + { + QString sKey; + stream >> sKey; + QByteArray pBuff; + stream >> pBuff; + + qx::QxXmlReader::type_byte_arr_ptr pBinData; + pBinData.reset(new QByteArray(pBuff)); + xmlReader.addBinaryData(sKey, pBinData); + } + + return stream; +} diff --git a/src/QxXml/QxXmlWriter.cpp b/src/QxXml/QxXmlWriter.cpp new file mode 100644 index 0000000..9f7157b --- /dev/null +++ b/src/QxXml/QxXmlWriter.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#include + +#include + +namespace qx +{ + + QString QxXmlWriter::writeBinaryData(const QString &namespaceUri, const QString &name, QxXmlWriter::type_byte_arr_ptr pData) + { + QString sKey(getNextKeyBinaryData()); + m_mapBinaryData.insert(sKey, pData); + + writeStartElement(namespaceUri, name); + writeAttribute(QX_XML_ATTRIBUTE_IS_BINARY_DATA, "1"); + writeCharacters(sKey); + writeEndElement(); + + return sKey; + } + + QString QxXmlWriter::writeBinaryData(const QString &qualifiedName, QxXmlWriter::type_byte_arr_ptr pData) + { + QString sKey(getNextKeyBinaryData()); + m_mapBinaryData.insert(sKey, pData); + + writeStartElement(qualifiedName); + writeAttribute(QX_XML_ATTRIBUTE_IS_BINARY_DATA, "1"); + writeCharacters(sKey); + writeEndElement(); + + return sKey; + } + +} // namespace qx + +QDataStream &operator<<(QDataStream &stream, const qx::QxXmlWriter &xmlWriter) +{ + stream << xmlWriter.getXml(); + stream << (qint32)(xmlWriter.getBinaryDataCount()); + + qx::QxXmlWriter::type_hash_bin_data_itr itr = xmlWriter.getBinaryDataItr(); + + while (itr.hasNext()) + { + itr.next(); + stream << itr.key(); + stream << (*itr.value()); + } + + return stream; +} diff --git a/src/all.cpp b/src/all.cpp new file mode 100644 index 0000000..609555a --- /dev/null +++ b/src/all.cpp @@ -0,0 +1,209 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +/*! + * \file all.cpp + * \author XDL Team + * \brief all.cpp file can be used to perform a Unity Build to reduce compilation times (http://stackoverflow.com/questions/543697/include-all-cpp-files-into-a-single-compilation-unit) : _QX_UNITY_BUILD compilation option must be defined (see QxOrm.pri configuration file for more details) + */ + +#ifdef _QX_UNITY_BUILD + +#include + +#include "./QxMemLeak/bool_array.cpp" +#include "./QxMemLeak/debug_new.cpp" +#include "./QxMemLeak/mem_pool_base.cpp" +#include "./QxMemLeak/static_mem_pool.cpp" + +#include "./QxSingleton/IxSingleton.cpp" +#include "./QxSingleton/QxSingletonX.cpp" +#include "./QxSingleton/QxSingletonInit.cpp" + +#include "./QxFactory/IxFactory.cpp" +#include "./QxFactory/QxFactoryX.cpp" + +#include "./QxCollection/QxCollection.cpp" + +#include "./QxCommon/QxBool.cpp" +#include "./QxCommon/QxCache.cpp" +#include "./QxCommon/QxSimpleCrypt.cpp" + +#include "./QxConvert/QxConvert_Export.cpp" + +#include "./QxRegister/IxClass.cpp" +#include "./QxRegister/QxClassX.cpp" + +#include "./QxDataMember/IxDataMember.cpp" +#include "./QxDataMember/IxDataMemberX.cpp" +#include "./QxDataMember/QxDataMember_QObject.cpp" + +#include "./QxTraits/unit_test_is_smart_ptr.cpp" +#include "./QxTraits/unit_test_is_container.cpp" + +// #include "./QxXml/QxXmlReader.cpp" +// #include "./QxXml/QxXmlWriter.cpp" + +#include "./QxDao/IxSqlQueryBuilder.cpp" +#include "./QxDao/QxSqlDatabase.cpp" +#include "./QxDao/IxSqlRelation.cpp" +#include "./QxDao/QxSqlQuery.cpp" +#include "./QxDao/QxSession.cpp" +#include "./QxDao/IxDao_Helper.cpp" +#include "./QxDao/IxPersistable.cpp" +#include "./QxDao/IxPersistableCollection.cpp" +#include "./QxDao/IxPersistableList.cpp" +#include "./QxDao/QxSqlRelationLinked.cpp" +#include "./QxDao/QxDaoAsync.cpp" +#include "./QxDao/QxSqlRelationParams.cpp" +#include "./QxDao/QxSoftDelete.cpp" +#include "./QxDao/QxDateNeutral.cpp" +#include "./QxDao/QxDateTimeNeutral.cpp" +#include "./QxDao/QxTimeNeutral.cpp" + +#include "./QxDao/QxSqlElement/IxSqlElement.cpp" +#include "./QxDao/QxSqlElement/QxSqlCompare.cpp" +#include "./QxDao/QxSqlElement/QxSqlElementTemp.cpp" +#include "./QxDao/QxSqlElement/QxSqlEmbedQuery.cpp" +#include "./QxDao/QxSqlElement/QxSqlExpression.cpp" +#include "./QxDao/QxSqlElement/QxSqlFreeText.cpp" +#include "./QxDao/QxSqlElement/QxSqlIn.cpp" +#include "./QxDao/QxSqlElement/QxSqlIsBetween.cpp" +#include "./QxDao/QxSqlElement/QxSqlIsNull.cpp" +#include "./QxDao/QxSqlElement/QxSqlLimit.cpp" +#include "./QxDao/QxSqlElement/QxSqlSort.cpp" + +#include "./QxDao/QxSqlGenerator/IxSqlGenerator.cpp" +#include "./QxDao/QxSqlGenerator/QxSqlGenerator_MySQL.cpp" +#include "./QxDao/QxSqlGenerator/QxSqlGenerator_Oracle.cpp" +#include "./QxDao/QxSqlGenerator/QxSqlGenerator_PostgreSQL.cpp" +#include "./QxDao/QxSqlGenerator/QxSqlGenerator_SQLite.cpp" +#include "./QxDao/QxSqlGenerator/QxSqlGenerator_MSSQLServer.cpp" +#include "./QxDao/QxSqlGenerator/QxSqlGenerator_Standard.cpp" + +#include "./QxDao/QxRepository/IxRepository.cpp" +#include "./QxDao/QxRepository/QxRepositoryX.cpp" + +#ifdef _QX_ENABLE_MONGODB +#include "./QxDao/QxMongoDB/QxMongoDB_Helper.cpp" +#endif // _QX_ENABLE_MONGODB + +#include "./QxSerialize/QxSerializeCheckInstance.cpp" + +#include "./QxSerialize/QxBoostSerializeHelper/IxBoostSerializeRegisterHelper.cpp" +#include "./QxSerialize/QxBoostSerializeHelper/QxBoostSerializeRegisterHelperX.cpp" + +#include "./QxSerialize/boost/QxExportDllBoostArchive.cpp" + +#include "./QxSerialize/Qt/QxSerialize_QBrush.cpp" +#include "./QxSerialize/Qt/QxSerialize_QByteArray.cpp" +#include "./QxSerialize/Qt/QxSerialize_QColor.cpp" +#include "./QxSerialize/Qt/QxSerialize_QDate.cpp" +#include "./QxSerialize/Qt/QxSerialize_QDateTime.cpp" +#include "./QxSerialize/Qt/QxSerialize_QFont.cpp" +#include "./QxSerialize/Qt/QxSerialize_QImage.cpp" +#include "./QxSerialize/Qt/QxSerialize_QMatrix.cpp" +#include "./QxSerialize/Qt/QxSerialize_QObject.cpp" +#include "./QxSerialize/Qt/QxSerialize_QPicture.cpp" +#include "./QxSerialize/Qt/QxSerialize_QPixmap.cpp" +#include "./QxSerialize/Qt/QxSerialize_QPoint.cpp" +#include "./QxSerialize/Qt/QxSerialize_QRect.cpp" +#include "./QxSerialize/Qt/QxSerialize_QRegExp.cpp" +#include "./QxSerialize/Qt/QxSerialize_QRegion.cpp" +#include "./QxSerialize/Qt/QxSerialize_QSize.cpp" +#include "./QxSerialize/Qt/QxSerialize_QString.cpp" +#include "./QxSerialize/Qt/QxSerialize_QStringList.cpp" +#include "./QxSerialize/Qt/QxSerialize_QTime.cpp" +#include "./QxSerialize/Qt/QxSerialize_QUrl.cpp" +#include "./QxSerialize/Qt/QxSerialize_QUuid.cpp" +#include "./QxSerialize/Qt/QxSerialize_QVariant.cpp" +#include "./QxSerialize/Qt/QxSerialize_QSqlError.cpp" + +#include "./QxSerialize/QDataStream/QxSerializeQDataStream_primitive_type.cpp" +#include "./QxSerialize/QDataStream/QxSerializeQDataStream_QObject.cpp" +#include "./QxSerialize/QDataStream/QxSerializeQDataStream_QSqlError.cpp" +#include "./QxSerialize/QDataStream/QxSerializeQDataStream_std_string.cpp" +#include "./QxSerialize/QDataStream/QxSerializeQDataStream_qx_registered_class.cpp" + +#include "./QxSerialize/QJson/QxSerializeQJson_qx_registered_class.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_IxService.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_IxSqlElement.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_QBrush.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_QColor.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_QFont.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_QImage.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_QMatrix.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_QObject.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_QPicture.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_QPixmap.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_QPoint.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_QRect.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_QRegExp.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_QRegion.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_QSize.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_QSqlError.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_QStringList.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_QxInvalidValue.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_QxInvalidValueX.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_QxSqlQuery.cpp" +#include "./QxSerialize/QJson/QxSerializeQJson_QxTransaction.cpp" + +#include "./QxRestApi/QxRestApi.cpp" + +#include "./QxService/IxParameter.cpp" +#include "./QxService/IxService.cpp" +#include "./QxService/QxConnect.cpp" +#include "./QxService/QxServer.cpp" +#include "./QxService/QxThread.cpp" +#include "./QxService/QxThreadPool.cpp" +#include "./QxService/QxTools.cpp" +#include "./QxService/QxTransaction.cpp" + +#include "./QxHttpServer/QxHttpRequest.cpp" +#include "./QxHttpServer/QxHttpResponse.cpp" +#include "./QxHttpServer/QxHttpServer.cpp" +#include "./QxHttpServer/QxHttpTransaction.cpp" +#include "./QxHttpServer/QxHttpCookie.cpp" +#include "./QxHttpServer/QxHttpSession.cpp" +#include "./QxHttpServer/QxHttpSessionManager.cpp" + +#include "./QxValidator/IxValidator.cpp" +#include "./QxValidator/IxValidatorX.cpp" +#include "./QxValidator/QxInvalidValue.cpp" +#include "./QxValidator/QxInvalidValueX.cpp" + +#include "./QxModelView/IxModel.cpp" +#include "./QxModelView/QxNestedModel.cpp" +#include "./QxModelView/QxModelRowCompare.cpp" + +#include "./main.cpp" + +#endif // _QX_UNITY_BUILD diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..4b37fe5 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include + +#ifdef Q_OS_WIN +#ifndef _QX_STATIC_BUILD + +#include + +extern "C" int WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /* lpReservedt */) +{ + Q_UNUSED(hInstance); + Q_UNUSED(dwReason); + +#ifndef _QX_NO_TRACE_DLL_ATTACH_DETACH + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + ::OutputDebugStringA("QxOrm.DllMain() ---> DLL_PROCESS_ATTACH\n"); + break; + case DLL_PROCESS_DETACH: + ::OutputDebugStringA("QxOrm.DllMain() ---> DLL_PROCESS_DETACH\n"); + break; + case DLL_THREAD_ATTACH: + ::OutputDebugStringA("QxOrm.DllMain() ---> DLL_THREAD_ATTACH\n"); + break; + case DLL_THREAD_DETACH: + ::OutputDebugStringA("QxOrm.DllMain() ---> DLL_THREAD_DETACH\n"); + break; + } +#endif // _QX_NO_TRACE_DLL_ATTACH_DETACH + + return 1; +} + +#endif // _QX_STATIC_BUILD +#endif // Q_OS_WIN diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..6eb5156 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.1) + +project(QxOrmAllTests LANGUAGES CXX) + +add_subdirectory(../ ./QxOrm) + +add_subdirectory(qxBlog) +add_subdirectory(qxBlogCompositeKey) +add_subdirectory(qxBlogCpp11) +add_subdirectory(qxBlogModelView) +add_subdirectory(qxDllSample) +add_subdirectory(qxClientServer) +add_subdirectory(qxBlogMongoDB) +add_subdirectory(qxBlogPImpl) +add_subdirectory(qxBlogRestApi) diff --git a/test/_bin/.gitignore b/test/_bin/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/_bin/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlog/CMakeLists.txt b/test/qxBlog/CMakeLists.txt new file mode 100644 index 0000000..371d12e --- /dev/null +++ b/test/qxBlog/CMakeLists.txt @@ -0,0 +1,56 @@ +cmake_minimum_required(VERSION 3.1) + +project(qxBlog LANGUAGES CXX) + +include(../../QxOrm.cmake) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_DEBUG_POSTFIX "d") + +set(HEADERS + ./include/precompiled.h + ./include/export.h + ./include/author.h + ./include/blog.h + ./include/category.h + ./include/comment.h + ) + +set(SRCS + ./src/author.cpp + ./src/blog.cpp + ./src/category.cpp + ./src/comment.cpp + ./src/main.cpp + ) + +add_executable(qxBlog ${SRCS} ${HEADERS}) + +target_compile_definitions(qxBlog PRIVATE -D_BUILDING_QX_BLOG) + +if(COMMAND target_precompile_headers) + target_precompile_headers(qxBlog PRIVATE ./include/precompiled.h) +endif() # (COMMAND target_precompile_headers) + +target_link_libraries(qxBlog ${QX_LIBRARIES} QxOrm) + +set_target_properties(qxBlog PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ) + +set_target_properties(qxBlog PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) diff --git a/test/qxBlog/debug/.gitignore b/test/qxBlog/debug/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlog/debug/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlog/include/author.h b/test/qxBlog/include/author.h new file mode 100644 index 0000000..1bbdbcb --- /dev/null +++ b/test/qxBlog/include/author.h @@ -0,0 +1,33 @@ +#ifndef _QX_BLOG_AUTHOR_H_ +#define _QX_BLOG_AUTHOR_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT author +{ +public: +// -- typedef + typedef std::shared_ptr blog_ptr; + typedef std::vector list_blog; +// -- enum + enum enum_sex { male, female, unknown }; +// -- properties + QString m_id; + QString m_name; + QDate m_birthdate; + enum_sex m_sex; + list_blog m_blogX; +// -- contructor, virtual destructor + author() : m_id("0"), m_sex(unknown) { ; } + virtual ~author() { ; } +// -- methods + int age() const; +}; + +QX_REGISTER_PRIMARY_KEY(author, QString) +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr author_ptr; +typedef qx::QxCollection list_author; + +#endif // _QX_BLOG_AUTHOR_H_ diff --git a/test/qxBlog/include/blog.h b/test/qxBlog/include/blog.h new file mode 100644 index 0000000..0529d99 --- /dev/null +++ b/test/qxBlog/include/blog.h @@ -0,0 +1,28 @@ +#ifndef _QX_BLOG_BLOG_H_ +#define _QX_BLOG_BLOG_H_ + +#include "author.h" +#include "comment.h" +#include "category.h" + +class QX_BLOG_DLL_EXPORT blog +{ +public: +// -- properties + long m_id; + QString m_text; + QDateTime m_dt_creation; + author_ptr m_author; + list_comment m_commentX; + list_category m_categoryX; +// -- contructor, virtual destructor + blog() : m_id(0) { ; } + virtual ~blog() { ; } +}; + +QX_REGISTER_HPP_QX_BLOG(blog, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr blog_ptr; +typedef std::vector list_blog; + +#endif // _QX_BLOG_BLOG_H_ diff --git a/test/qxBlog/include/category.h b/test/qxBlog/include/category.h new file mode 100644 index 0000000..2840089 --- /dev/null +++ b/test/qxBlog/include/category.h @@ -0,0 +1,27 @@ +#ifndef _QX_BLOG_CATEGORY_H_ +#define _QX_BLOG_CATEGORY_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT category +{ +public: +// -- typedef + typedef std::shared_ptr blog_ptr; + typedef qx::QxCollection list_blog; +// -- properties + long m_id; + QString m_name; + QString m_desc; + list_blog m_blogX; +// -- contructor, virtual destructor + category() : m_id(0) { ; } + virtual ~category() { ; } +}; + +QX_REGISTER_HPP_QX_BLOG(category, qx::trait::no_base_class_defined, 0) + +typedef QSharedPointer category_ptr; +typedef qx::QxCollection list_category; + +#endif // _QX_BLOG_CATEGORY_H_ diff --git a/test/qxBlog/include/comment.h b/test/qxBlog/include/comment.h new file mode 100644 index 0000000..a2bdb6f --- /dev/null +++ b/test/qxBlog/include/comment.h @@ -0,0 +1,26 @@ +#ifndef _QX_BLOG_COMMENT_H_ +#define _QX_BLOG_COMMENT_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT comment +{ +public: +// -- typedef + typedef std::shared_ptr blog_ptr; +// -- properties + long m_id; + QString m_text; + QDateTime m_dt_create; + blog_ptr m_blog; +// -- contructor, virtual destructor + comment() : m_id(0) { ; } + virtual ~comment() { ; } +}; + +QX_REGISTER_HPP_QX_BLOG(comment, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr comment_ptr; +typedef QList list_comment; + +#endif // _QX_BLOG_COMMENT_H_ diff --git a/test/qxBlog/include/export.h b/test/qxBlog/include/export.h new file mode 100644 index 0000000..b1c11df --- /dev/null +++ b/test/qxBlog/include/export.h @@ -0,0 +1,18 @@ +#ifndef _QX_BLOG_EXPORT_H_ +#define _QX_BLOG_EXPORT_H_ + +#ifdef _BUILDING_QX_BLOG +#define QX_BLOG_DLL_EXPORT QX_DLL_EXPORT_HELPER +#else // _BUILDING_QX_BLOG +#define QX_BLOG_DLL_EXPORT QX_DLL_IMPORT_HELPER +#endif // _BUILDING_QX_BLOG + +#ifdef _BUILDING_QX_BLOG +#define QX_REGISTER_HPP_QX_BLOG QX_REGISTER_HPP_EXPORT_DLL +#define QX_REGISTER_CPP_QX_BLOG QX_REGISTER_CPP_EXPORT_DLL +#else // _BUILDING_QX_BLOG +#define QX_REGISTER_HPP_QX_BLOG QX_REGISTER_HPP_IMPORT_DLL +#define QX_REGISTER_CPP_QX_BLOG QX_REGISTER_CPP_IMPORT_DLL +#endif // _BUILDING_QX_BLOG + +#endif // _QX_BLOG_EXPORT_H_ diff --git a/test/qxBlog/include/precompiled.h b/test/qxBlog/include/precompiled.h new file mode 100644 index 0000000..0d8b994 --- /dev/null +++ b/test/qxBlog/include/precompiled.h @@ -0,0 +1,8 @@ +#ifndef _QX_BLOG_PRECOMPILED_HEADER_H_ +#define _QX_BLOG_PRECOMPILED_HEADER_H_ + +#include + +#include "export.h" + +#endif // _QX_BLOG_PRECOMPILED_HEADER_H_ diff --git a/test/qxBlog/qt/moc/.gitignore b/test/qxBlog/qt/moc/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlog/qt/moc/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlog/qxBlog.pro b/test/qxBlog/qxBlog.pro new file mode 100644 index 0000000..326ad74 --- /dev/null +++ b/test/qxBlog/qxBlog.pro @@ -0,0 +1,34 @@ +include(../../QxOrm.pri) + +TEMPLATE = app +DEFINES += _BUILDING_QX_BLOG +INCLUDEPATH += ../../../QxOrm/include/ +DESTDIR = ../../../QxOrm/test/_bin/ +LIBS += -L"../../../QxOrm/lib" + +!contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) { +PRECOMPILED_HEADER = ./include/precompiled.h +} # !contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) + +macx:CONFIG-=app_bundle + +CONFIG(debug, debug|release) { +TARGET = qxBlogd +LIBS += -l"QxOrmd" +} else { +TARGET = qxBlog +LIBS += -l"QxOrm" +} # CONFIG(debug, debug|release) + +HEADERS += ./include/precompiled.h +HEADERS += ./include/export.h +HEADERS += ./include/author.h +HEADERS += ./include/blog.h +HEADERS += ./include/category.h +HEADERS += ./include/comment.h + +SOURCES += ./src/author.cpp +SOURCES += ./src/blog.cpp +SOURCES += ./src/category.cpp +SOURCES += ./src/comment.cpp +SOURCES += ./src/main.cpp diff --git a/test/qxBlog/release/.gitignore b/test/qxBlog/release/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlog/release/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlog/src/author.cpp b/test/qxBlog/src/author.cpp new file mode 100644 index 0000000..7fafb3b --- /dev/null +++ b/test/qxBlog/src/author.cpp @@ -0,0 +1,28 @@ +#include "../include/precompiled.h" + +#include "../include/author.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(author) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& author::m_id, "author_id"); + + t.data(& author::m_name, "name"); + t.data(& author::m_birthdate, "birthdate"); + t.data(& author::m_sex, "sex"); + + t.relationOneToMany(& author::m_blogX, "list_blog", "author_id"); + + t.fct_0(std::mem_fn(& author::age), "age"); // using std::mem_fn() here is just a workaround for an issue with some versions of MSVC, it is not required with a full compliant C++11 compiler (http://stackoverflow.com/questions/23778883/vs2013-stdfunction-with-member-function) +}} + +int author::age() const +{ + if (! m_birthdate.isValid()) { return -1; } + return (QDate::currentDate().year() - m_birthdate.year()); +} diff --git a/test/qxBlog/src/blog.cpp b/test/qxBlog/src/blog.cpp new file mode 100644 index 0000000..dd0aa68 --- /dev/null +++ b/test/qxBlog/src/blog.cpp @@ -0,0 +1,20 @@ +#include "../include/precompiled.h" + +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(blog) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& blog::m_id, "blog_id"); + + t.data(& blog::m_text, "blog_text"); + t.data(& blog::m_dt_creation, "date_creation"); + + t.relationManyToOne(& blog::m_author, "author_id"); + t.relationOneToMany(& blog::m_commentX, "list_comment", "blog_id"); + t.relationManyToMany(& blog::m_categoryX, "list_category", "category_blog", "blog_id", "category_id"); +}} diff --git a/test/qxBlog/src/category.cpp b/test/qxBlog/src/category.cpp new file mode 100644 index 0000000..723fda5 --- /dev/null +++ b/test/qxBlog/src/category.cpp @@ -0,0 +1,19 @@ +#include "../include/precompiled.h" + +#include "../include/category.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(category) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& category::m_id, "category_id"); + + t.data(& category::m_name, "name"); + t.data(& category::m_desc, "description"); + + t.relationManyToMany(& category::m_blogX, "list_blog", "category_blog", "category_id", "blog_id"); +}} diff --git a/test/qxBlog/src/comment.cpp b/test/qxBlog/src/comment.cpp new file mode 100644 index 0000000..d2d5cd5 --- /dev/null +++ b/test/qxBlog/src/comment.cpp @@ -0,0 +1,19 @@ +#include "../include/precompiled.h" + +#include "../include/comment.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(comment) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& comment::m_id, "comment_id"); + + t.data(& comment::m_text, "comment_text"); + t.data(& comment::m_dt_create, "date_creation"); + + t.relationManyToOne(& comment::m_blog, "blog_id"); +}} diff --git a/test/qxBlog/src/main.cpp b/test/qxBlog/src/main.cpp new file mode 100644 index 0000000..895de56 --- /dev/null +++ b/test/qxBlog/src/main.cpp @@ -0,0 +1,425 @@ +#include "../include/precompiled.h" + +#include + +#include "../include/blog.h" +#include "../include/author.h" +#include "../include/comment.h" +#include "../include/category.h" + +#include + +int main(int argc, char * argv[]) +{ + // Qt application + QCoreApplication app(argc, argv); + QFile::remove("./qxBlog.sqlite"); + + // Parameters to connect to database + qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE"); + qx::QxSqlDatabase::getSingleton()->setDatabaseName("./qxBlog.sqlite"); + qx::QxSqlDatabase::getSingleton()->setHostName("localhost"); + qx::QxSqlDatabase::getSingleton()->setUserName("root"); + qx::QxSqlDatabase::getSingleton()->setPassword(""); + qx::QxSqlDatabase::getSingleton()->setFormatSqlQueryBeforeLogging(true); + qx::QxSqlDatabase::getSingleton()->setDisplayTimerDetails(true); + + // Only for debug purpose : assert if invalid offset detected fetching a relation + qx::QxSqlDatabase::getSingleton()->setVerifyOffsetRelation(true); + + // Create all tables in database + QSqlError daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + + // Create a list of 3 author + author_ptr author_1; author_1.reset(new author()); + author_ptr author_2; author_2.reset(new author()); + author_ptr author_3; author_3.reset(new author()); + + author_1->m_id = "author_id_1"; author_1->m_name = "author_1"; + author_1->m_sex = author::male; author_1->m_birthdate = QDate::currentDate(); + author_2->m_id = "author_id_2"; author_2->m_name = "author_2"; + author_2->m_sex = author::female; author_2->m_birthdate = QDate::currentDate(); + author_3->m_id = "author_id_3"; author_3->m_name = "author_3"; + author_3->m_sex = author::female; author_3->m_birthdate = QDate::currentDate(); + + list_author authorX; + authorX.insert(author_1->m_id, author_1); + authorX.insert(author_2->m_id, author_2); + authorX.insert(author_3->m_id, author_3); + + // Insert list of 3 author into database + daoError = qx::dao::insert(authorX); + qAssert(qx::dao::count() == 3); + + // Delete all authors in database and try to insert them using exec batch method + daoError = qx::dao::delete_all(); qAssert(! daoError.isValid()); + qAssert(qx::dao::count() == 0); + daoError = qx::dao::insert(authorX, NULL, true); qAssert(! daoError.isValid()); + qAssert(qx::dao::count() == 3); + + // Clone author 2 : 'author_id_2' + author_ptr author_clone = qx::clone(* author_2); + qAssert(author_clone->m_id == "author_id_2"); + qAssert(author_clone->m_sex == author::female); + + // Create a query to fetch only female author : 'author_id_2' and 'author_id_3' + qx::QxSqlQuery query("WHERE author.sex = :sex"); + query.bind(":sex", author::female); + + list_author list_of_female_author; + daoError = qx::dao::fetch_by_query(query, list_of_female_author); + qAssert(list_of_female_author.count() == 2); + + // Dump list of female author (xml serialization) + qx::dump(list_of_female_author, false); + qx::dump(list_of_female_author, true); + + // Test qx::QxSqlQuery::freeText() with/without placeholders + query = qx_query(); query.freeText("WHERE author.sex = " + QString::number(static_cast(author::female))); + daoError = qx::dao::fetch_by_query(query, list_of_female_author); + qAssert(list_of_female_author.count() == 2); + query = qx_query(); query.freeText("WHERE author.sex = :sex", QVariantList() << author::female); + daoError = qx::dao::fetch_by_query(query, list_of_female_author); + qAssert(list_of_female_author.count() == 2); + query = qx_query(); query.freeText("WHERE author.sex=:sex AND author.author_id=:author_id", QVariantList() << author::female << "author_id_2"); + daoError = qx::dao::fetch_by_query(query, list_of_female_author); + qAssert(list_of_female_author.count() == 1); + query = qx_query(); query.freeText("WHERE (author.sex = :sex) AND (author.author_id = :author_id)", QVariantList() << author::female << "author_id_2"); + daoError = qx::dao::fetch_by_query(query, list_of_female_author); + qAssert(list_of_female_author.count() == 1); + + // Create 3 categories + category_ptr category_1 = category_ptr(new category()); + category_ptr category_2 = category_ptr(new category()); + category_ptr category_3 = category_ptr(new category()); + + category_1->m_name = "category_1"; category_1->m_desc = "desc_1"; + category_2->m_name = "category_2"; category_2->m_desc = "desc_2"; + category_3->m_name = "category_3"; category_3->m_desc = "desc_3"; + + { // Create a scope to destroy temporary connexion to database + + // Open a transaction to database + QSqlDatabase db = qx::QxSqlDatabase::getDatabase(); + bool bCommit = db.transaction(); + + // Insert 3 categories into database, use 'db' parameter for the transaction + daoError = qx::dao::insert(category_1, (& db)); bCommit = (bCommit && ! daoError.isValid()); + daoError = qx::dao::insert(category_2, (& db)); bCommit = (bCommit && ! daoError.isValid()); + daoError = qx::dao::insert(category_3, (& db)); bCommit = (bCommit && ! daoError.isValid()); + + qAssert(bCommit); + qAssert(category_1->m_id != 0); + qAssert(category_2->m_id != 0); + qAssert(category_3->m_id != 0); + + // Terminate transaction => commit or rollback if there is error + if (bCommit) { db.commit(); } + else { db.rollback(); } + + } // End of scope : 'db' is destroyed + + // Create a blog with the class name (factory) + qx::any blog_any = qx::create("blog"); + blog_ptr blog_1; + try { blog_1 = qx::any_cast(blog_any); } + catch (...) { blog_1.reset(new blog()); } + blog_1->m_text = "blog_text_1"; + blog_1->m_dt_creation = QDateTime::currentDateTime(); + blog_1->m_author = author_1; + + // Insert 'blog_1' into database with 'save()' method + daoError = qx::dao::save(blog_1); + + // Modify 'blog_1' properties and save into database + blog_1->m_text = "update blog_text_1"; + blog_1->m_author = author_2; + daoError = qx::dao::save(blog_1); + + // Add 2 comments to 'blog_1' + comment_ptr comment_1; comment_1.reset(new comment()); + comment_ptr comment_2; comment_2.reset(new comment()); + + comment_1->m_text = "comment_1 text"; + comment_1->m_dt_create = QDateTime::currentDateTime(); + comment_1->m_blog = blog_1; + comment_2->m_text = "comment_2 text"; + comment_2->m_dt_create = QDateTime::currentDateTime(); + comment_2->m_blog = blog_1; + + daoError = qx::dao::insert(comment_1); + daoError = qx::dao::insert(comment_2); + qAssert(qx::dao::count() == 2); + + // Add 2 categories to 'blog_1' => must insert into extra-table 'category_blog' + blog_1->m_categoryX.insert(category_1->m_id, category_1); + blog_1->m_categoryX.insert(category_3->m_id, category_3); + daoError = qx::dao::save_with_relation("list_category", blog_1); + + // Fetch blog into a new variable with all relation : 'author', 'comment' and 'category' + blog_ptr blog_tmp; blog_tmp.reset(new blog()); + blog_tmp->m_id = blog_1->m_id; + daoError = qx::dao::fetch_by_id_with_all_relation(blog_tmp); + + qAssert(blog_tmp->m_commentX.count() == 2); + qAssert(blog_tmp->m_categoryX.count() == 2); + qAssert(blog_tmp->m_text == "update blog_text_1"); + qAssert(blog_tmp->m_author && blog_tmp->m_author->m_id == "author_id_2"); + + // Fetch blog into a new variable with many relations using "*->*->*->*" (4 levels of relationships) + blog_tmp.reset(new blog()); + blog_tmp->m_id = blog_1->m_id; + daoError = qx::dao::fetch_by_id_with_relation("*->*->*->*", blog_tmp); + + qAssert(blog_tmp->m_commentX.count() == 2); + qAssert(blog_tmp->m_categoryX.count() == 2); + qAssert(blog_tmp->m_text == "update blog_text_1"); + qAssert(blog_tmp->m_author && blog_tmp->m_author->m_id == "author_id_2"); + + // Dump 'blog_tmp' result from database (xml serialization) + qx::dump(blog_tmp, false); + qx::dump(blog_tmp, true); + + // Fetch relations defining columns to fetch with syntax { col_1, col_2, etc... } + list_blog lstBlogComplexRelation; + daoError = qx::dao::fetch_all_with_relation(QStringList() << "{ blog_text }" << "author_id { name, birthdate }" << "list_comment { comment_text } -> blog_id -> *", lstBlogComplexRelation); + qx::dump(lstBlogComplexRelation); + qAssert(lstBlogComplexRelation.size() > 0); + qAssert(lstBlogComplexRelation[0]->m_text != ""); // Fetched + qAssert(lstBlogComplexRelation[0]->m_dt_creation.isNull()); // Not fetched + qAssert(lstBlogComplexRelation[0]->m_author->m_sex == author::unknown); // Not fetched + qAssert(lstBlogComplexRelation[0]->m_author->m_name != ""); // Fetched + qAssert(lstBlogComplexRelation[0]->m_commentX.size() > 0); + qAssert(lstBlogComplexRelation[0]->m_commentX[0]->m_dt_create.isNull()); // Not fetched + qAssert(lstBlogComplexRelation[0]->m_commentX[0]->m_text != ""); // Fetched + qAssert(lstBlogComplexRelation[0]->m_commentX[0]->m_blog); + + // Fetch relations defining columns to remove before fetching with syntax -{ col_1, col_2, etc... } + list_blog lstBlogComplexRelation2; + daoError = qx::dao::fetch_all_with_relation(QStringList() << "-{ blog_text }" << "author_id -{ name, birthdate }" << "list_comment -{ comment_text } -> blog_id -> *", lstBlogComplexRelation2); + qx::dump(lstBlogComplexRelation2); + qAssert(lstBlogComplexRelation2.size() > 0); + qAssert(lstBlogComplexRelation2[0]->m_text == ""); // Not fetched + qAssert(! lstBlogComplexRelation2[0]->m_dt_creation.isNull()); // Fetched + qAssert(lstBlogComplexRelation2[0]->m_author->m_sex != author::unknown); // Fetched + qAssert(lstBlogComplexRelation2[0]->m_author->m_name == ""); // Not fetched + qAssert(lstBlogComplexRelation2[0]->m_commentX.size() > 0); + qAssert(! lstBlogComplexRelation2[0]->m_commentX[0]->m_dt_create.isNull()); // Fetched + qAssert(lstBlogComplexRelation2[0]->m_commentX[0]->m_text == ""); // Not fetched + qAssert(lstBlogComplexRelation2[0]->m_commentX[0]->m_blog); + +#ifndef _QX_NO_JSON + // Custom JSON serialization process + QString customJsonFull = qx::serialization::json::to_string(blog_tmp, 1); + QString customJsonFiltered = qx::serialization::json::to_string(blog_tmp, 1, "filter: { blog_text } | author_id { name, birthdate } | list_comment { comment_text } -> blog_id -> *"); + qDebug("[QxOrm] custom JSON serialization process (full) : \n%s", qPrintable(customJsonFull)); + qDebug("[QxOrm] custom JSON serialization process (filtered) : \n%s", qPrintable(customJsonFiltered)); + + blog_ptr blogFromJsonFull; blogFromJsonFull.reset(new blog()); + blog_ptr blogFromJsonFiltered; blogFromJsonFiltered.reset(new blog()); + qx::serialization::json::from_string(blogFromJsonFull, customJsonFull, 1); + qx::serialization::json::from_string(blogFromJsonFiltered, customJsonFull, 1, "filter: { blog_text } | author_id { name, birthdate } | list_comment { comment_text } -> blog_id -> *"); + + qx::dump(blogFromJsonFull); + qAssert(blogFromJsonFull->m_commentX.count() == 2); + qAssert(blogFromJsonFull->m_categoryX.count() == 2); + qAssert(blogFromJsonFull->m_text == "update blog_text_1"); + qAssert(blogFromJsonFull->m_author && blogFromJsonFull->m_author->m_id == "author_id_2"); + + qx::dump(blogFromJsonFiltered); + qAssert(blogFromJsonFiltered->m_text != ""); // Fetched + qAssert(blogFromJsonFiltered->m_dt_creation.isNull()); // Not fetched + qAssert(blogFromJsonFiltered->m_author->m_sex == author::unknown); // Not fetched + qAssert(blogFromJsonFiltered->m_author->m_name != ""); // Fetched + qAssert(blogFromJsonFiltered->m_commentX.size() > 0); + qAssert(blogFromJsonFiltered->m_commentX[0]->m_dt_create.isNull()); // Not fetched + qAssert(blogFromJsonFiltered->m_commentX[0]->m_text != ""); // Fetched + qAssert(blogFromJsonFiltered->m_commentX[0]->m_blog); +#endif // _QX_NO_JSON + + // Fetch relations defining columns to fetch with syntax { col_1, col_2, etc... } + custom table alias using syntax + custom table alias suffix using syntax <..._my_alias_suffix> + list_blog lstBlogComplexRelation3; + daoError = qx::dao::fetch_all_with_relation(QStringList() << " { blog_text }" << "author_id { name, birthdate }" << "list_comment { comment_text } -> blog_id -> * <..._my_alias_suffix>", lstBlogComplexRelation3); + qx::dump(lstBlogComplexRelation3); + qAssert(lstBlogComplexRelation3.size() > 0); + qAssert(lstBlogComplexRelation3[0]->m_text != ""); // Fetched + qAssert(lstBlogComplexRelation3[0]->m_dt_creation.isNull()); // Not fetched + qAssert(lstBlogComplexRelation3[0]->m_author->m_sex == author::unknown); // Not fetched + qAssert(lstBlogComplexRelation3[0]->m_author->m_name != ""); // Fetched + qAssert(lstBlogComplexRelation3[0]->m_commentX.size() > 0); + qAssert(lstBlogComplexRelation3[0]->m_commentX[0]->m_dt_create.isNull()); // Not fetched + qAssert(lstBlogComplexRelation3[0]->m_commentX[0]->m_text != ""); // Fetched + qAssert(lstBlogComplexRelation3[0]->m_commentX[0]->m_blog); + + // Test to add join SQL sub-queries (inside LEFT OUTER JOIN or INNER JOIN) + list_blog lstBlogWithJoinQueries; + query = qx_query().where("blog_alias.blog_text").isEqualTo("update blog_text_1"); + query.addJoinQuery("list_comment_alias", "AND list_comment_alias.comment_text IS NOT NULL"); + query.addJoinQuery("author_alias", qx_query().freeText("AND author_alias.sex = :sex", QVariantList() << author::female)); + daoError = qx::dao::fetch_by_query_with_relation(QStringList() << " { blog_text }" << "author_id { name, birthdate, sex }" << "list_comment { comment_text }", query, lstBlogWithJoinQueries); + qx::dump(lstBlogWithJoinQueries); + qAssert(lstBlogWithJoinQueries.size() > 0); + qAssert(lstBlogWithJoinQueries[0]->m_text == "update blog_text_1"); + qAssert(lstBlogWithJoinQueries[0]->m_author->m_sex == author::female); + + // When join SQL sub-queries are used, then relationships should keep user defined order (in this example : 'list_comment' before 'author') + lstBlogWithJoinQueries.clear(); + query = qx_query().where("blog_alias.blog_text").isEqualTo("update blog_text_1"); + query.addJoinQuery("list_comment_alias", "AND list_comment_alias.comment_text IS NOT NULL"); + query.addJoinQuery("author_alias", qx_query("AND author_alias.sex = :sex", QVariantList() << author::female)); + daoError = qx::dao::fetch_by_query_with_relation(QStringList() << " { blog_text }" << "list_comment { comment_text }" << "author_id { name, birthdate, sex }", query, lstBlogWithJoinQueries); + qx::dump(lstBlogWithJoinQueries); + qAssert(lstBlogWithJoinQueries.size() > 0); + qAssert(lstBlogWithJoinQueries[0]->m_text == "update blog_text_1"); + qAssert(lstBlogWithJoinQueries[0]->m_author->m_sex == author::female); + + // Check qx::dao::save_with_relation_recursive() function + daoError = qx::dao::save_with_relation_recursive(blog_tmp); + qAssert(! daoError.isValid()); + daoError = qx::dao::save_with_relation_recursive(blog_tmp, qx::dao::save_mode::e_update_only); + qAssert(! daoError.isValid()); + + // Call 'age()' method with class name and method name (reflexion) + qx_bool bInvokeOk = qx::QxClassX::invoke("author", "age", author_1); + qAssert(bInvokeOk); + + // Check count with relations and filter + long lBlogCountWithRelation = 0; qx_query queryBlogCountWithRelation; + daoError = qx::dao::count_with_relation(lBlogCountWithRelation, QStringList() << "author_id" << "list_comment -> blog_id -> *", queryBlogCountWithRelation); + qAssert(! daoError.isValid() && (lBlogCountWithRelation > 0)); + + // Test 'isDirty()' method + qx::dao::ptr blog_isdirty = qx::dao::ptr(new blog()); + blog_isdirty->m_id = blog_1->m_id; + daoError = qx::dao::fetch_by_id(blog_isdirty); + qAssert(! daoError.isValid() && ! blog_isdirty.isDirty()); + + blog_isdirty->m_text = "blog property 'text' modified => blog is dirty !!!"; + QStringList lstDiff; bool bDirty = blog_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 1) && (lstDiff.at(0) == "blog_text")); + if (bDirty) { qDebug("[QxOrm] test dirty 1 : blog is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + // Update only property 'm_text' of 'blog_isdirty' + daoError = qx::dao::update_optimized(blog_isdirty); + qAssert(! daoError.isValid() && ! blog_isdirty.isDirty()); + qx::dump(blog_isdirty, false); + qx::dump(blog_isdirty, true); + + // Test 'isDirty()' method with a container + typedef qx::dao::ptr< QList > type_lst_author_test_is_dirty; + type_lst_author_test_is_dirty container_isdirty = type_lst_author_test_is_dirty(new QList()); + daoError = qx::dao::fetch_all(container_isdirty); + qAssert(! daoError.isValid() && ! container_isdirty.isDirty() && (container_isdirty->count() == 3)); + + author_ptr author_ptr_dirty = container_isdirty->at(1); + author_ptr_dirty->m_name = "author name modified at index 1 => container is dirty !!!"; + bDirty = container_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 1)); + if (bDirty) { qDebug("[QxOrm] test dirty 2 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + author_ptr_dirty = container_isdirty->at(2); + author_ptr_dirty->m_birthdate = QDate(1998, 03, 06); + bDirty = container_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 2)); + if (bDirty) { qDebug("[QxOrm] test dirty 3 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + // Update only property 'm_name' at position 1, only property 'm_birthdate' at position 2 and nothing at position 0 + daoError = qx::dao::update_optimized(container_isdirty); + qAssert(! daoError.isValid() && ! container_isdirty.isDirty()); + qx::dump(container_isdirty, false); + qx::dump(container_isdirty, true); + + // Fetch only property 'm_dt_creation' of blog + QStringList lstColumns = QStringList() << "date_creation"; + list_blog lst_blog_with_only_date_creation; + daoError = qx::dao::fetch_all(lst_blog_with_only_date_creation, NULL, lstColumns); + qAssert(! daoError.isValid() && (lst_blog_with_only_date_creation.size() > 0)); + if ((lst_blog_with_only_date_creation.size() > 0) && (lst_blog_with_only_date_creation[0].get() != NULL)) + { qAssert(lst_blog_with_only_date_creation[0]->m_text.isEmpty()); } + qx::dump(lst_blog_with_only_date_creation, false); + qx::dump(lst_blog_with_only_date_creation, true); + + // Dump all registered classes into QxOrm context (introspection engine) + qx::QxClassX::dumpAllClasses(); + + // Call a custom SQL query or a stored procedure + qx_query testStoredProc("SELECT * FROM author"); + daoError = qx::dao::call_query(testStoredProc); + qAssert(! daoError.isValid()); + testStoredProc.dumpSqlResult(); + QVariant valFromSqlResult = testStoredProc.getSqlResultAt(0, "birthdate"); qAssert(! valFromSqlResult.isNull()); + valFromSqlResult = testStoredProc.getSqlResultAt(0, "BIRTHDATE", false); qAssert(! valFromSqlResult.isNull()); + valFromSqlResult = testStoredProc.getSqlResultAt(0, "BIRTHDATE", true); qAssert(valFromSqlResult.isNull()); + + // Call a custom SQL query or a stored procedure and fetch automatically properties (with a collection of items) + qx_query testStoredProcBis("SELECT * FROM author"); + authorX.clear(); + daoError = qx::dao::execute_query(testStoredProcBis, authorX); + qAssert(! daoError.isValid()); qAssert(authorX.count() > 0); + qx::dump(authorX, false); + qx::dump(authorX, true); + + // Call a custom SQL query or a stored procedure and fetch automatically properties + qx_query testStoredProcThird("SELECT name, category_id FROM category"); + category_ptr category_tmp = category_ptr(new category()); + daoError = qx::dao::execute_query(testStoredProcThird, category_tmp); + qAssert(! daoError.isValid()); qAssert(category_tmp->m_id != 0); + qx::dump(category_tmp, false); + qx::dump(category_tmp, true); + + // Test SQL DISTINCT keyword + QList listOfBlogDistinct; + qx_query queryDistinct; queryDistinct.distinct().limit(10); + daoError = qx::dao::fetch_by_query(queryDistinct, listOfBlogDistinct, NULL, QStringList() << "blog_text"); + qAssert(! daoError.isValid()); + qx::dump(listOfBlogDistinct); + qAssert(listOfBlogDistinct.count() > 0); + qAssert(listOfBlogDistinct.at(0).m_id == 0); + qAssert(! listOfBlogDistinct.at(0).m_text.isEmpty()); + + // Test SQL DISTINCT keyword with relationships + listOfBlogDistinct.clear(); + qx_query queryDistinctWithRelations; queryDistinctWithRelations.distinct().limit(10); + daoError = qx::dao::fetch_by_query_with_relation(QStringList() << " { blog_text }" << "list_comment { comment_text }" << "author_id { name, birthdate }", queryDistinctWithRelations, listOfBlogDistinct); + qAssert(! daoError.isValid()); + qx::dump(listOfBlogDistinct); + qAssert(listOfBlogDistinct.count() > 0); + qAssert(listOfBlogDistinct.at(0).m_id == 0); + qAssert(! listOfBlogDistinct.at(0).m_text.isEmpty()); + qAssert(listOfBlogDistinct.at(0).m_author.get() != NULL); + qAssert(listOfBlogDistinct.at(0).m_author->m_id == "0"); // Not fetched + qAssert(! listOfBlogDistinct.at(0).m_author->m_name.isEmpty()); + qAssert(listOfBlogDistinct.at(0).m_commentX.size() > 1); + qAssert(listOfBlogDistinct.at(0).m_commentX.at(0)->m_id == 0); // Not fetched + qAssert(! listOfBlogDistinct.at(0).m_commentX.at(0)->m_text.isEmpty()); + + // Test SQL DISTINCT keyword with relationships forcing ID in root level + listOfBlogDistinct.clear(); + qx_query queryDistinctWithRelationsAndId; queryDistinctWithRelationsAndId.distinct().limit(10); + daoError = qx::dao::fetch_by_query_with_relation(QStringList() << " { blog_id, blog_text }" << "list_comment { comment_text }" << "author_id { name, birthdate }", queryDistinctWithRelationsAndId, listOfBlogDistinct); + qAssert(! daoError.isValid()); + qx::dump(listOfBlogDistinct); + qAssert(listOfBlogDistinct.count() > 0); + qAssert(listOfBlogDistinct.at(0).m_id != 0); // Force fetched even if DISTINCT keyword is used + qAssert(! listOfBlogDistinct.at(0).m_text.isEmpty()); + qAssert(listOfBlogDistinct.at(0).m_author.get() != NULL); + qAssert(listOfBlogDistinct.at(0).m_author->m_id == "0"); // Not fetched + qAssert(! listOfBlogDistinct.at(0).m_author->m_name.isEmpty()); + qAssert(listOfBlogDistinct.at(0).m_commentX.size() > 1); + qAssert(listOfBlogDistinct.at(0).m_commentX.at(0)->m_id == 0); // Not fetched + qAssert(! listOfBlogDistinct.at(0).m_commentX.at(0)->m_text.isEmpty()); + + // Test fetch relationships (with alias) only in LEFT OUTER/INNER JOIN and WHERE clauses (so no columns in SELECT part) : use {NULL} syntax to define no relation columns in SELECT part + list_blog lstBlogComplexRelation4; + daoError = qx::dao::fetch_all_with_relation(QStringList() << " { blog_text }" << "author_id { NULL }" << "list_comment { NULL }", lstBlogComplexRelation4); + qAssert(! daoError.isValid()); + qx::dump(lstBlogComplexRelation4); + qAssert((lstBlogComplexRelation4.size() > 0) && (lstBlogComplexRelation4[0].get() != NULL)); + qAssert(lstBlogComplexRelation4[0]->m_author.get() == NULL); // Not fetched + qAssert(lstBlogComplexRelation4[0]->m_commentX.size() == 0); // Not fetched + + return 0; +} diff --git a/test/qxBlogCompositeKey/CMakeLists.txt b/test/qxBlogCompositeKey/CMakeLists.txt new file mode 100644 index 0000000..604f240 --- /dev/null +++ b/test/qxBlogCompositeKey/CMakeLists.txt @@ -0,0 +1,56 @@ +cmake_minimum_required(VERSION 3.1) + +project(qxBlogCompositeKey LANGUAGES CXX) + +include(../../QxOrm.cmake) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_DEBUG_POSTFIX "d") + +set(HEADERS + ./include/precompiled.h + ./include/export.h + ./include/author.h + ./include/blog.h + ./include/category.h + ./include/comment.h + ) + +set(SRCS + ./src/author.cpp + ./src/blog.cpp + ./src/category.cpp + ./src/comment.cpp + ./src/main.cpp + ) + +add_executable(qxBlogCompositeKey ${SRCS} ${HEADERS}) + +target_compile_definitions(qxBlogCompositeKey PRIVATE -D_BUILDING_QX_BLOG) + +if(COMMAND target_precompile_headers) + target_precompile_headers(qxBlogCompositeKey PRIVATE ./include/precompiled.h) +endif() # (COMMAND target_precompile_headers) + +target_link_libraries(qxBlogCompositeKey ${QX_LIBRARIES} QxOrm) + +set_target_properties(qxBlogCompositeKey PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ) + +set_target_properties(qxBlogCompositeKey PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) diff --git a/test/qxBlogCompositeKey/debug/.gitignore b/test/qxBlogCompositeKey/debug/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogCompositeKey/debug/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogCompositeKey/include/author.h b/test/qxBlogCompositeKey/include/author.h new file mode 100644 index 0000000..0ac8879 --- /dev/null +++ b/test/qxBlogCompositeKey/include/author.h @@ -0,0 +1,57 @@ +#ifndef _QX_BLOG_AUTHOR_H_ +#define _QX_BLOG_AUTHOR_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT author +{ + + QX_REGISTER_FRIEND_CLASS(author) + +public: + +// -- composite key (multi-column primary key in database) + typedef std::tuple type_composite_key; + static QString str_composite_key() { return "author_id_0|author_id_1|author_id_2"; } + +// -- typedef + typedef std::shared_ptr blog_ptr; + typedef std::vector list_blog; + +// -- enum + enum enum_sex { male, female, unknown }; + +// -- properties + type_composite_key m_id; + QString m_name; + QDate m_birthdate; + enum_sex m_sex; + list_blog m_blogX; + +// -- contructor, virtual destructor + author() : m_id("", 0, ""), m_sex(unknown) { ; } + virtual ~author() { ; } + +// -- methods + int age() const; + +// -- methods "get" to composite key + type_composite_key getId() const { return m_id; } + QString getId_0() const { return std::get<0>(m_id); } + long getId_1() const { return std::get<1>(m_id); } + QString getId_2() const { return std::get<2>(m_id); } + +// -- methods "set" to composite key + void setId_0(const QString & s) { std::get<0>(m_id) = s; } + void setId_1(long l) { std::get<1>(m_id) = l; } + void setId_2(const QString & s) { std::get<2>(m_id) = s; } + +}; + +QX_REGISTER_PRIMARY_KEY(author, author::type_composite_key) +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr author_ptr; +typedef qx::QxCollection list_author; + +#endif // _QX_BLOG_AUTHOR_H_ diff --git a/test/qxBlogCompositeKey/include/blog.h b/test/qxBlogCompositeKey/include/blog.h new file mode 100644 index 0000000..59c72d7 --- /dev/null +++ b/test/qxBlogCompositeKey/include/blog.h @@ -0,0 +1,48 @@ +#ifndef _QX_BLOG_BLOG_H_ +#define _QX_BLOG_BLOG_H_ + +#include "author.h" +#include "comment.h" +#include "category.h" + +class QX_BLOG_DLL_EXPORT blog +{ + + QX_REGISTER_FRIEND_CLASS(blog) + +public: + +// -- composite key (multi-column primary key in database) + typedef QPair type_composite_key; + static QString str_composite_key() { return "blog_id_0|blog_id_1"; } + +// -- properties + type_composite_key m_id; + QString m_text; + QDateTime m_dt_creation; + author_ptr m_author; + list_comment m_commentX; + list_category m_categoryX; + +// -- contructor, virtual destructor + blog() : m_id(0, "") { ; } + virtual ~blog() { ; } + +// -- methods "get" to composite key + type_composite_key getId() const { return m_id; } + long getId_0() const { return m_id.first; } + QString getId_1() const { return m_id.second; } + +// -- methods "set" to composite key + void setId_0(long l) { m_id.first = l; } + void setId_1(const QString & s) { m_id.second = s; } + +}; + +QX_REGISTER_PRIMARY_KEY(blog, blog::type_composite_key) +QX_REGISTER_HPP_QX_BLOG(blog, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr blog_ptr; +typedef std::vector list_blog; + +#endif // _QX_BLOG_BLOG_H_ diff --git a/test/qxBlogCompositeKey/include/category.h b/test/qxBlogCompositeKey/include/category.h new file mode 100644 index 0000000..62d6c8c --- /dev/null +++ b/test/qxBlogCompositeKey/include/category.h @@ -0,0 +1,52 @@ +#ifndef _QX_BLOG_CATEGORY_H_ +#define _QX_BLOG_CATEGORY_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT category +{ + + QX_REGISTER_FRIEND_CLASS(category) + +public: + +// -- composite key (multi-column primary key in database) + typedef std::tuple type_composite_key; + static QString str_composite_key() { return "category_id_0|category_id_1|category_id_2|category_id_3"; } + +// -- typedef + typedef std::shared_ptr blog_ptr; + typedef qx::QxCollection list_blog; + +// -- properties + type_composite_key m_id; + QString m_name; + QString m_desc; + list_blog m_blogX; + +// -- contructor, virtual destructor + category() : m_id("", 0, "", 0) { ; } + virtual ~category() { ; } + +// -- methods "get" to composite key + type_composite_key getId() const { return m_id; } + QString getId_0() const { return std::get<0>(m_id); } + long getId_1() const { return std::get<1>(m_id); } + QString getId_2() const { return std::get<2>(m_id); } + long getId_3() const { return std::get<3>(m_id); } + +// -- methods "set" to composite key + void setId_0(const QString & s) { std::get<0>(m_id) = s; } + void setId_1(long l) { std::get<1>(m_id) = l; } + void setId_2(const QString & s) { std::get<2>(m_id) = s; } + void setId_3(long l) { std::get<3>(m_id) = l; } + +}; + +QX_REGISTER_PRIMARY_KEY(category, category::type_composite_key) +QX_REGISTER_HPP_QX_BLOG(category, qx::trait::no_base_class_defined, 0) + +typedef QSharedPointer category_ptr; +typedef qx::QxCollection list_category; + +#endif // _QX_BLOG_CATEGORY_H_ diff --git a/test/qxBlogCompositeKey/include/comment.h b/test/qxBlogCompositeKey/include/comment.h new file mode 100644 index 0000000..797cdd5 --- /dev/null +++ b/test/qxBlogCompositeKey/include/comment.h @@ -0,0 +1,47 @@ +#ifndef _QX_BLOG_COMMENT_H_ +#define _QX_BLOG_COMMENT_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT comment +{ + + QX_REGISTER_FRIEND_CLASS(comment) + +public: + +// -- composite key (multi-column primary key in database) + typedef std::tuple type_composite_key; + static QString str_composite_key() { return "comment_id_0|comment_id_1"; } + +// -- typedef + typedef std::shared_ptr blog_ptr; + +// -- properties + type_composite_key m_id; + QString m_text; + QDateTime m_dt_create; + blog_ptr m_blog; + +// -- contructor, virtual destructor + comment() : m_id(0, "") { ; } + virtual ~comment() { ; } + +// -- methods "get" to composite key + type_composite_key getId() const { return m_id; } + long getId_0() const { return std::get<0>(m_id); } + QString getId_1() const { return std::get<1>(m_id); } + +// -- methods "set" to composite key + void setId_0(long l) { std::get<0>(m_id) = l; } + void setId_1(const QString & s) { std::get<1>(m_id) = s; } + +}; + +QX_REGISTER_PRIMARY_KEY(comment, comment::type_composite_key) +QX_REGISTER_HPP_QX_BLOG(comment, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr comment_ptr; +typedef QList list_comment; + +#endif // _QX_BLOG_COMMENT_H_ diff --git a/test/qxBlogCompositeKey/include/export.h b/test/qxBlogCompositeKey/include/export.h new file mode 100644 index 0000000..b1c11df --- /dev/null +++ b/test/qxBlogCompositeKey/include/export.h @@ -0,0 +1,18 @@ +#ifndef _QX_BLOG_EXPORT_H_ +#define _QX_BLOG_EXPORT_H_ + +#ifdef _BUILDING_QX_BLOG +#define QX_BLOG_DLL_EXPORT QX_DLL_EXPORT_HELPER +#else // _BUILDING_QX_BLOG +#define QX_BLOG_DLL_EXPORT QX_DLL_IMPORT_HELPER +#endif // _BUILDING_QX_BLOG + +#ifdef _BUILDING_QX_BLOG +#define QX_REGISTER_HPP_QX_BLOG QX_REGISTER_HPP_EXPORT_DLL +#define QX_REGISTER_CPP_QX_BLOG QX_REGISTER_CPP_EXPORT_DLL +#else // _BUILDING_QX_BLOG +#define QX_REGISTER_HPP_QX_BLOG QX_REGISTER_HPP_IMPORT_DLL +#define QX_REGISTER_CPP_QX_BLOG QX_REGISTER_CPP_IMPORT_DLL +#endif // _BUILDING_QX_BLOG + +#endif // _QX_BLOG_EXPORT_H_ diff --git a/test/qxBlogCompositeKey/include/precompiled.h b/test/qxBlogCompositeKey/include/precompiled.h new file mode 100644 index 0000000..0d8b994 --- /dev/null +++ b/test/qxBlogCompositeKey/include/precompiled.h @@ -0,0 +1,8 @@ +#ifndef _QX_BLOG_PRECOMPILED_HEADER_H_ +#define _QX_BLOG_PRECOMPILED_HEADER_H_ + +#include + +#include "export.h" + +#endif // _QX_BLOG_PRECOMPILED_HEADER_H_ diff --git a/test/qxBlogCompositeKey/qt/moc/.gitignore b/test/qxBlogCompositeKey/qt/moc/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogCompositeKey/qt/moc/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogCompositeKey/qxBlog.pro b/test/qxBlogCompositeKey/qxBlog.pro new file mode 100644 index 0000000..9a7727d --- /dev/null +++ b/test/qxBlogCompositeKey/qxBlog.pro @@ -0,0 +1,34 @@ +include(../../QxOrm.pri) + +TEMPLATE = app +DEFINES += _BUILDING_QX_BLOG +INCLUDEPATH += ../../../QxOrm/include/ +DESTDIR = ../../../QxOrm/test/_bin/ +LIBS += -L"../../../QxOrm/lib" + +!contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) { +PRECOMPILED_HEADER = ./include/precompiled.h +} # !contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) + +macx:CONFIG-=app_bundle + +CONFIG(debug, debug|release) { +TARGET = qxBlogCompositeKeyd +LIBS += -l"QxOrmd" +} else { +TARGET = qxBlogCompositeKey +LIBS += -l"QxOrm" +} # CONFIG(debug, debug|release) + +HEADERS += ./include/precompiled.h +HEADERS += ./include/export.h +HEADERS += ./include/author.h +HEADERS += ./include/blog.h +HEADERS += ./include/category.h +HEADERS += ./include/comment.h + +SOURCES += ./src/author.cpp +SOURCES += ./src/blog.cpp +SOURCES += ./src/category.cpp +SOURCES += ./src/comment.cpp +SOURCES += ./src/main.cpp diff --git a/test/qxBlogCompositeKey/release/.gitignore b/test/qxBlogCompositeKey/release/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogCompositeKey/release/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogCompositeKey/src/author.cpp b/test/qxBlogCompositeKey/src/author.cpp new file mode 100644 index 0000000..d08db6b --- /dev/null +++ b/test/qxBlogCompositeKey/src/author.cpp @@ -0,0 +1,28 @@ +#include "../include/precompiled.h" + +#include "../include/author.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(author) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& author::m_id, author::str_composite_key()); + + t.data(& author::m_name, "name"); + t.data(& author::m_birthdate, "birthdate"); + t.data(& author::m_sex, "sex"); + + t.relationOneToMany(& author::m_blogX, blog::str_composite_key(), author::str_composite_key()); + + t.fct_0(std::mem_fn(& author::age), "age"); // using std::mem_fn() here is just a workaround for an issue with some versions of MSVC, it is not required with a full compliant C++11 compiler (http://stackoverflow.com/questions/23778883/vs2013-stdfunction-with-member-function) +}} + +int author::age() const +{ + if (! m_birthdate.isValid()) { return -1; } + return (QDate::currentDate().year() - m_birthdate.year()); +} diff --git a/test/qxBlogCompositeKey/src/blog.cpp b/test/qxBlogCompositeKey/src/blog.cpp new file mode 100644 index 0000000..481cb8e --- /dev/null +++ b/test/qxBlogCompositeKey/src/blog.cpp @@ -0,0 +1,60 @@ +#include "../include/precompiled.h" + +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(blog) + +void logCustomSqlCallback(qx::IxDataMemberSqlCallbackParams & params, const QString & fctName) +{ + QString msg = "-- " + fctName + " : " + params.pDataMember->getNameParent() + "::" + params.pDataMember->getName() + " --"; + msg += "\nSQL query generated by QxOrm library for this data member : " + params.sSQL; + if (params.pSqlQueryBuilder) { msg += "\nCurrent building full SQL query (not finished yet) : " + params.pSqlQueryBuilder->getCurrentBuildingSql(); } + msg += "\n"; + qDebug(qPrintable(msg)); +} + +void myCustomGetSqlName(qx::IxDataMemberSqlCallbackParams & params) +{ + logCustomSqlCallback(params, "myCustomGetSqlName"); +} + +void myCustomGetSqlTablePointNameAsAlias(qx::IxDataMemberSqlCallbackParams & params) +{ + logCustomSqlCallback(params, "myCustomGetSqlTablePointNameAsAlias"); +} + +void myCustomGetSqlNameEqualToPlaceHolder(qx::IxDataMemberSqlCallbackParams & params) +{ + logCustomSqlCallback(params, "myCustomGetSqlNameEqualToPlaceHolder"); +} + +void myCustomGetSqlAliasEqualToPlaceHolder(qx::IxDataMemberSqlCallbackParams & params) +{ + logCustomSqlCallback(params, "myCustomGetSqlAliasEqualToPlaceHolder"); +} + +void myCustomGetSqlAlias(qx::IxDataMemberSqlCallbackParams & params) +{ + logCustomSqlCallback(params, "myCustomGetSqlAlias"); +} + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& blog::m_id, blog::str_composite_key()); + + t.data(& blog::m_text, "blog_text"); + qx::IxDataMember * pData = t.data(& blog::m_dt_creation, "date_creation"); + + pData->customGetSqlName(myCustomGetSqlName); + pData->customGetSqlTablePointNameAsAlias(myCustomGetSqlTablePointNameAsAlias); + pData->customGetSqlNameEqualToPlaceHolder(myCustomGetSqlNameEqualToPlaceHolder); + pData->customGetSqlAliasEqualToPlaceHolder(myCustomGetSqlAliasEqualToPlaceHolder); + pData->customGetSqlAlias(myCustomGetSqlAlias); + + t.relationManyToOne(& blog::m_author, author::str_composite_key()); + t.relationOneToMany(& blog::m_commentX, comment::str_composite_key(), blog::str_composite_key()); + t.relationManyToMany(& blog::m_categoryX, "list_category", "category_blog", blog::str_composite_key(), category::str_composite_key()); +}} diff --git a/test/qxBlogCompositeKey/src/category.cpp b/test/qxBlogCompositeKey/src/category.cpp new file mode 100644 index 0000000..cc27505 --- /dev/null +++ b/test/qxBlogCompositeKey/src/category.cpp @@ -0,0 +1,19 @@ +#include "../include/precompiled.h" + +#include "../include/category.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(category) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& category::m_id, category::str_composite_key()); + + t.data(& category::m_name, "name"); + t.data(& category::m_desc, "description"); + + t.relationManyToMany(& category::m_blogX, "list_blog", "category_blog", category::str_composite_key(), blog::str_composite_key()); +}} diff --git a/test/qxBlogCompositeKey/src/comment.cpp b/test/qxBlogCompositeKey/src/comment.cpp new file mode 100644 index 0000000..339995c --- /dev/null +++ b/test/qxBlogCompositeKey/src/comment.cpp @@ -0,0 +1,19 @@ +#include "../include/precompiled.h" + +#include "../include/comment.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(comment) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& comment::m_id, comment::str_composite_key()); + + t.data(& comment::m_text, "comment_text"); + t.data(& comment::m_dt_create, "date_creation"); + + t.relationManyToOne(& comment::m_blog, blog::str_composite_key()); +}} diff --git a/test/qxBlogCompositeKey/src/main.cpp b/test/qxBlogCompositeKey/src/main.cpp new file mode 100644 index 0000000..853b908 --- /dev/null +++ b/test/qxBlogCompositeKey/src/main.cpp @@ -0,0 +1,225 @@ +#include "../include/precompiled.h" + +#include + +#include "../include/blog.h" +#include "../include/author.h" +#include "../include/comment.h" +#include "../include/category.h" + +#include + +int main(int argc, char * argv[]) +{ + // Qt application + QCoreApplication app(argc, argv); + QFile::remove("./qxBlogCompositeKey.sqlite"); + + // Parameters to connect to database + qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE"); + qx::QxSqlDatabase::getSingleton()->setDatabaseName("./qxBlogCompositeKey.sqlite"); + qx::QxSqlDatabase::getSingleton()->setHostName("localhost"); + qx::QxSqlDatabase::getSingleton()->setUserName("root"); + qx::QxSqlDatabase::getSingleton()->setPassword(""); + qx::QxSqlDatabase::getSingleton()->setAddSqlSquareBracketsForTableName(true); + qx::QxSqlDatabase::getSingleton()->setAddSqlSquareBracketsForColumnName(true); + + // Only for debug purpose : assert if invalid offset detected fetching a relation + qx::QxSqlDatabase::getSingleton()->setVerifyOffsetRelation(true); + qx::IxSqlRelation::setTraceRelationInit(true); + qx::QxClassX::registerAllClasses(); + + // Create all tables in database + QSqlError daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + + // Create a list of 3 author + author_ptr author_1; author_1.reset(new author()); + author_ptr author_2; author_2.reset(new author()); + author_ptr author_3; author_3.reset(new author()); + + author_1->setId_0("_111_"); author_1->setId_1(100); author_1->setId_2("1_1"); + author_1->m_name = "author_1"; author_1->m_sex = author::male; author_1->m_birthdate = QDate::currentDate(); + author_2->setId_0("_222_"); author_2->setId_1(200); author_2->setId_2("2_2"); + author_2->m_name = "author_2"; author_2->m_sex = author::female; author_2->m_birthdate = QDate::currentDate(); + author_3->setId_0("_333_"); author_3->setId_1(300); author_3->setId_2("3_3"); + author_3->m_name = "author_3"; author_3->m_sex = author::female; author_3->m_birthdate = QDate::currentDate(); + + list_author authorX; + authorX.insert(author_1->getId(), author_1); + authorX.insert(author_2->getId(), author_2); + authorX.insert(author_3->getId(), author_3); + + // Insert list of 3 author into database + daoError = qx::dao::insert(authorX); + qAssert(qx::dao::count() == 3); + + // Clone author 2 : 'author_id_2' + author_ptr author_clone = qx::clone(* author_2); + qAssert(author_clone->getId() == author::type_composite_key("_222_", 200, "2_2")); + qAssert(author_clone->m_sex == author::female); + + // Create a query to fetch only female author : 'author_id_2' and 'author_id_3' + qx::QxSqlQuery query("WHERE author.sex = :sex"); + query.bind(":sex", author::female); + + list_author list_of_female_author; + daoError = qx::dao::fetch_by_query(query, list_of_female_author); + qAssert(list_of_female_author.count() == 2); + + // Dump list of female author (xml serialization) + qx::dump(list_of_female_author); + + // Create 3 categories + category_ptr category_1 = category_ptr(new category()); + category_ptr category_2 = category_ptr(new category()); + category_ptr category_3 = category_ptr(new category()); + + category_1->setId_0("101"); category_1->setId_1(10); + category_1->setId_2("__11_XX"); category_1->setId_3(10010); + category_1->m_name = "category_1"; category_1->m_desc = "desc_1"; + category_2->setId_0("202"); category_2->setId_1(20); + category_2->setId_2("__22_XX"); category_2->setId_3(20020); + category_2->m_name = "category_2"; category_2->m_desc = "desc_2"; + category_3->setId_0("303"); category_3->setId_1(30); + category_3->setId_2("__33_XX"); category_3->setId_3(30030); + category_3->m_name = "category_3"; category_3->m_desc = "desc_3"; + + { // Create a scope to destroy temporary connexion to database + + // Open a transaction to database + QSqlDatabase db = qx::QxSqlDatabase::getDatabase(); + bool bCommit = db.transaction(); + + // Insert 3 categories into database, use 'db' parameter for the transaction + daoError = qx::dao::insert(category_1, (& db)); bCommit = (bCommit && ! daoError.isValid()); + daoError = qx::dao::insert(category_2, (& db)); bCommit = (bCommit && ! daoError.isValid()); + daoError = qx::dao::insert(category_3, (& db)); bCommit = (bCommit && ! daoError.isValid()); + + qAssert(bCommit); + qAssert(category_1->getId() == category::type_composite_key("101", 10, "__11_XX", 10010)); + qAssert(category_2->getId() == category::type_composite_key("202", 20, "__22_XX", 20020)); + qAssert(category_3->getId() == category::type_composite_key("303", 30, "__33_XX", 30030)); + + // Terminate transaction => commit or rollback if there is error + if (bCommit) { db.commit(); } + else { db.rollback(); } + + } // End of scope : 'db' is destroyed + + // Create a blog with the class name (factory) + qx::any blog_any = qx::create("blog"); + blog_ptr blog_1; + try { blog_1 = qx::any_cast(blog_any); } + catch (...) { blog_1.reset(new blog()); } + blog_1->setId_0(1); + blog_1->setId_1("blog 1"); + blog_1->m_text = "blog_text_1"; + blog_1->m_dt_creation = QDateTime::currentDateTime(); + blog_1->m_author = author_1; + + // Insert 'blog_1' into database with 'save()' method + daoError = qx::dao::save(blog_1); + + // Modify 'blog_1' properties and save into database + blog_1->m_text = "update blog_text_1"; + blog_1->m_author = author_2; + daoError = qx::dao::save(blog_1); + + // Add 2 comments to 'blog_1' + comment_ptr comment_1; comment_1.reset(new comment()); + comment_ptr comment_2; comment_2.reset(new comment()); + + comment_1->setId_0(1); + comment_1->setId_1("comment 1"); + comment_1->m_text = "comment_1 text"; + comment_1->m_dt_create = QDateTime::currentDateTime(); + comment_1->m_blog = blog_1; + comment_2->setId_0(2); + comment_2->setId_1("comment 2"); + comment_2->m_text = "comment_2 text"; + comment_2->m_dt_create = QDateTime::currentDateTime(); + comment_2->m_blog = blog_1; + + daoError = qx::dao::insert(comment_1); + daoError = qx::dao::insert(comment_2); + qAssert(! daoError.isValid() && (qx::dao::count() == 2)); + + // Add 2 categories to 'blog_1' => must insert into extra-table 'category_blog' + blog_1->m_categoryX.insert(category_1->getId(), category_1); + blog_1->m_categoryX.insert(category_3->getId(), category_3); + daoError = qx::dao::save_with_relation("list_category", blog_1); + + // Fetch blog into a new variable with all relation : 'author', 'comment' and 'category' + blog_ptr blog_tmp; blog_tmp.reset(new blog()); + blog_tmp->setId_0(blog_1->getId_0()); + blog_tmp->setId_1(blog_1->getId_1()); + daoError = qx::dao::fetch_by_id_with_all_relation(blog_tmp); + + qAssert(! daoError.isValid()); + qAssert(blog_tmp->m_commentX.count() == 2); + qAssert(blog_tmp->m_categoryX.count() == 2); + qAssert(blog_tmp->m_text == "update blog_text_1"); + qAssert(blog_tmp->m_author && blog_tmp->m_author->getId() == author::type_composite_key("_222_", 200, "2_2")); + + // Dump 'blog_tmp' result from database (xml serialization) + qx::dump(blog_tmp); + + // Fetch relations defining columns to fetch with syntax { col_1, col_2, etc... } + list_blog lstBlogComplexRelation; + daoError = qx::dao::fetch_all_with_relation(QStringList() << author::str_composite_key() + " { name, birthdate }" << comment::str_composite_key() + " { comment_text } -> " + blog::str_composite_key() + " -> *", lstBlogComplexRelation); + qx::dump(lstBlogComplexRelation); + qAssert(lstBlogComplexRelation.size() > 0); + qAssert(lstBlogComplexRelation[0]->m_author->m_sex == author::unknown); // Not fetched + qAssert(lstBlogComplexRelation[0]->m_author->m_name != ""); // Fetched + qAssert(lstBlogComplexRelation[0]->m_commentX.size() > 0); + qAssert(lstBlogComplexRelation[0]->m_commentX[0]->m_dt_create.isNull()); // Not fetched + qAssert(lstBlogComplexRelation[0]->m_commentX[0]->m_text != ""); // Fetched + qAssert(lstBlogComplexRelation[0]->m_commentX[0]->m_blog); + + // Call 'age()' method with class name and method name (reflexion) + qx_bool bInvokeOk = qx::QxClassX::invoke("author", "age", author_1); + qAssert(bInvokeOk); + + // Test 'isDirty()' method + qx::dao::ptr blog_isdirty = qx::dao::ptr(new blog()); + blog_isdirty->setId_0(blog_1->getId_0()); + blog_isdirty->setId_1(blog_1->getId_1()); + daoError = qx::dao::fetch_by_id(blog_isdirty); + qAssert(! daoError.isValid() && ! blog_isdirty.isDirty()); + + blog_isdirty->m_text = "blog property 'text' modified => blog is dirty !!!"; + QStringList lstDiff; bool bDirty = blog_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 1) && (lstDiff.at(0) == "blog_text")); + if (bDirty) { qDebug("[QxOrm] test dirty 1 : blog is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + daoError = qx::dao::update(blog_isdirty); + qAssert(! daoError.isValid() && ! blog_isdirty.isDirty()); + qx::dump(blog_isdirty); + + // Test 'isDirty()' method with a container + typedef qx::dao::ptr< QList > type_lst_author_test_is_dirty; + type_lst_author_test_is_dirty container_isdirty = type_lst_author_test_is_dirty(new QList()); + daoError = qx::dao::fetch_all(container_isdirty); + qAssert(! daoError.isValid() && ! container_isdirty.isDirty() && (container_isdirty->count() == 3)); + + author_ptr author_ptr_dirty = container_isdirty->at(1); + author_ptr_dirty->m_name = "author name modified at index 1 => container is dirty !!!"; + bDirty = container_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 1)); + if (bDirty) { qDebug("[QxOrm] test dirty 2 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + author_ptr_dirty = container_isdirty->at(2); + author_ptr_dirty->m_birthdate = QDate(1998, 03, 06); + bDirty = container_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 2)); + if (bDirty) { qDebug("[QxOrm] test dirty 3 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + daoError = qx::dao::save(container_isdirty); + qAssert(! daoError.isValid() && ! container_isdirty.isDirty()); + qx::dump(container_isdirty); + + return 0; +} diff --git a/test/qxBlogCpp11/CMakeLists.txt b/test/qxBlogCpp11/CMakeLists.txt new file mode 100644 index 0000000..ecd4e98 --- /dev/null +++ b/test/qxBlogCpp11/CMakeLists.txt @@ -0,0 +1,56 @@ +cmake_minimum_required(VERSION 3.1) + +project(qxBlogCpp11 LANGUAGES CXX) + +include(../../QxOrm.cmake) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_DEBUG_POSTFIX "d") + +set(HEADERS + ./include/precompiled.h + ./include/export.h + ./include/author.h + ./include/blog.h + ./include/category.h + ./include/comment.h + ) + +set(SRCS + ./src/author.cpp + ./src/blog.cpp + ./src/category.cpp + ./src/comment.cpp + ./src/main.cpp + ) + +add_executable(qxBlogCpp11 ${SRCS} ${HEADERS}) + +target_compile_definitions(qxBlogCpp11 PRIVATE -D_BUILDING_QX_BLOG) + +if(COMMAND target_precompile_headers) + target_precompile_headers(qxBlogCpp11 PRIVATE ./include/precompiled.h) +endif() # (COMMAND target_precompile_headers) + +target_link_libraries(qxBlogCpp11 ${QX_LIBRARIES} QxOrm) + +set_target_properties(qxBlogCpp11 PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ) + +set_target_properties(qxBlogCpp11 PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) diff --git a/test/qxBlogCpp11/debug/.gitignore b/test/qxBlogCpp11/debug/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogCpp11/debug/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogCpp11/include/author.h b/test/qxBlogCpp11/include/author.h new file mode 100644 index 0000000..6ee2023 --- /dev/null +++ b/test/qxBlogCpp11/include/author.h @@ -0,0 +1,57 @@ +#ifndef _QX_BLOG_AUTHOR_H_ +#define _QX_BLOG_AUTHOR_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT author +{ + + QX_REGISTER_FRIEND_CLASS(author) + +public: + +// -- composite key (multi-column primary key in database) + typedef QX_TUPLE type_composite_key; + static QString str_composite_key() { return "author_id_0|author_id_1|author_id_2"; } + +// -- typedef + typedef std::shared_ptr blog_ptr; + typedef std::vector list_blog; + +// -- enum + enum enum_sex { male, female, unknown }; + +// -- properties + type_composite_key m_id; + QString m_name; + QDate m_birthdate; + enum_sex m_sex; + list_blog m_blogX; + +// -- contructor, virtual destructor + author() : m_id("", 0, ""), m_sex(unknown) { ; } + virtual ~author() { ; } + +// -- methods + int age() const; + +// -- methods "get" to composite key + type_composite_key getId() const { return m_id; } + QString getId_0() const { return QX_TUPLE_GET<0>(m_id); } + long getId_1() const { return QX_TUPLE_GET<1>(m_id); } + QString getId_2() const { return QX_TUPLE_GET<2>(m_id); } + +// -- methods "set" to composite key + void setId_0(const QString & s) { QX_TUPLE_GET<0>(m_id) = s; } + void setId_1(long l) { QX_TUPLE_GET<1>(m_id) = l; } + void setId_2(const QString & s) { QX_TUPLE_GET<2>(m_id) = s; } + +}; + +QX_REGISTER_PRIMARY_KEY(author, author::type_composite_key) +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr author_ptr; +typedef qx::QxCollection list_author; + +#endif // _QX_BLOG_AUTHOR_H_ diff --git a/test/qxBlogCpp11/include/blog.h b/test/qxBlogCpp11/include/blog.h new file mode 100644 index 0000000..59c72d7 --- /dev/null +++ b/test/qxBlogCpp11/include/blog.h @@ -0,0 +1,48 @@ +#ifndef _QX_BLOG_BLOG_H_ +#define _QX_BLOG_BLOG_H_ + +#include "author.h" +#include "comment.h" +#include "category.h" + +class QX_BLOG_DLL_EXPORT blog +{ + + QX_REGISTER_FRIEND_CLASS(blog) + +public: + +// -- composite key (multi-column primary key in database) + typedef QPair type_composite_key; + static QString str_composite_key() { return "blog_id_0|blog_id_1"; } + +// -- properties + type_composite_key m_id; + QString m_text; + QDateTime m_dt_creation; + author_ptr m_author; + list_comment m_commentX; + list_category m_categoryX; + +// -- contructor, virtual destructor + blog() : m_id(0, "") { ; } + virtual ~blog() { ; } + +// -- methods "get" to composite key + type_composite_key getId() const { return m_id; } + long getId_0() const { return m_id.first; } + QString getId_1() const { return m_id.second; } + +// -- methods "set" to composite key + void setId_0(long l) { m_id.first = l; } + void setId_1(const QString & s) { m_id.second = s; } + +}; + +QX_REGISTER_PRIMARY_KEY(blog, blog::type_composite_key) +QX_REGISTER_HPP_QX_BLOG(blog, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr blog_ptr; +typedef std::vector list_blog; + +#endif // _QX_BLOG_BLOG_H_ diff --git a/test/qxBlogCpp11/include/category.h b/test/qxBlogCpp11/include/category.h new file mode 100644 index 0000000..ff90f5d --- /dev/null +++ b/test/qxBlogCpp11/include/category.h @@ -0,0 +1,52 @@ +#ifndef _QX_BLOG_CATEGORY_H_ +#define _QX_BLOG_CATEGORY_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT category +{ + + QX_REGISTER_FRIEND_CLASS(category) + +public: + +// -- composite key (multi-column primary key in database) + typedef QX_TUPLE type_composite_key; + static QString str_composite_key() { return "category_id_0|category_id_1|category_id_2|category_id_3"; } + +// -- typedef + typedef std::shared_ptr blog_ptr; + typedef qx::QxCollection list_blog; + +// -- properties + type_composite_key m_id; + QString m_name; + QString m_desc; + list_blog m_blogX; + +// -- contructor, virtual destructor + category() : m_id("", 0, "", 0) { ; } + virtual ~category() { ; } + +// -- methods "get" to composite key + type_composite_key getId() const { return m_id; } + QString getId_0() const { return QX_TUPLE_GET<0>(m_id); } + long getId_1() const { return QX_TUPLE_GET<1>(m_id); } + QString getId_2() const { return QX_TUPLE_GET<2>(m_id); } + long getId_3() const { return QX_TUPLE_GET<3>(m_id); } + +// -- methods "set" to composite key + void setId_0(const QString & s) { QX_TUPLE_GET<0>(m_id) = s; } + void setId_1(long l) { QX_TUPLE_GET<1>(m_id) = l; } + void setId_2(const QString & s) { QX_TUPLE_GET<2>(m_id) = s; } + void setId_3(long l) { QX_TUPLE_GET<3>(m_id) = l; } + +}; + +QX_REGISTER_PRIMARY_KEY(category, category::type_composite_key) +QX_REGISTER_HPP_QX_BLOG(category, qx::trait::no_base_class_defined, 0) + +typedef QSharedPointer category_ptr; +typedef qx::QxCollection list_category; + +#endif // _QX_BLOG_CATEGORY_H_ diff --git a/test/qxBlogCpp11/include/comment.h b/test/qxBlogCpp11/include/comment.h new file mode 100644 index 0000000..bb4a682 --- /dev/null +++ b/test/qxBlogCpp11/include/comment.h @@ -0,0 +1,47 @@ +#ifndef _QX_BLOG_COMMENT_H_ +#define _QX_BLOG_COMMENT_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT comment +{ + + QX_REGISTER_FRIEND_CLASS(comment) + +public: + +// -- composite key (multi-column primary key in database) + typedef QX_TUPLE type_composite_key; + static QString str_composite_key() { return "comment_id_0|comment_id_1"; } + +// -- typedef + typedef std::shared_ptr blog_ptr; + +// -- properties + type_composite_key m_id; + QString m_text; + QDateTime m_dt_create; + blog_ptr m_blog; + +// -- contructor, virtual destructor + comment() : m_id(0, "") { ; } + virtual ~comment() { ; } + +// -- methods "get" to composite key + type_composite_key getId() const { return m_id; } + long getId_0() const { return QX_TUPLE_GET<0>(m_id); } + QString getId_1() const { return QX_TUPLE_GET<1>(m_id); } + +// -- methods "set" to composite key + void setId_0(long l) { QX_TUPLE_GET<0>(m_id) = l; } + void setId_1(const QString & s) { QX_TUPLE_GET<1>(m_id) = s; } + +}; + +QX_REGISTER_PRIMARY_KEY(comment, comment::type_composite_key) +QX_REGISTER_HPP_QX_BLOG(comment, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr comment_ptr; +typedef QList list_comment; + +#endif // _QX_BLOG_COMMENT_H_ diff --git a/test/qxBlogCpp11/include/export.h b/test/qxBlogCpp11/include/export.h new file mode 100644 index 0000000..b1c11df --- /dev/null +++ b/test/qxBlogCpp11/include/export.h @@ -0,0 +1,18 @@ +#ifndef _QX_BLOG_EXPORT_H_ +#define _QX_BLOG_EXPORT_H_ + +#ifdef _BUILDING_QX_BLOG +#define QX_BLOG_DLL_EXPORT QX_DLL_EXPORT_HELPER +#else // _BUILDING_QX_BLOG +#define QX_BLOG_DLL_EXPORT QX_DLL_IMPORT_HELPER +#endif // _BUILDING_QX_BLOG + +#ifdef _BUILDING_QX_BLOG +#define QX_REGISTER_HPP_QX_BLOG QX_REGISTER_HPP_EXPORT_DLL +#define QX_REGISTER_CPP_QX_BLOG QX_REGISTER_CPP_EXPORT_DLL +#else // _BUILDING_QX_BLOG +#define QX_REGISTER_HPP_QX_BLOG QX_REGISTER_HPP_IMPORT_DLL +#define QX_REGISTER_CPP_QX_BLOG QX_REGISTER_CPP_IMPORT_DLL +#endif // _BUILDING_QX_BLOG + +#endif // _QX_BLOG_EXPORT_H_ diff --git a/test/qxBlogCpp11/include/precompiled.h b/test/qxBlogCpp11/include/precompiled.h new file mode 100644 index 0000000..79e12d4 --- /dev/null +++ b/test/qxBlogCpp11/include/precompiled.h @@ -0,0 +1,24 @@ +#ifndef _QX_BLOG_PRECOMPILED_HEADER_H_ +#define _QX_BLOG_PRECOMPILED_HEADER_H_ + +#include + +#include "export.h" + +/* +// To resolve a problem with MSVC++ 2010 and MSVC++ 2012 (and later ?) : +// These compilers provide a tuple header but BOOST_NO_CXX11_HDR_TUPLE macro is defined ! +// This is because these compilers doesn't provide variadic templates +#if (defined(_QX_CPP_11_TUPLE) && !defined(BOOST_NO_CXX11_HDR_TUPLE)) +#define QX_TUPLE std::tuple +#define QX_TUPLE_GET std::get +#else // (defined(_QX_CPP_11_TUPLE) && !defined(BOOST_NO_CXX11_HDR_TUPLE)) +#define QX_TUPLE boost::tuple +#define QX_TUPLE_GET boost::tuples::get +#endif // (defined(_QX_CPP_11_TUPLE) && !defined(BOOST_NO_CXX11_HDR_TUPLE)) +*/ + +#define QX_TUPLE std::tuple +#define QX_TUPLE_GET std::get + +#endif // _QX_BLOG_PRECOMPILED_HEADER_H_ diff --git a/test/qxBlogCpp11/qt/moc/.gitignore b/test/qxBlogCpp11/qt/moc/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogCpp11/qt/moc/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogCpp11/qxBlog.pro b/test/qxBlogCpp11/qxBlog.pro new file mode 100644 index 0000000..6cf9e47 --- /dev/null +++ b/test/qxBlogCpp11/qxBlog.pro @@ -0,0 +1,36 @@ +include(../../QxOrm.pri) + +TEMPLATE = app +DEFINES += _BUILDING_QX_BLOG +INCLUDEPATH += ../../../QxOrm/include/ +DESTDIR = ../../../QxOrm/test/_bin/ +LIBS += -L"../../../QxOrm/lib" + +!contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) { +PRECOMPILED_HEADER = ./include/precompiled.h +} # !contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) + +CONFIG += c++11 + +macx:CONFIG-=app_bundle + +CONFIG(debug, debug|release) { +TARGET = qxBlogCpp11d +LIBS += -l"QxOrmd" +} else { +TARGET = qxBlogCpp11 +LIBS += -l"QxOrm" +} # CONFIG(debug, debug|release) + +HEADERS += ./include/precompiled.h +HEADERS += ./include/export.h +HEADERS += ./include/author.h +HEADERS += ./include/blog.h +HEADERS += ./include/category.h +HEADERS += ./include/comment.h + +SOURCES += ./src/author.cpp +SOURCES += ./src/blog.cpp +SOURCES += ./src/category.cpp +SOURCES += ./src/comment.cpp +SOURCES += ./src/main.cpp diff --git a/test/qxBlogCpp11/release/.gitignore b/test/qxBlogCpp11/release/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogCpp11/release/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogCpp11/src/author.cpp b/test/qxBlogCpp11/src/author.cpp new file mode 100644 index 0000000..d08db6b --- /dev/null +++ b/test/qxBlogCpp11/src/author.cpp @@ -0,0 +1,28 @@ +#include "../include/precompiled.h" + +#include "../include/author.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(author) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& author::m_id, author::str_composite_key()); + + t.data(& author::m_name, "name"); + t.data(& author::m_birthdate, "birthdate"); + t.data(& author::m_sex, "sex"); + + t.relationOneToMany(& author::m_blogX, blog::str_composite_key(), author::str_composite_key()); + + t.fct_0(std::mem_fn(& author::age), "age"); // using std::mem_fn() here is just a workaround for an issue with some versions of MSVC, it is not required with a full compliant C++11 compiler (http://stackoverflow.com/questions/23778883/vs2013-stdfunction-with-member-function) +}} + +int author::age() const +{ + if (! m_birthdate.isValid()) { return -1; } + return (QDate::currentDate().year() - m_birthdate.year()); +} diff --git a/test/qxBlogCpp11/src/blog.cpp b/test/qxBlogCpp11/src/blog.cpp new file mode 100644 index 0000000..3e1e4eb --- /dev/null +++ b/test/qxBlogCpp11/src/blog.cpp @@ -0,0 +1,20 @@ +#include "../include/precompiled.h" + +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(blog) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& blog::m_id, blog::str_composite_key()); + + t.data(& blog::m_text, "blog_text"); + t.data(& blog::m_dt_creation, "date_creation"); + + t.relationManyToOne(& blog::m_author, author::str_composite_key()); + t.relationOneToMany(& blog::m_commentX, comment::str_composite_key(), blog::str_composite_key()); + t.relationManyToMany(& blog::m_categoryX, "list_category", "category_blog", blog::str_composite_key(), category::str_composite_key()); +}} diff --git a/test/qxBlogCpp11/src/category.cpp b/test/qxBlogCpp11/src/category.cpp new file mode 100644 index 0000000..cc27505 --- /dev/null +++ b/test/qxBlogCpp11/src/category.cpp @@ -0,0 +1,19 @@ +#include "../include/precompiled.h" + +#include "../include/category.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(category) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& category::m_id, category::str_composite_key()); + + t.data(& category::m_name, "name"); + t.data(& category::m_desc, "description"); + + t.relationManyToMany(& category::m_blogX, "list_blog", "category_blog", category::str_composite_key(), blog::str_composite_key()); +}} diff --git a/test/qxBlogCpp11/src/comment.cpp b/test/qxBlogCpp11/src/comment.cpp new file mode 100644 index 0000000..339995c --- /dev/null +++ b/test/qxBlogCpp11/src/comment.cpp @@ -0,0 +1,19 @@ +#include "../include/precompiled.h" + +#include "../include/comment.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(comment) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& comment::m_id, comment::str_composite_key()); + + t.data(& comment::m_text, "comment_text"); + t.data(& comment::m_dt_create, "date_creation"); + + t.relationManyToOne(& comment::m_blog, blog::str_composite_key()); +}} diff --git a/test/qxBlogCpp11/src/main.cpp b/test/qxBlogCpp11/src/main.cpp new file mode 100644 index 0000000..e76c835 --- /dev/null +++ b/test/qxBlogCpp11/src/main.cpp @@ -0,0 +1,209 @@ +#include "../include/precompiled.h" + +#include + +#include "../include/blog.h" +#include "../include/author.h" +#include "../include/comment.h" +#include "../include/category.h" + +#include + +int main(int argc, char * argv[]) +{ + // Qt application + QCoreApplication app(argc, argv); + QFile::remove("./qxBlogCpp11.sqlite"); + + // Parameters to connect to database + qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE"); + qx::QxSqlDatabase::getSingleton()->setDatabaseName("./qxBlogCpp11.sqlite"); + qx::QxSqlDatabase::getSingleton()->setHostName("localhost"); + qx::QxSqlDatabase::getSingleton()->setUserName("root"); + qx::QxSqlDatabase::getSingleton()->setPassword(""); + + // Only for debug purpose : assert if invalid offset detected fetching a relation + qx::QxSqlDatabase::getSingleton()->setVerifyOffsetRelation(true); + + // Create all tables in database + QSqlError daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + + // Create a list of 3 author + author_ptr author_1; author_1.reset(new author()); + author_ptr author_2; author_2.reset(new author()); + author_ptr author_3; author_3.reset(new author()); + + author_1->setId_0("_111_"); author_1->setId_1(100); author_1->setId_2("1_1"); + author_1->m_name = "author_1"; author_1->m_sex = author::male; author_1->m_birthdate = QDate::currentDate(); + author_2->setId_0("_222_"); author_2->setId_1(200); author_2->setId_2("2_2"); + author_2->m_name = "author_2"; author_2->m_sex = author::female; author_2->m_birthdate = QDate::currentDate(); + author_3->setId_0("_333_"); author_3->setId_1(300); author_3->setId_2("3_3"); + author_3->m_name = "author_3"; author_3->m_sex = author::female; author_3->m_birthdate = QDate::currentDate(); + + list_author authorX; + authorX.insert(author_1->getId(), author_1); + authorX.insert(author_2->getId(), author_2); + authorX.insert(author_3->getId(), author_3); + + // Insert list of 3 author into database + daoError = qx::dao::insert(authorX); + qAssert(qx::dao::count() == 3); + + // Clone author 2 : 'author_id_2' + author_ptr author_clone = qx::clone_to_std_shared_ptr(* author_2); + qAssert(author_clone->getId() == author::type_composite_key("_222_", 200, "2_2")); + qAssert(author_clone->m_sex == author::female); + + // Create a query to fetch only female author : 'author_id_2' and 'author_id_3' + qx::QxSqlQuery query("WHERE author.sex = :sex"); + query.bind(":sex", author::female); + + list_author list_of_female_author; + daoError = qx::dao::fetch_by_query(query, list_of_female_author); + qAssert(list_of_female_author.count() == 2); + + // Dump list of female author (xml serialization) + qx::dump(list_of_female_author); + + // Create 3 categories + category_ptr category_1 = category_ptr(new category()); + category_ptr category_2 = category_ptr(new category()); + category_ptr category_3 = category_ptr(new category()); + + category_1->setId_0("101"); category_1->setId_1(10); + category_1->setId_2("__11_XX"); category_1->setId_3(10010); + category_1->m_name = "category_1"; category_1->m_desc = "desc_1"; + category_2->setId_0("202"); category_2->setId_1(20); + category_2->setId_2("__22_XX"); category_2->setId_3(20020); + category_2->m_name = "category_2"; category_2->m_desc = "desc_2"; + category_3->setId_0("303"); category_3->setId_1(30); + category_3->setId_2("__33_XX"); category_3->setId_3(30030); + category_3->m_name = "category_3"; category_3->m_desc = "desc_3"; + + { // Create a scope to destroy temporary connexion to database + + // Open a transaction to database + QSqlDatabase db = qx::QxSqlDatabase::getDatabase(); + bool bCommit = db.transaction(); + + // Insert 3 categories into database, use 'db' parameter for the transaction + daoError = qx::dao::insert(category_1, (& db)); bCommit = (bCommit && ! daoError.isValid()); + daoError = qx::dao::insert(category_2, (& db)); bCommit = (bCommit && ! daoError.isValid()); + daoError = qx::dao::insert(category_3, (& db)); bCommit = (bCommit && ! daoError.isValid()); + + qAssert(bCommit); + qAssert(category_1->getId() == category::type_composite_key("101", 10, "__11_XX", 10010)); + qAssert(category_2->getId() == category::type_composite_key("202", 20, "__22_XX", 20020)); + qAssert(category_3->getId() == category::type_composite_key("303", 30, "__33_XX", 30030)); + + // Terminate transaction => commit or rollback if there is error + if (bCommit) { db.commit(); } + else { db.rollback(); } + + } // End of scope : 'db' is destroyed + + // Create a blog with the class name (factory) + qx::any blog_any = qx::create("blog"); + blog_ptr blog_1; + try { blog_1 = qx::any_cast(blog_any); } + catch (...) { blog_1.reset(new blog()); } + blog_1->setId_0(1); + blog_1->setId_1("blog 1"); + blog_1->m_text = "blog_text_1"; + blog_1->m_dt_creation = QDateTime::currentDateTime(); + blog_1->m_author = author_1; + + // Insert 'blog_1' into database with 'save()' method + daoError = qx::dao::save(blog_1); + + // Modify 'blog_1' properties and save into database + blog_1->m_text = "update blog_text_1"; + blog_1->m_author = author_2; + daoError = qx::dao::save(blog_1); + + // Add 2 comments to 'blog_1' + comment_ptr comment_1; comment_1.reset(new comment()); + comment_ptr comment_2; comment_2.reset(new comment()); + + comment_1->setId_0(1); + comment_1->setId_1("comment 1"); + comment_1->m_text = "comment_1 text"; + comment_1->m_dt_create = QDateTime::currentDateTime(); + comment_1->m_blog = blog_1; + comment_2->setId_0(2); + comment_2->setId_1("comment 2"); + comment_2->m_text = "comment_2 text"; + comment_2->m_dt_create = QDateTime::currentDateTime(); + comment_2->m_blog = blog_1; + + daoError = qx::dao::insert(comment_1); + daoError = qx::dao::insert(comment_2); + qAssert(! daoError.isValid() && (qx::dao::count() == 2)); + + // Add 2 categories to 'blog_1' => must insert into extra-table 'category_blog' + blog_1->m_categoryX.insert(category_1->getId(), category_1); + blog_1->m_categoryX.insert(category_3->getId(), category_3); + daoError = qx::dao::save_with_relation("list_category", blog_1); + + // Fetch blog into a new variable with all relation : 'author', 'comment' and 'category' + blog_ptr blog_tmp; blog_tmp.reset(new blog()); + blog_tmp->setId_0(blog_1->getId_0()); + blog_tmp->setId_1(blog_1->getId_1()); + daoError = qx::dao::fetch_by_id_with_all_relation(blog_tmp); + + qAssert(! daoError.isValid()); + qAssert(blog_tmp->m_commentX.count() == 2); + qAssert(blog_tmp->m_categoryX.count() == 2); + qAssert(blog_tmp->m_text == "update blog_text_1"); + qAssert(blog_tmp->m_author && blog_tmp->m_author->getId() == author::type_composite_key("_222_", 200, "2_2")); + + // Dump 'blog_tmp' result from database (xml serialization) + qx::dump(blog_tmp); + + // Call 'age()' method with class name and method name (reflexion) + qx_bool bInvokeOk = qx::QxClassX::invoke("author", "age", author_1); + qAssert(bInvokeOk); + + // Test 'isDirty()' method + qx::dao::ptr blog_isdirty = qx::dao::ptr(new blog()); + blog_isdirty->setId_0(blog_1->getId_0()); + blog_isdirty->setId_1(blog_1->getId_1()); + daoError = qx::dao::fetch_by_id(blog_isdirty); + qAssert(! daoError.isValid() && ! blog_isdirty.isDirty()); + + blog_isdirty->m_text = "blog property 'text' modified => blog is dirty !!!"; + QStringList lstDiff; bool bDirty = blog_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 1) && (lstDiff.at(0) == "blog_text")); + if (bDirty) { qDebug("[QxOrm] test dirty 1 : blog is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + daoError = qx::dao::update(blog_isdirty); + qAssert(! daoError.isValid() && ! blog_isdirty.isDirty()); + qx::dump(blog_isdirty); + + // Test 'isDirty()' method with a container + typedef qx::dao::ptr< QList > type_lst_author_test_is_dirty; + type_lst_author_test_is_dirty container_isdirty = type_lst_author_test_is_dirty(new QList()); + daoError = qx::dao::fetch_all(container_isdirty); + qAssert(! daoError.isValid() && ! container_isdirty.isDirty() && (container_isdirty->count() == 3)); + + author_ptr author_ptr_dirty = container_isdirty->at(1); + author_ptr_dirty->m_name = "author name modified at index 1 => container is dirty !!!"; + bDirty = container_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 1)); + if (bDirty) { qDebug("[QxOrm] test dirty 2 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + author_ptr_dirty = container_isdirty->at(2); + author_ptr_dirty->m_birthdate = QDate(1998, 03, 06); + bDirty = container_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 2)); + if (bDirty) { qDebug("[QxOrm] test dirty 3 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + daoError = qx::dao::save(container_isdirty); + qAssert(! daoError.isValid() && ! container_isdirty.isDirty()); + qx::dump(container_isdirty); + + return 0; +} diff --git a/test/qxBlogModelView/CMakeLists.txt b/test/qxBlogModelView/CMakeLists.txt new file mode 100644 index 0000000..48e7b60 --- /dev/null +++ b/test/qxBlogModelView/CMakeLists.txt @@ -0,0 +1,76 @@ +cmake_minimum_required(VERSION 3.1) + +project(qxBlogModelView LANGUAGES CXX) + +include(../../QxOrm.cmake) + +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Sql Gui Widgets Quick Qml REQUIRED) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_DEBUG_POSTFIX "d") + +set(HEADERS + ./include/precompiled.h + ./include/export.h + ./include/author.h + ./include/blog.h + ./include/category.h + ./include/comment.h + ./include/model_view_from_qxee/author.model_view.gen.h + ./include/model_view_from_qxee/blog.model_view.gen.h + ./include/model_view_from_qxee/category.model_view.gen.h + ./include/model_view_from_qxee/comment.model_view.gen.h + ) + +set(SRCS + ./src/author.cpp + ./src/blog.cpp + ./src/category.cpp + ./src/comment.cpp + ./src/main.cpp + ./src/model_view_from_qxee/author.model_view.gen.cpp + ./src/model_view_from_qxee/blog.model_view.gen.cpp + ./src/model_view_from_qxee/category.model_view.gen.cpp + ./src/model_view_from_qxee/comment.model_view.gen.cpp + ) + +set(QRCS + ./qt/rcc/qxBlogModelView.qrc + ) + +if(COMMAND qt_add_resources) +qt_add_resources(QRCS_HDRS ${QRCS}) +else() # (COMMAND qt_add_resources) +qt5_add_resources(QRCS_HDRS ${QRCS}) +endif() # (COMMAND qt_add_resources) + +add_executable(qxBlogModelView ${SRCS} ${HEADERS} ${QRCS_HDRS}) + +target_compile_definitions(qxBlogModelView PRIVATE -D_BUILDING_QX_BLOG) + +if(COMMAND target_precompile_headers) + target_precompile_headers(qxBlogModelView PRIVATE ./include/precompiled.h) +endif() # (COMMAND target_precompile_headers) + +target_link_libraries(qxBlogModelView ${QX_LIBRARIES} QxOrm Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Quick Qt${QT_VERSION_MAJOR}::Qml) + +set_target_properties(qxBlogModelView PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ) + +set_target_properties(qxBlogModelView PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) diff --git a/test/qxBlogModelView/debug/.gitignore b/test/qxBlogModelView/debug/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogModelView/debug/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogModelView/include/author.h b/test/qxBlogModelView/include/author.h new file mode 100644 index 0000000..b778fe3 --- /dev/null +++ b/test/qxBlogModelView/include/author.h @@ -0,0 +1,49 @@ +#ifndef _QX_BLOG_AUTHOR_H_ +#define _QX_BLOG_AUTHOR_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT author +{ +public: +// -- typedef + typedef std::shared_ptr blog_ptr; + typedef std::vector list_blog; + typedef std::vector type_list_of_blog; +// -- enum + enum enum_sex { male, female, unknown }; +// -- properties + QString m_id; + QString m_name; + QDate m_birthdate; + enum_sex m_sex; + list_blog m_blogX; +// -- contructor, virtual destructor + author() : m_id("0"), m_sex(unknown) { ; } + virtual ~author() { ; } +// -- methods + int age() const; + + QString getauthor_id() const; + QString getname() const; + QDate getbirthdate() const; + enum_sex getsex() const; + type_list_of_blog getlist_of_blog() const; + type_list_of_blog & list_of_blog(); + const type_list_of_blog & list_of_blog() const; + + void setauthor_id(const QString & val); + void setname(const QString & val); + void setbirthdate(const QDate & val); + void setsex(const enum_sex & val); + void setlist_of_blog(const type_list_of_blog & val); + +}; + +QX_REGISTER_PRIMARY_KEY(author, QString) +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr author_ptr; +typedef qx::QxCollection list_author; + +#endif // _QX_BLOG_AUTHOR_H_ diff --git a/test/qxBlogModelView/include/blog.h b/test/qxBlogModelView/include/blog.h new file mode 100644 index 0000000..e84c7f6 --- /dev/null +++ b/test/qxBlogModelView/include/blog.h @@ -0,0 +1,53 @@ +#ifndef _QX_BLOG_BLOG_H_ +#define _QX_BLOG_BLOG_H_ + +#include "author.h" +#include "comment.h" +#include "category.h" + +class QX_BLOG_DLL_EXPORT blog +{ +public: + + typedef std::shared_ptr type_author; + typedef QList type_list_of_comment; + typedef qx::QxCollection type_list_of_category; + +// -- properties + long m_id; + QString m_title; + QString m_text; + QDateTime m_dt_creation; + author_ptr m_author; + list_comment m_commentX; + list_category m_categoryX; +// -- contructor, virtual destructor + blog() : m_id(0) { ; } + virtual ~blog() { ; } + + long getblog_id() const; + QString gettitle() const; + QString gettext() const; + type_author getauthor() const; + type_list_of_comment getlist_of_comment() const; + type_list_of_comment & list_of_comment(); + const type_list_of_comment & list_of_comment() const; + type_list_of_category getlist_of_category() const; + type_list_of_category & list_of_category(); + const type_list_of_category & list_of_category() const; + + void setblog_id(const long & val); + void settitle(const QString & val); + void settext(const QString & val); + void setauthor(const type_author & val); + void setlist_of_comment(const type_list_of_comment & val); + void setlist_of_category(const type_list_of_category & val); + +}; + +QX_REGISTER_HPP_QX_BLOG(blog, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr blog_ptr; +typedef std::vector list_blog; + +#endif // _QX_BLOG_BLOG_H_ diff --git a/test/qxBlogModelView/include/category.h b/test/qxBlogModelView/include/category.h new file mode 100644 index 0000000..40d1d92 --- /dev/null +++ b/test/qxBlogModelView/include/category.h @@ -0,0 +1,41 @@ +#ifndef _QX_BLOG_CATEGORY_H_ +#define _QX_BLOG_CATEGORY_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT category +{ +public: +// -- typedef + typedef std::shared_ptr blog_ptr; + typedef qx::QxCollection list_blog; + typedef qx::QxCollection type_list_of_blog; +// -- properties + long m_id; + QString m_name; + QString m_desc; + list_blog m_blogX; +// -- contructor, virtual destructor + category() : m_id(0) { ; } + virtual ~category() { ; } + + long getcategory_id() const; + QString getname() const; + QString getdescription() const; + type_list_of_blog getlist_of_blog() const; + type_list_of_blog & list_of_blog(); + const type_list_of_blog & list_of_blog() const; + + void setcategory_id(const long & val); + void setname(const QString & val); + void setdescription(const QString & val); + void setlist_of_blog(const type_list_of_blog & val); + +}; + +QX_REGISTER_HPP_QX_BLOG(category, qx::trait::no_base_class_defined, 0) + +typedef QSharedPointer category_ptr; +typedef qx::QxCollection list_category; + +#endif // _QX_BLOG_CATEGORY_H_ diff --git a/test/qxBlogModelView/include/comment.h b/test/qxBlogModelView/include/comment.h new file mode 100644 index 0000000..e2e97af --- /dev/null +++ b/test/qxBlogModelView/include/comment.h @@ -0,0 +1,39 @@ +#ifndef _QX_BLOG_COMMENT_H_ +#define _QX_BLOG_COMMENT_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT comment +{ +public: +// -- typedef + typedef std::shared_ptr blog_ptr; + typedef std::shared_ptr type_blog_id; +// -- properties + long m_id; + QString m_title; + QString m_text; + QDateTime m_dt_create; + blog_ptr m_blog; +// -- contructor, virtual destructor + comment() : m_id(0) { ; } + virtual ~comment() { ; } + + long getcomment_id() const; + QString gettitle() const; + QString gettext() const; + type_blog_id getblog_id() const; + + void setcomment_id(const long & val); + void settitle(const QString & val); + void settext(const QString & val); + void setblog_id(const type_blog_id & val); + +}; + +QX_REGISTER_HPP_QX_BLOG(comment, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr comment_ptr; +typedef QList list_comment; + +#endif // _QX_BLOG_COMMENT_H_ diff --git a/test/qxBlogModelView/include/export.h b/test/qxBlogModelView/include/export.h new file mode 100644 index 0000000..b1c11df --- /dev/null +++ b/test/qxBlogModelView/include/export.h @@ -0,0 +1,18 @@ +#ifndef _QX_BLOG_EXPORT_H_ +#define _QX_BLOG_EXPORT_H_ + +#ifdef _BUILDING_QX_BLOG +#define QX_BLOG_DLL_EXPORT QX_DLL_EXPORT_HELPER +#else // _BUILDING_QX_BLOG +#define QX_BLOG_DLL_EXPORT QX_DLL_IMPORT_HELPER +#endif // _BUILDING_QX_BLOG + +#ifdef _BUILDING_QX_BLOG +#define QX_REGISTER_HPP_QX_BLOG QX_REGISTER_HPP_EXPORT_DLL +#define QX_REGISTER_CPP_QX_BLOG QX_REGISTER_CPP_EXPORT_DLL +#else // _BUILDING_QX_BLOG +#define QX_REGISTER_HPP_QX_BLOG QX_REGISTER_HPP_IMPORT_DLL +#define QX_REGISTER_CPP_QX_BLOG QX_REGISTER_CPP_IMPORT_DLL +#endif // _BUILDING_QX_BLOG + +#endif // _QX_BLOG_EXPORT_H_ diff --git a/test/qxBlogModelView/include/model_view_from_qxee/author.model_view.gen.h b/test/qxBlogModelView/include/model_view_from_qxee/author.model_view.gen.h new file mode 100644 index 0000000..99d203d --- /dev/null +++ b/test/qxBlogModelView/include/model_view_from_qxee/author.model_view.gen.h @@ -0,0 +1,50 @@ +/************************************************************************************************ +** File created by QxEntityEditor 1.1.9 (2016/05/04 10:05) : please, do NOT modify this file ! ** +************************************************************************************************/ + +#ifndef _QXBLOG_AUTHOR_MODEL_VIEW_H_ +#define _QXBLOG_AUTHOR_MODEL_VIEW_H_ + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include "../../include/precompiled.h" // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +#include "../../include/author.h" + +namespace model_view { + +typedef qx::QxModel author_model_base_class; + +class QX_BLOG_DLL_EXPORT author_model : public author_model_base_class +{ + + Q_OBJECT + +public: + + author_model(QObject * parent = 0); + author_model(qx::IxModel * other, QObject * parent); + virtual ~author_model(); + + Q_INVOKABLE QObject * list_of_blog(int row, bool bLoadFromDatabase = false, const QString & sAppendRelations = QString()); + + /* List of properties exposed by the model (5) : + - author_id + - firstname + - lastname + - birthdate + - sex + */ + +protected: + + virtual void syncNestedModel(int row, const QStringList & relation); + virtual void syncAllNestedModel(const QStringList & relation); + +}; + +} // namespace model_view + +#endif // _QXBLOG_AUTHOR_MODEL_VIEW_H_ diff --git a/test/qxBlogModelView/include/model_view_from_qxee/blog.model_view.gen.h b/test/qxBlogModelView/include/model_view_from_qxee/blog.model_view.gen.h new file mode 100644 index 0000000..a7be9ea --- /dev/null +++ b/test/qxBlogModelView/include/model_view_from_qxee/blog.model_view.gen.h @@ -0,0 +1,50 @@ +/************************************************************************************************ +** File created by QxEntityEditor 1.1.9 (2016/05/04 10:05) : please, do NOT modify this file ! ** +************************************************************************************************/ + +#ifndef _QXBLOG_BLOG_MODEL_VIEW_H_ +#define _QXBLOG_BLOG_MODEL_VIEW_H_ + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include "../../include/precompiled.h" // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +#include "../../include/blog.h" + +namespace model_view { + +typedef qx::QxModel blog_model_base_class; + +class QX_BLOG_DLL_EXPORT blog_model : public blog_model_base_class +{ + + Q_OBJECT + +public: + + blog_model(QObject * parent = 0); + blog_model(qx::IxModel * other, QObject * parent); + virtual ~blog_model(); + + Q_INVOKABLE QObject * author_id(int row, bool bLoadFromDatabase = false, const QString & sAppendRelations = QString()); + Q_INVOKABLE QObject * list_of_comment(int row, bool bLoadFromDatabase = false, const QString & sAppendRelations = QString()); + Q_INVOKABLE QObject * list_of_category(int row, bool bLoadFromDatabase = false, const QString & sAppendRelations = QString()); + + /* List of properties exposed by the model (3) : + - blog_id + - title + - text + */ + +protected: + + virtual void syncNestedModel(int row, const QStringList & relation); + virtual void syncAllNestedModel(const QStringList & relation); + +}; + +} // namespace model_view + +#endif // _QXBLOG_BLOG_MODEL_VIEW_H_ diff --git a/test/qxBlogModelView/include/model_view_from_qxee/category.model_view.gen.h b/test/qxBlogModelView/include/model_view_from_qxee/category.model_view.gen.h new file mode 100644 index 0000000..96ea941 --- /dev/null +++ b/test/qxBlogModelView/include/model_view_from_qxee/category.model_view.gen.h @@ -0,0 +1,48 @@ +/************************************************************************************************ +** File created by QxEntityEditor 1.1.9 (2016/05/04 10:05) : please, do NOT modify this file ! ** +************************************************************************************************/ + +#ifndef _QXBLOG_CATEGORY_MODEL_VIEW_H_ +#define _QXBLOG_CATEGORY_MODEL_VIEW_H_ + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include "../../include/precompiled.h" // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +#include "../../include/category.h" + +namespace model_view { + +typedef qx::QxModel category_model_base_class; + +class QX_BLOG_DLL_EXPORT category_model : public category_model_base_class +{ + + Q_OBJECT + +public: + + category_model(QObject * parent = 0); + category_model(qx::IxModel * other, QObject * parent); + virtual ~category_model(); + + Q_INVOKABLE QObject * list_of_blog(int row, bool bLoadFromDatabase = false, const QString & sAppendRelations = QString()); + + /* List of properties exposed by the model (3) : + - category_id + - name + - description + */ + +protected: + + virtual void syncNestedModel(int row, const QStringList & relation); + virtual void syncAllNestedModel(const QStringList & relation); + +}; + +} // namespace model_view + +#endif // _QXBLOG_CATEGORY_MODEL_VIEW_H_ diff --git a/test/qxBlogModelView/include/model_view_from_qxee/comment.model_view.gen.h b/test/qxBlogModelView/include/model_view_from_qxee/comment.model_view.gen.h new file mode 100644 index 0000000..7ec418d --- /dev/null +++ b/test/qxBlogModelView/include/model_view_from_qxee/comment.model_view.gen.h @@ -0,0 +1,48 @@ +/************************************************************************************************ +** File created by QxEntityEditor 1.1.9 (2016/05/04 10:05) : please, do NOT modify this file ! ** +************************************************************************************************/ + +#ifndef _QXBLOG_COMMENT_MODEL_VIEW_H_ +#define _QXBLOG_COMMENT_MODEL_VIEW_H_ + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include "../../include/precompiled.h" // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +#include "../../include/comment.h" + +namespace model_view { + +typedef qx::QxModel comment_model_base_class; + +class QX_BLOG_DLL_EXPORT comment_model : public comment_model_base_class +{ + + Q_OBJECT + +public: + + comment_model(QObject * parent = 0); + comment_model(qx::IxModel * other, QObject * parent); + virtual ~comment_model(); + + Q_INVOKABLE QObject * blog_id(int row, bool bLoadFromDatabase = false, const QString & sAppendRelations = QString()); + + /* List of properties exposed by the model (3) : + - comment_id + - title + - text + */ + +protected: + + virtual void syncNestedModel(int row, const QStringList & relation); + virtual void syncAllNestedModel(const QStringList & relation); + +}; + +} // namespace model_view + +#endif // _QXBLOG_COMMENT_MODEL_VIEW_H_ diff --git a/test/qxBlogModelView/include/precompiled.h b/test/qxBlogModelView/include/precompiled.h new file mode 100644 index 0000000..e5cbe6c --- /dev/null +++ b/test/qxBlogModelView/include/precompiled.h @@ -0,0 +1,9 @@ +#ifndef _QX_BLOG_PRECOMPILED_HEADER_H_ +#define _QX_BLOG_PRECOMPILED_HEADER_H_ + +#include +#include + +#include "export.h" + +#endif // _QX_BLOG_PRECOMPILED_HEADER_H_ diff --git a/test/qxBlogModelView/qt/moc/.gitignore b/test/qxBlogModelView/qt/moc/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogModelView/qt/moc/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogModelView/qt/rcc/documents/main.qml b/test/qxBlogModelView/qt/rcc/documents/main.qml new file mode 100644 index 0000000..183fe1d --- /dev/null +++ b/test/qxBlogModelView/qt/rcc/documents/main.qml @@ -0,0 +1,37 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 + +Item { + width: 400 + height: 300 + Row { + height: 20 + spacing: 20 + Button { + text: "Clear" + onClicked: myModel.clear() + } + Button { + text: "Fetch All" + onClicked: myModel.qxFetchAll_() + } + Button { + text: "Save" + onClicked: myModel.qxSave_() + } + } + ListView { + y: 30 + height: 270 + model: myModel + delegate: Row { + height: 20 + spacing: 10 + Text { text: "id: " + author_id } + TextField { + text: name + onTextChanged: name = text + } + } + } +} diff --git a/test/qxBlogModelView/qt/rcc/documents/main_qt4.qml b/test/qxBlogModelView/qt/rcc/documents/main_qt4.qml new file mode 100644 index 0000000..7ba8d21 --- /dev/null +++ b/test/qxBlogModelView/qt/rcc/documents/main_qt4.qml @@ -0,0 +1,16 @@ +import QtQuick 1.0 + +Item { + width: 400 + height: 300 + ListView { + anchors.fill: parent + model: myModel + delegate: Row { + height: 20 + spacing: 10 + Text { text: "id: " + author_id } + Text { text: "name: " + name } + } + } +} diff --git a/test/qxBlogModelView/qt/rcc/documents/main_qt6.qml b/test/qxBlogModelView/qt/rcc/documents/main_qt6.qml new file mode 100644 index 0000000..d278f59 --- /dev/null +++ b/test/qxBlogModelView/qt/rcc/documents/main_qt6.qml @@ -0,0 +1,37 @@ +import QtQuick +import QtQuick.Controls + +Item { + width: 400 + height: 300 + Row { + height: 20 + spacing: 20 + Button { + text: "Clear" + onClicked: myModel.clear() + } + Button { + text: "Fetch All" + onClicked: myModel.qxFetchAll_() + } + Button { + text: "Save" + onClicked: myModel.qxSave_() + } + } + ListView { + y: 30 + height: 270 + model: myModel + delegate: Row { + height: 20 + spacing: 10 + Text { text: "id: " + author_id } + TextField { + text: name + onTextChanged: name = text + } + } + } +} diff --git a/test/qxBlogModelView/qt/rcc/documents/main_relationship.qml b/test/qxBlogModelView/qt/rcc/documents/main_relationship.qml new file mode 100644 index 0000000..9bba5fc --- /dev/null +++ b/test/qxBlogModelView/qt/rcc/documents/main_relationship.qml @@ -0,0 +1,93 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 + +Item { + width: 500 + height: 300 + + /* + 1- How to display a relationship value : + * root model : blog + * child model : author + * display author name using getModelValue() method of qx::IxModel base class + */ + Text { + y: 30 + color: "blue" + text: "--- 1. display relationship author name using getModelValue() method of qx::IxModel base class ---" + } + ListView { + y: 50 + height: 270 + model: myModel + delegate: Row { + height: 20 + spacing: 10 + Text { text: "blog id : " + blog_id } + TextField { + text: blog_text + onTextChanged: blog_text = text + } + Text { text: "author name : " + myModel.author_id(0).getModelValue(0, "name") } // display author name using getModelValue() method of qx::IxModel base class + } + } + + /* + 2- How to display a relationship value : + * root model : blog + * child model : author + * display author name passing the author child model to another QML component, then using the role value + */ + Text { + y: 80 + color: "blue" + text: "--- 2. display relationship author name passing the author child model to another QML component, then using the author 'name' role value ---" + } + ListView { + y: 100 + height: 270 + model: myModel + delegate: Row { + height: 20 + spacing: 10 + Text { text: "blog id : " + blog_id } + TextField { + text: blog_text + onTextChanged: blog_text = text + } + ListView { + x: 200 + height: 270 + model: myModel.author_id(0) // pass the author child model to another QML component + delegate: Row { + Text { text: "author name : " + name } // use role 'name' defined in author class to display the author name + } + } + } + } + + /* + 3- Display the author row id in database using the 'author_id' role defined in blog class + */ + Text { + y: 130 + color: "blue" + text: "--- 3. display the author row id in database using the 'author_id' role defined in blog class ---" + } + ListView { + y: 150 + height: 270 + model: myModel + delegate: Row { + height: 20 + spacing: 10 + Text { text: "blog id : " + blog_id } + TextField { + text: blog_text + onTextChanged: blog_text = text + } + Text { text: "author id : " + author_id } + } + } + +} diff --git a/test/qxBlogModelView/qt/rcc/documents/main_relationship_qt6.qml b/test/qxBlogModelView/qt/rcc/documents/main_relationship_qt6.qml new file mode 100644 index 0000000..78637b5 --- /dev/null +++ b/test/qxBlogModelView/qt/rcc/documents/main_relationship_qt6.qml @@ -0,0 +1,93 @@ +import QtQuick +import QtQuick.Controls + +Item { + width: 500 + height: 300 + + /* + 1- How to display a relationship value : + * root model : blog + * child model : author + * display author name using getModelValue() method of qx::IxModel base class + */ + Text { + y: 30 + color: "blue" + text: "--- 1. display relationship author name using getModelValue() method of qx::IxModel base class ---" + } + ListView { + y: 50 + height: 270 + model: myModel + delegate: Row { + height: 20 + spacing: 10 + Text { text: "blog id : " + blog_id } + TextField { + text: blog_text + onTextChanged: blog_text = text + } + Text { text: "author name : " + myModel.author_id(0).getModelValue(0, "name") } // display author name using getModelValue() method of qx::IxModel base class + } + } + + /* + 2- How to display a relationship value : + * root model : blog + * child model : author + * display author name passing the author child model to another QML component, then using the role value + */ + Text { + y: 80 + color: "blue" + text: "--- 2. display relationship author name passing the author child model to another QML component, then using the author 'name' role value ---" + } + ListView { + y: 100 + height: 270 + model: myModel + delegate: Row { + height: 20 + spacing: 10 + Text { text: "blog id : " + blog_id } + TextField { + text: blog_text + onTextChanged: blog_text = text + } + ListView { + x: 200 + height: 270 + model: myModel.author_id(0) // pass the author child model to another QML component + delegate: Row { + Text { text: "author name : " + name } // use role 'name' defined in author class to display the author name + } + } + } + } + + /* + 3- Display the author row id in database using the 'author_id' role defined in blog class + */ + Text { + y: 130 + color: "blue" + text: "--- 3. display the author row id in database using the 'author_id' role defined in blog class ---" + } + ListView { + y: 150 + height: 270 + model: myModel + delegate: Row { + height: 20 + spacing: 10 + Text { text: "blog id : " + blog_id } + TextField { + text: blog_text + onTextChanged: blog_text = text + } + Text { text: "author id : " + author_id } + } + } + +} diff --git a/test/qxBlogModelView/qt/rcc/images/.gitignore b/test/qxBlogModelView/qt/rcc/images/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogModelView/qt/rcc/images/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogModelView/qt/rcc/qxBlogModelView.qrc b/test/qxBlogModelView/qt/rcc/qxBlogModelView.qrc new file mode 100644 index 0000000..43b3c12 --- /dev/null +++ b/test/qxBlogModelView/qt/rcc/qxBlogModelView.qrc @@ -0,0 +1,9 @@ + + + documents/main.qml + documents/main_qt4.qml + documents/main_qt6.qml + documents/main_relationship.qml + documents/main_relationship_qt6.qml + + diff --git a/test/qxBlogModelView/qxBlog.pro b/test/qxBlogModelView/qxBlog.pro new file mode 100644 index 0000000..12d02ae --- /dev/null +++ b/test/qxBlogModelView/qxBlog.pro @@ -0,0 +1,53 @@ +include(../../QxOrm.pri) + +TEMPLATE = app +DEFINES += _BUILDING_QX_BLOG +INCLUDEPATH += ../../../QxOrm/include/ +DESTDIR = ../../../QxOrm/test/_bin/ +LIBS += -L"../../../QxOrm/lib" +QT += gui + +!contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) { +PRECOMPILED_HEADER = ./include/precompiled.h +} # !contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) + +greaterThan(QT_MAJOR_VERSION, 4) { +QT += widgets +QT += quick +QT += qml +} else { +QT += declarative +} + +CONFIG(debug, debug|release) { +TARGET = qxBlogModelViewd +LIBS += -l"QxOrmd" +} else { +TARGET = qxBlogModelView +LIBS += -l"QxOrm" +} # CONFIG(debug, debug|release) + +HEADERS += ./include/precompiled.h +HEADERS += ./include/export.h +HEADERS += ./include/author.h +HEADERS += ./include/blog.h +HEADERS += ./include/category.h +HEADERS += ./include/comment.h + +HEADERS += ./include/model_view_from_qxee/author.model_view.gen.h +HEADERS += ./include/model_view_from_qxee/blog.model_view.gen.h +HEADERS += ./include/model_view_from_qxee/category.model_view.gen.h +HEADERS += ./include/model_view_from_qxee/comment.model_view.gen.h + +SOURCES += ./src/author.cpp +SOURCES += ./src/blog.cpp +SOURCES += ./src/category.cpp +SOURCES += ./src/comment.cpp +SOURCES += ./src/main.cpp + +SOURCES += ./src/model_view_from_qxee/author.model_view.gen.cpp +SOURCES += ./src/model_view_from_qxee/blog.model_view.gen.cpp +SOURCES += ./src/model_view_from_qxee/category.model_view.gen.cpp +SOURCES += ./src/model_view_from_qxee/comment.model_view.gen.cpp + +RESOURCES += ./qt/rcc/qxBlogModelView.qrc diff --git a/test/qxBlogModelView/release/.gitignore b/test/qxBlogModelView/release/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogModelView/release/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogModelView/src/author.cpp b/test/qxBlogModelView/src/author.cpp new file mode 100644 index 0000000..f76c8e1 --- /dev/null +++ b/test/qxBlogModelView/src/author.cpp @@ -0,0 +1,52 @@ +#include "../include/precompiled.h" + +#include "../include/author.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(author) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& author::m_id, "author_id"); + + t.data(& author::m_name, "name"); + t.data(& author::m_birthdate, "birthdate"); + t.data(& author::m_sex, "sex"); + + t.relationOneToMany(& author::m_blogX, "list_blog", "author_id"); + + t.fct_0(std::mem_fn(& author::age), "age"); // using std::mem_fn() here is just a workaround for an issue with some versions of MSVC, it is not required with a full compliant C++11 compiler (http://stackoverflow.com/questions/23778883/vs2013-stdfunction-with-member-function) +}} + +int author::age() const +{ + if (! m_birthdate.isValid()) { return -1; } + return (QDate::currentDate().year() - m_birthdate.year()); +} + +QString author::getauthor_id() const { return m_id; } + +QString author::getname() const { return m_name; } + +QDate author::getbirthdate() const { return m_birthdate; } + +author::enum_sex author::getsex() const { return m_sex; } + +author::type_list_of_blog author::getlist_of_blog() const { return m_blogX; } + +author::type_list_of_blog & author::list_of_blog() { return m_blogX; } + +const author::type_list_of_blog & author::list_of_blog() const { return m_blogX; } + +void author::setauthor_id(const QString & val) { m_id = val; } + +void author::setname(const QString & val) { m_name = val; } + +void author::setbirthdate(const QDate & val) { m_birthdate = val; } + +void author::setsex(const author::enum_sex & val) { m_sex = val; } + +void author::setlist_of_blog(const author::type_list_of_blog & val) { m_blogX = val; } diff --git a/test/qxBlogModelView/src/blog.cpp b/test/qxBlogModelView/src/blog.cpp new file mode 100644 index 0000000..d6251b8 --- /dev/null +++ b/test/qxBlogModelView/src/blog.cpp @@ -0,0 +1,53 @@ +#include "../include/precompiled.h" + +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(blog) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& blog::m_id, "blog_id"); + + t.data(& blog::m_title, "blog_title"); + t.data(& blog::m_text, "blog_text"); + t.data(& blog::m_dt_creation, "date_creation"); + + t.relationManyToOne(& blog::m_author, "author_id"); + t.relationOneToMany(& blog::m_commentX, "list_comment", "blog_id"); + t.relationManyToMany(& blog::m_categoryX, "list_category", "category_blog", "blog_id", "category_id"); +}} + +long blog::getblog_id() const { return m_id; } + +QString blog::gettitle() const { return m_title; } + +QString blog::gettext() const { return m_text; } + +blog::type_author blog::getauthor() const { return m_author; } + +blog::type_list_of_comment blog::getlist_of_comment() const { return m_commentX; } + +blog::type_list_of_comment & blog::list_of_comment() { return m_commentX; } + +const blog::type_list_of_comment & blog::list_of_comment() const { return m_commentX; } + +blog::type_list_of_category blog::getlist_of_category() const { return m_categoryX; } + +blog::type_list_of_category & blog::list_of_category() { return m_categoryX; } + +const blog::type_list_of_category & blog::list_of_category() const { return m_categoryX; } + +void blog::setblog_id(const long & val) { m_id = val; } + +void blog::settitle(const QString & val) { m_title = val; } + +void blog::settext(const QString & val) { m_text = val; } + +void blog::setauthor(const blog::type_author & val) { m_author = val; } + +void blog::setlist_of_comment(const blog::type_list_of_comment & val) { m_commentX = val; } + +void blog::setlist_of_category(const blog::type_list_of_category & val) { m_categoryX = val; } diff --git a/test/qxBlogModelView/src/category.cpp b/test/qxBlogModelView/src/category.cpp new file mode 100644 index 0000000..e2e6a2c --- /dev/null +++ b/test/qxBlogModelView/src/category.cpp @@ -0,0 +1,39 @@ +#include "../include/precompiled.h" + +#include "../include/category.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(category) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& category::m_id, "category_id"); + + t.data(& category::m_name, "name"); + t.data(& category::m_desc, "description"); + + t.relationManyToMany(& category::m_blogX, "list_blog", "category_blog", "category_id", "blog_id"); +}} + +long category::getcategory_id() const { return m_id; } + +QString category::getname() const { return m_name; } + +QString category::getdescription() const { return m_desc; } + +category::type_list_of_blog category::getlist_of_blog() const { return m_blogX; } + +category::type_list_of_blog & category::list_of_blog() { return m_blogX; } + +const category::type_list_of_blog & category::list_of_blog() const { return m_blogX; } + +void category::setcategory_id(const long & val) { m_id = val; } + +void category::setname(const QString & val) { m_name = val; } + +void category::setdescription(const QString & val) { m_desc = val; } + +void category::setlist_of_blog(const category::type_list_of_blog & val) { m_blogX = val; } diff --git a/test/qxBlogModelView/src/comment.cpp b/test/qxBlogModelView/src/comment.cpp new file mode 100644 index 0000000..3c18843 --- /dev/null +++ b/test/qxBlogModelView/src/comment.cpp @@ -0,0 +1,36 @@ +#include "../include/precompiled.h" + +#include "../include/comment.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(comment) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& comment::m_id, "comment_id"); + + t.data(& comment::m_title, "comment_title"); + t.data(& comment::m_text, "comment_text"); + t.data(& comment::m_dt_create, "date_creation"); + + t.relationManyToOne(& comment::m_blog, "blog_id"); +}} + +long comment::getcomment_id() const { return m_id; } + +QString comment::gettitle() const { return m_title; } + +QString comment::gettext() const { return m_text; } + +comment::type_blog_id comment::getblog_id() const { return m_blog; } + +void comment::setcomment_id(const long & val) { m_id = val; } + +void comment::settitle(const QString & val) { m_title = val; } + +void comment::settext(const QString & val) { m_text = val; } + +void comment::setblog_id(const comment::type_blog_id & val) { m_blog = val; } diff --git a/test/qxBlogModelView/src/main.cpp b/test/qxBlogModelView/src/main.cpp new file mode 100644 index 0000000..3052c32 --- /dev/null +++ b/test/qxBlogModelView/src/main.cpp @@ -0,0 +1,325 @@ +#include "../include/precompiled.h" + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#include +#include +#include +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#include +#include +#include +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + +#include "../include/blog.h" +#include "../include/author.h" +#include "../include/comment.h" +#include "../include/category.h" + +#include "../include/model_view_from_qxee/blog.model_view.gen.h" + +#include + +void init_data(); +void test_qt_widget(); +void test_qml_view(); +void test_qml_view_with_relationship(); + +int main(int argc, char * argv[]) +{ + QApplication app(argc, argv); + init_data(); + test_qt_widget(); + test_qml_view(); + test_qml_view_with_relationship(); + return 0; +} + +void init_data() +{ + QFile::remove("./qxBlog.sqlite"); + + // Parameters to connect to database + qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE"); + qx::QxSqlDatabase::getSingleton()->setDatabaseName("./qxBlog.sqlite"); + qx::QxSqlDatabase::getSingleton()->setHostName("localhost"); + qx::QxSqlDatabase::getSingleton()->setUserName("root"); + qx::QxSqlDatabase::getSingleton()->setPassword(""); + + // Only for debug purpose : assert if invalid offset detected fetching a relation + qx::QxSqlDatabase::getSingleton()->setVerifyOffsetRelation(true); + + // Create all tables in database + QSqlError daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + + // Create a list of 3 author + author_ptr author_1; author_1.reset(new author()); + author_ptr author_2; author_2.reset(new author()); + author_ptr author_3; author_3.reset(new author()); + + author_1->m_id = "author_id_1"; author_1->m_name = "author_1"; + author_1->m_sex = author::male; author_1->m_birthdate = QDate::currentDate(); + author_2->m_id = "author_id_2"; author_2->m_name = "author_2"; + author_2->m_sex = author::female; author_2->m_birthdate = QDate::currentDate(); + author_3->m_id = "author_id_3"; author_3->m_name = "author_3"; + author_3->m_sex = author::female; author_3->m_birthdate = QDate::currentDate(); + + list_author authorX; + authorX.insert(author_1->m_id, author_1); + authorX.insert(author_2->m_id, author_2); + authorX.insert(author_3->m_id, author_3); + + // Insert list of 3 author into database + daoError = qx::dao::insert(authorX); + qAssert(qx::dao::count() == 3); + + // Clone author 2 : 'author_id_2' + author_ptr author_clone = qx::clone(* author_2); + qAssert(author_clone->m_id == "author_id_2"); + qAssert(author_clone->m_sex == author::female); + + // Create a query to fetch only female author : 'author_id_2' and 'author_id_3' + qx::QxSqlQuery query("WHERE author.sex = :sex"); + query.bind(":sex", author::female); + + list_author list_of_female_author; + daoError = qx::dao::fetch_by_query(query, list_of_female_author); + qAssert(list_of_female_author.count() == 2); + + // Dump list of female author (xml serialization) + qx::dump(list_of_female_author); + + // Create 3 categories + category_ptr category_1 = category_ptr(new category()); + category_ptr category_2 = category_ptr(new category()); + category_ptr category_3 = category_ptr(new category()); + + category_1->m_name = "category_1"; category_1->m_desc = "desc_1"; + category_2->m_name = "category_2"; category_2->m_desc = "desc_2"; + category_3->m_name = "category_3"; category_3->m_desc = "desc_3"; + + { // Create a scope to destroy temporary connexion to database + + // Open a transaction to database + QSqlDatabase db = qx::QxSqlDatabase::getDatabase(); + bool bCommit = db.transaction(); + + // Insert 3 categories into database, use 'db' parameter for the transaction + daoError = qx::dao::insert(category_1, (& db)); bCommit = (bCommit && ! daoError.isValid()); + daoError = qx::dao::insert(category_2, (& db)); bCommit = (bCommit && ! daoError.isValid()); + daoError = qx::dao::insert(category_3, (& db)); bCommit = (bCommit && ! daoError.isValid()); + + qAssert(bCommit); + qAssert(category_1->m_id != 0); + qAssert(category_2->m_id != 0); + qAssert(category_3->m_id != 0); + + // Terminate transaction => commit or rollback if there is error + if (bCommit) { db.commit(); } + else { db.rollback(); } + + } // End of scope : 'db' is destroyed + + // Create a blog with the class name (factory) + qx::any blog_any = qx::create("blog"); + blog_ptr blog_1; + try { blog_1 = qx::any_cast(blog_any); } + catch (...) { blog_1.reset(new blog()); } + blog_1->m_text = "blog_text_1"; + blog_1->m_dt_creation = QDateTime::currentDateTime(); + blog_1->m_author = author_1; + + // Insert 'blog_1' into database with 'save()' method + daoError = qx::dao::save(blog_1); + + // Modify 'blog_1' properties and save into database + blog_1->m_text = "update blog_text_1"; + blog_1->m_author = author_2; + daoError = qx::dao::save(blog_1); + + // Add 2 comments to 'blog_1' + comment_ptr comment_1; comment_1.reset(new comment()); + comment_ptr comment_2; comment_2.reset(new comment()); + + comment_1->m_text = "comment_1 text"; + comment_1->m_dt_create = QDateTime::currentDateTime(); + comment_1->m_blog = blog_1; + comment_2->m_text = "comment_2 text"; + comment_2->m_dt_create = QDateTime::currentDateTime(); + comment_2->m_blog = blog_1; + + daoError = qx::dao::insert(comment_1); + daoError = qx::dao::insert(comment_2); + qAssert(qx::dao::count() == 2); + + // Add 2 categories to 'blog_1' => must insert into extra-table 'category_blog' + blog_1->m_categoryX.insert(category_1->m_id, category_1); + blog_1->m_categoryX.insert(category_3->m_id, category_3); + daoError = qx::dao::save_with_relation("list_category", blog_1); + + // Fetch blog into a new variable with all relation : 'author', 'comment' and 'category' + blog_ptr blog_tmp; blog_tmp.reset(new blog()); + blog_tmp->m_id = blog_1->m_id; + daoError = qx::dao::fetch_by_id_with_all_relation(blog_tmp); + + qAssert(blog_tmp->m_commentX.count() == 2); + qAssert(blog_tmp->m_categoryX.count() == 2); + qAssert(blog_tmp->m_text == "update blog_text_1"); + qAssert(blog_tmp->m_author && blog_tmp->m_author->m_id == "author_id_2"); + + // Fetch blog into a new variable with many relations using "*->*->*->*" (4 levels of relationships) + blog_tmp.reset(new blog()); + blog_tmp->m_id = blog_1->m_id; + daoError = qx::dao::fetch_by_id_with_relation("*->*->*->*", blog_tmp); + + qAssert(blog_tmp->m_commentX.count() == 2); + qAssert(blog_tmp->m_categoryX.count() == 2); + qAssert(blog_tmp->m_text == "update blog_text_1"); + qAssert(blog_tmp->m_author && blog_tmp->m_author->m_id == "author_id_2"); + + // Dump 'blog_tmp' result from database (xml serialization) + qx::dump(blog_tmp); + + // Check qx::dao::save_with_relation_recursive() function + daoError = qx::dao::save_with_relation_recursive(blog_tmp); + qAssert(! daoError.isValid()); + daoError = qx::dao::save_with_relation_recursive(blog_tmp, qx::dao::save_mode::e_update_only); + qAssert(! daoError.isValid()); + + // Call 'age()' method with class name and method name (reflexion) + qx_bool bInvokeOk = qx::QxClassX::invoke("author", "age", author_1); + qAssert(bInvokeOk); + + // Test 'isDirty()' method + qx::dao::ptr blog_isdirty = qx::dao::ptr(new blog()); + blog_isdirty->m_id = blog_1->m_id; + daoError = qx::dao::fetch_by_id(blog_isdirty); + qAssert(! daoError.isValid() && ! blog_isdirty.isDirty()); + + blog_isdirty->m_text = "blog property 'text' modified => blog is dirty !!!"; + QStringList lstDiff; bool bDirty = blog_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 1) && (lstDiff.at(0) == "blog_text")); + if (bDirty) { qDebug("[QxOrm] test dirty 1 : blog is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + // Update only property 'm_text' of 'blog_isdirty' + daoError = qx::dao::update_optimized(blog_isdirty); + qAssert(! daoError.isValid() && ! blog_isdirty.isDirty()); + qx::dump(blog_isdirty); + + // Test 'isDirty()' method with a container + typedef qx::dao::ptr< QList > type_lst_author_test_is_dirty; + type_lst_author_test_is_dirty container_isdirty = type_lst_author_test_is_dirty(new QList()); + daoError = qx::dao::fetch_all(container_isdirty); + qAssert(! daoError.isValid() && ! container_isdirty.isDirty() && (container_isdirty->count() == 3)); + + author_ptr author_ptr_dirty = container_isdirty->at(1); + author_ptr_dirty->m_name = "author name modified at index 1 => container is dirty !!!"; + bDirty = container_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 1)); + if (bDirty) { qDebug("[QxOrm] test dirty 2 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + author_ptr_dirty = container_isdirty->at(2); + author_ptr_dirty->m_birthdate = QDate(1998, 03, 06); + bDirty = container_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 2)); + if (bDirty) { qDebug("[QxOrm] test dirty 3 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + // Update only property 'm_name' at position 1, only property 'm_birthdate' at position 2 and nothing at position 0 + daoError = qx::dao::update_optimized(container_isdirty); + qAssert(! daoError.isValid() && ! container_isdirty.isDirty()); + qx::dump(container_isdirty); + + // Fetch only property 'm_dt_creation' of blog + QStringList lstColumns = QStringList() << "date_creation"; + list_blog lst_blog_with_only_date_creation; + daoError = qx::dao::fetch_all(lst_blog_with_only_date_creation, NULL, lstColumns); + qAssert(! daoError.isValid() && (lst_blog_with_only_date_creation.size() > 0)); + if ((lst_blog_with_only_date_creation.size() > 0) && (lst_blog_with_only_date_creation[0].get() != NULL)) + { qAssert(lst_blog_with_only_date_creation[0]->m_text.isEmpty()); } + qx::dump(lst_blog_with_only_date_creation); + + // Dump all registered classes into QxOrm context (introspection engine) + qx::QxClassX::dumpAllClasses(); + + // Call a custom SQL query or a stored procedure + qx_query testStoredProc("SELECT * FROM author"); + daoError = qx::dao::call_query(testStoredProc); + qAssert(! daoError.isValid()); + testStoredProc.dumpSqlResult(); + + // Call a custom SQL query or a stored procedure and fetch automatically properties (with a collection of items) + qx_query testStoredProcBis("SELECT * FROM author"); + authorX.clear(); + daoError = qx::dao::execute_query(testStoredProcBis, authorX); + qAssert(! daoError.isValid()); qAssert(authorX.count() > 0); + qx::dump(authorX); + + // Call a custom SQL query or a stored procedure and fetch automatically properties + qx_query testStoredProcThird("SELECT name, category_id FROM category"); + category_ptr category_tmp = category_ptr(new category()); + daoError = qx::dao::execute_query(testStoredProcThird, category_tmp); + qAssert(! daoError.isValid()); qAssert(category_tmp->m_id != 0); + qx::dump(category_tmp); + + // Just to test compilation of nested models + qx::IxModel * pNestedModel1 = qx::model_view::create_nested_model(NULL, QModelIndex(), category_tmp); Q_UNUSED(pNestedModel1); + qx::IxModel * pNestedModel2 = qx::model_view::create_nested_model(NULL, QModelIndex(), authorX); Q_UNUSED(pNestedModel2); +} + +void test_qt_widget() +{ + qx::IxModel * pModel = new qx::QxModel(); + pModel->qxFetchAll(); + + QTableView tableView; + tableView.setModel(pModel); + tableView.setSortingEnabled(true); + tableView.show(); + qApp->exec(); +} + +void test_qml_view() +{ + qx::IxModel * pModel = new qx::QxModel(); + pModel->qxFetchAll(); + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QQuickView qmlView; + QString sQmlFile = "qrc:/documents/main_qt6.qml"; +#elif (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QQuickView qmlView; + QString sQmlFile = "qrc:/documents/main.qml"; +#else // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QDeclarativeView qmlView; + QString sQmlFile = "qrc:/documents/main_qt4.qml"; +#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + + qmlView.rootContext()->setContextProperty("myModel", pModel); + qmlView.setSource(QUrl(sQmlFile)); + qmlView.show(); + qApp->exec(); +} + +void test_qml_view_with_relationship() +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + qx::IxModel * pModel = new model_view::blog_model(); + pModel->qxFetchAll(QStringList() << "*"); + + QQuickView qmlView; +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QString sQmlFile = "qrc:/documents/main_relationship_qt6.qml"; +#else // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QString sQmlFile = "qrc:/documents/main_relationship.qml"; +#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + + qmlView.rootContext()->setContextProperty("myModel", pModel); + qmlView.setSource(QUrl(sQmlFile)); + qmlView.show(); + qApp->exec(); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +} diff --git a/test/qxBlogModelView/src/model_view_from_qxee/author.model_view.gen.cpp b/test/qxBlogModelView/src/model_view_from_qxee/author.model_view.gen.cpp new file mode 100644 index 0000000..09b42c8 --- /dev/null +++ b/test/qxBlogModelView/src/model_view_from_qxee/author.model_view.gen.cpp @@ -0,0 +1,75 @@ +/************************************************************************************************ +** File created by QxEntityEditor 1.1.9 (2016/05/04 10:05) : please, do NOT modify this file ! ** +************************************************************************************************/ + +#include "../../include/precompiled.h" + +#include "../../include/model_view_from_qxee/author.model_view.gen.h" +#include "../../include/model_view_from_qxee/blog.model_view.gen.h" + +#include + +namespace model_view { + +author_model::author_model(QObject * parent /* = 0 */) : author_model_base_class(parent) { ; } + +author_model::author_model(qx::IxModel * other, QObject * parent) : author_model_base_class(other, parent) { ; } + +author_model::~author_model() { ; } + +QObject * author_model::list_of_blog(int row, bool bLoadFromDatabase /* = false */, const QString & sAppendRelations /* = QString() */) +{ + QString sRelation = "list_blog"; + qx::IxModel * pChild = (bLoadFromDatabase ? NULL : this->getChild(row, sRelation)); + if (pChild) { return static_cast(pChild); } + + if ((row < 0) || (row >= this->m_model.count())) { qAssert(false); return NULL; } + author_model_base_class::type_ptr ptr = this->m_model.getByIndex(row); + if (! ptr) { qAssert(false); return NULL; } + QString id = ptr->getauthor_id(); + author::type_list_of_blog value = ptr->getlist_of_blog(); + + if (bLoadFromDatabase) + { + if (! sAppendRelations.isEmpty() && ! sAppendRelations.startsWith("->") && ! sAppendRelations.startsWith(">>")) { sRelation += "->" + sAppendRelations; } + else if (! sAppendRelations.isEmpty()) { sRelation += sAppendRelations; } + author tmp; + tmp.setauthor_id(id); + this->m_lastError = qx::dao::fetch_by_id_with_relation(sRelation, tmp); + if (this->m_lastError.isValid()) { return NULL; } + value = tmp.getlist_of_blog(); + ptr->setlist_of_blog(value); + } + + model_view::blog_model * pNewChild = NULL; + pChild = qx::model_view::create_nested_model_with_type(this, QModelIndex(), value, pNewChild); + if (pChild) { this->insertChild(row, "list_blog", pChild); } + return static_cast(pChild); +} + +void author_model::syncNestedModel(int row, const QStringList & relation) +{ + Q_UNUSED(relation); + qx::IxModel * pNestedModel = NULL; + if ((row < 0) || (row >= this->m_model.count())) { return; } + author_model_base_class::type_ptr ptr = this->m_model.getByIndex(row); + if (! ptr) { return; } + + pNestedModel = this->getChild(row, "list_blog"); + if (pNestedModel) + { + this->syncNestedModelRecursive(pNestedModel, relation); + author::type_list_of_blog value; + qx::model_view::sync_nested_model(pNestedModel, value); + ptr->setlist_of_blog(value); + } +} + +void author_model::syncAllNestedModel(const QStringList & relation) +{ + if (this->m_lstChild.count() <= 0) { return; } + for (long l = 0; l < this->m_model.count(); l++) + { this->syncNestedModel(static_cast(l), relation); } +} + +} // namespace model_view diff --git a/test/qxBlogModelView/src/model_view_from_qxee/blog.model_view.gen.cpp b/test/qxBlogModelView/src/model_view_from_qxee/blog.model_view.gen.cpp new file mode 100644 index 0000000..314bbf2 --- /dev/null +++ b/test/qxBlogModelView/src/model_view_from_qxee/blog.model_view.gen.cpp @@ -0,0 +1,155 @@ +/************************************************************************************************ +** File created by QxEntityEditor 1.1.9 (2016/05/04 10:05) : please, do NOT modify this file ! ** +************************************************************************************************/ + +#include "../../include/precompiled.h" + +#include "../../include/model_view_from_qxee/blog.model_view.gen.h" +#include "../../include/model_view_from_qxee/author.model_view.gen.h" +#include "../../include/model_view_from_qxee/comment.model_view.gen.h" +#include "../../include/model_view_from_qxee/category.model_view.gen.h" + +#include + +namespace model_view { + +blog_model::blog_model(QObject * parent /* = 0 */) : blog_model_base_class(parent) { ; } + +blog_model::blog_model(qx::IxModel * other, QObject * parent) : blog_model_base_class(other, parent) { ; } + +blog_model::~blog_model() { ; } + +QObject * blog_model::author_id(int row, bool bLoadFromDatabase /* = false */, const QString & sAppendRelations /* = QString() */) +{ + QString sRelation = "author_id"; + qx::IxModel * pChild = (bLoadFromDatabase ? NULL : this->getChild(row, sRelation)); + if (pChild) { return static_cast(pChild); } + + if ((row < 0) || (row >= this->m_model.count())) { qAssert(false); return NULL; } + blog_model_base_class::type_ptr ptr = this->m_model.getByIndex(row); + if (! ptr) { qAssert(false); return NULL; } + long id = ptr->getblog_id(); + blog::type_author value = ptr->getauthor(); + + if (bLoadFromDatabase) + { + if (! sAppendRelations.isEmpty() && ! sAppendRelations.startsWith("->") && ! sAppendRelations.startsWith(">>")) { sRelation += "->" + sAppendRelations; } + else if (! sAppendRelations.isEmpty()) { sRelation += sAppendRelations; } + blog tmp; + tmp.setblog_id(id); + this->m_lastError = qx::dao::fetch_by_id_with_relation(sRelation, tmp); + if (this->m_lastError.isValid()) { return NULL; } + value = tmp.getauthor(); + ptr->setauthor(value); + } + + model_view::author_model * pNewChild = NULL; + pChild = qx::model_view::create_nested_model_with_type(this, QModelIndex(), value, pNewChild); + if (pChild) { this->insertChild(row, "author_id", pChild); } + return static_cast(pChild); +} + +QObject * blog_model::list_of_comment(int row, bool bLoadFromDatabase /* = false */, const QString & sAppendRelations /* = QString() */) +{ + QString sRelation = "list_comment"; + qx::IxModel * pChild = (bLoadFromDatabase ? NULL : this->getChild(row, sRelation)); + if (pChild) { return static_cast(pChild); } + + if ((row < 0) || (row >= this->m_model.count())) { qAssert(false); return NULL; } + blog_model_base_class::type_ptr ptr = this->m_model.getByIndex(row); + if (! ptr) { qAssert(false); return NULL; } + long id = ptr->getblog_id(); + blog::type_list_of_comment value = ptr->getlist_of_comment(); + + if (bLoadFromDatabase) + { + if (! sAppendRelations.isEmpty() && ! sAppendRelations.startsWith("->") && ! sAppendRelations.startsWith(">>")) { sRelation += "->" + sAppendRelations; } + else if (! sAppendRelations.isEmpty()) { sRelation += sAppendRelations; } + blog tmp; + tmp.setblog_id(id); + this->m_lastError = qx::dao::fetch_by_id_with_relation(sRelation, tmp); + if (this->m_lastError.isValid()) { return NULL; } + value = tmp.getlist_of_comment(); + ptr->setlist_of_comment(value); + } + + model_view::comment_model * pNewChild = NULL; + pChild = qx::model_view::create_nested_model_with_type(this, QModelIndex(), value, pNewChild); + if (pChild) { this->insertChild(row, "list_comment", pChild); } + return static_cast(pChild); +} + +QObject * blog_model::list_of_category(int row, bool bLoadFromDatabase /* = false */, const QString & sAppendRelations /* = QString() */) +{ + QString sRelation = "list_category"; + qx::IxModel * pChild = (bLoadFromDatabase ? NULL : this->getChild(row, sRelation)); + if (pChild) { return static_cast(pChild); } + + if ((row < 0) || (row >= this->m_model.count())) { qAssert(false); return NULL; } + blog_model_base_class::type_ptr ptr = this->m_model.getByIndex(row); + if (! ptr) { qAssert(false); return NULL; } + long id = ptr->getblog_id(); + blog::type_list_of_category value = ptr->getlist_of_category(); + + if (bLoadFromDatabase) + { + if (! sAppendRelations.isEmpty() && ! sAppendRelations.startsWith("->") && ! sAppendRelations.startsWith(">>")) { sRelation += "->" + sAppendRelations; } + else if (! sAppendRelations.isEmpty()) { sRelation += sAppendRelations; } + blog tmp; + tmp.setblog_id(id); + this->m_lastError = qx::dao::fetch_by_id_with_relation(sRelation, tmp); + if (this->m_lastError.isValid()) { return NULL; } + value = tmp.getlist_of_category(); + ptr->setlist_of_category(value); + } + + model_view::category_model * pNewChild = NULL; + pChild = qx::model_view::create_nested_model_with_type(this, QModelIndex(), value, pNewChild); + if (pChild) { this->insertChild(row, "list_category", pChild); } + return static_cast(pChild); +} + +void blog_model::syncNestedModel(int row, const QStringList & relation) +{ + Q_UNUSED(relation); + qx::IxModel * pNestedModel = NULL; + if ((row < 0) || (row >= this->m_model.count())) { return; } + blog_model_base_class::type_ptr ptr = this->m_model.getByIndex(row); + if (! ptr) { return; } + + pNestedModel = this->getChild(row, "author_id"); + if (pNestedModel) + { + this->syncNestedModelRecursive(pNestedModel, relation); + blog::type_author value; + qx::model_view::sync_nested_model(pNestedModel, value); + ptr->setauthor(value); + } + + pNestedModel = this->getChild(row, "list_comment"); + if (pNestedModel) + { + this->syncNestedModelRecursive(pNestedModel, relation); + blog::type_list_of_comment value; + qx::model_view::sync_nested_model(pNestedModel, value); + ptr->setlist_of_comment(value); + } + + pNestedModel = this->getChild(row, "list_category"); + if (pNestedModel) + { + this->syncNestedModelRecursive(pNestedModel, relation); + blog::type_list_of_category value; + qx::model_view::sync_nested_model(pNestedModel, value); + ptr->setlist_of_category(value); + } +} + +void blog_model::syncAllNestedModel(const QStringList & relation) +{ + if (this->m_lstChild.count() <= 0) { return; } + for (long l = 0; l < this->m_model.count(); l++) + { this->syncNestedModel(static_cast(l), relation); } +} + +} // namespace model_view diff --git a/test/qxBlogModelView/src/model_view_from_qxee/category.model_view.gen.cpp b/test/qxBlogModelView/src/model_view_from_qxee/category.model_view.gen.cpp new file mode 100644 index 0000000..bab816a --- /dev/null +++ b/test/qxBlogModelView/src/model_view_from_qxee/category.model_view.gen.cpp @@ -0,0 +1,75 @@ +/************************************************************************************************ +** File created by QxEntityEditor 1.1.9 (2016/05/04 10:05) : please, do NOT modify this file ! ** +************************************************************************************************/ + +#include "../../include/precompiled.h" + +#include "../../include/model_view_from_qxee/category.model_view.gen.h" +#include "../../include/model_view_from_qxee/blog.model_view.gen.h" + +#include + +namespace model_view { + +category_model::category_model(QObject * parent /* = 0 */) : category_model_base_class(parent) { ; } + +category_model::category_model(qx::IxModel * other, QObject * parent) : category_model_base_class(other, parent) { ; } + +category_model::~category_model() { ; } + +QObject * category_model::list_of_blog(int row, bool bLoadFromDatabase /* = false */, const QString & sAppendRelations /* = QString() */) +{ + QString sRelation = "list_blog"; + qx::IxModel * pChild = (bLoadFromDatabase ? NULL : this->getChild(row, sRelation)); + if (pChild) { return static_cast(pChild); } + + if ((row < 0) || (row >= this->m_model.count())) { qAssert(false); return NULL; } + category_model_base_class::type_ptr ptr = this->m_model.getByIndex(row); + if (! ptr) { qAssert(false); return NULL; } + long id = ptr->getcategory_id(); + category::type_list_of_blog value = ptr->getlist_of_blog(); + + if (bLoadFromDatabase) + { + if (! sAppendRelations.isEmpty() && ! sAppendRelations.startsWith("->") && ! sAppendRelations.startsWith(">>")) { sRelation += "->" + sAppendRelations; } + else if (! sAppendRelations.isEmpty()) { sRelation += sAppendRelations; } + category tmp; + tmp.setcategory_id(id); + this->m_lastError = qx::dao::fetch_by_id_with_relation(sRelation, tmp); + if (this->m_lastError.isValid()) { return NULL; } + value = tmp.getlist_of_blog(); + ptr->setlist_of_blog(value); + } + + model_view::blog_model * pNewChild = NULL; + pChild = qx::model_view::create_nested_model_with_type(this, QModelIndex(), value, pNewChild); + if (pChild) { this->insertChild(row, "list_blog", pChild); } + return static_cast(pChild); +} + +void category_model::syncNestedModel(int row, const QStringList & relation) +{ + Q_UNUSED(relation); + qx::IxModel * pNestedModel = NULL; + if ((row < 0) || (row >= this->m_model.count())) { return; } + category_model_base_class::type_ptr ptr = this->m_model.getByIndex(row); + if (! ptr) { return; } + + pNestedModel = this->getChild(row, "list_blog"); + if (pNestedModel) + { + this->syncNestedModelRecursive(pNestedModel, relation); + category::type_list_of_blog value; + qx::model_view::sync_nested_model(pNestedModel, value); + ptr->setlist_of_blog(value); + } +} + +void category_model::syncAllNestedModel(const QStringList & relation) +{ + if (this->m_lstChild.count() <= 0) { return; } + for (long l = 0; l < this->m_model.count(); l++) + { this->syncNestedModel(static_cast(l), relation); } +} + +} // namespace model_view diff --git a/test/qxBlogModelView/src/model_view_from_qxee/comment.model_view.gen.cpp b/test/qxBlogModelView/src/model_view_from_qxee/comment.model_view.gen.cpp new file mode 100644 index 0000000..941ae44 --- /dev/null +++ b/test/qxBlogModelView/src/model_view_from_qxee/comment.model_view.gen.cpp @@ -0,0 +1,75 @@ +/************************************************************************************************ +** File created by QxEntityEditor 1.1.9 (2016/05/04 10:05) : please, do NOT modify this file ! ** +************************************************************************************************/ + +#include "../../include/precompiled.h" + +#include "../../include/model_view_from_qxee/comment.model_view.gen.h" +#include "../../include/model_view_from_qxee/blog.model_view.gen.h" + +#include + +namespace model_view { + +comment_model::comment_model(QObject * parent /* = 0 */) : comment_model_base_class(parent) { ; } + +comment_model::comment_model(qx::IxModel * other, QObject * parent) : comment_model_base_class(other, parent) { ; } + +comment_model::~comment_model() { ; } + +QObject * comment_model::blog_id(int row, bool bLoadFromDatabase /* = false */, const QString & sAppendRelations /* = QString() */) +{ + QString sRelation = "blog_id"; + qx::IxModel * pChild = (bLoadFromDatabase ? NULL : this->getChild(row, sRelation)); + if (pChild) { return static_cast(pChild); } + + if ((row < 0) || (row >= this->m_model.count())) { qAssert(false); return NULL; } + comment_model_base_class::type_ptr ptr = this->m_model.getByIndex(row); + if (! ptr) { qAssert(false); return NULL; } + long id = ptr->getcomment_id(); + comment::type_blog_id value = ptr->getblog_id(); + + if (bLoadFromDatabase) + { + if (! sAppendRelations.isEmpty() && ! sAppendRelations.startsWith("->") && ! sAppendRelations.startsWith(">>")) { sRelation += "->" + sAppendRelations; } + else if (! sAppendRelations.isEmpty()) { sRelation += sAppendRelations; } + comment tmp; + tmp.setcomment_id(id); + this->m_lastError = qx::dao::fetch_by_id_with_relation(sRelation, tmp); + if (this->m_lastError.isValid()) { return NULL; } + value = tmp.getblog_id(); + ptr->setblog_id(value); + } + + model_view::blog_model * pNewChild = NULL; + pChild = qx::model_view::create_nested_model_with_type(this, QModelIndex(), value, pNewChild); + if (pChild) { this->insertChild(row, "blog_id", pChild); } + return static_cast(pChild); +} + +void comment_model::syncNestedModel(int row, const QStringList & relation) +{ + Q_UNUSED(relation); + qx::IxModel * pNestedModel = NULL; + if ((row < 0) || (row >= this->m_model.count())) { return; } + comment_model_base_class::type_ptr ptr = this->m_model.getByIndex(row); + if (! ptr) { return; } + + pNestedModel = this->getChild(row, "blog_id"); + if (pNestedModel) + { + this->syncNestedModelRecursive(pNestedModel, relation); + comment::type_blog_id value; + qx::model_view::sync_nested_model(pNestedModel, value); + ptr->setblog_id(value); + } +} + +void comment_model::syncAllNestedModel(const QStringList & relation) +{ + if (this->m_lstChild.count() <= 0) { return; } + for (long l = 0; l < this->m_model.count(); l++) + { this->syncNestedModel(static_cast(l), relation); } +} + +} // namespace model_view diff --git a/test/qxBlogMongoDB/CMakeLists.txt b/test/qxBlogMongoDB/CMakeLists.txt new file mode 100644 index 0000000..e963565 --- /dev/null +++ b/test/qxBlogMongoDB/CMakeLists.txt @@ -0,0 +1,66 @@ +cmake_minimum_required(VERSION 3.1) + +project(qxBlogMongoDB LANGUAGES CXX) + +include(../../QxOrm.cmake) + +if(_QX_ENABLE_MONGODB) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_DEBUG_POSTFIX "d") + +set(HEADERS + ./include/precompiled.h + ./include/export.h + ./include/author.h + ./include/blog.h + ./include/category.h + ./include/comment.h + ./include/TestQtProperty.h + ) + +set(SRCS + ./src/author.cpp + ./src/blog.cpp + ./src/category.cpp + ./src/comment.cpp + ./src/TestQtProperty.cpp + ./src/main.cpp + ) + +add_executable(qxBlogMongoDB ${SRCS} ${HEADERS}) + +target_compile_definitions(qxBlogMongoDB PRIVATE -D_BUILDING_QX_BLOG) + +if(COMMAND target_precompile_headers) + target_precompile_headers(qxBlogMongoDB PRIVATE ./include/precompiled.h) +endif() # (COMMAND target_precompile_headers) + +target_link_libraries(qxBlogMongoDB ${QX_LIBRARIES} QxOrm) + +set_target_properties(qxBlogMongoDB PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ) + +set_target_properties(qxBlogMongoDB PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) + +else() # _QX_ENABLE_MONGODB + +message(STATUS "qxBlogMongoDB project not loaded because _QX_ENABLE_MONGODB option not enabled (QxOrm MongoDB database driver is not available)") + +endif() # _QX_ENABLE_MONGODB diff --git a/test/qxBlogMongoDB/debug/.gitignore b/test/qxBlogMongoDB/debug/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogMongoDB/debug/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogMongoDB/include/TestQtProperty.h b/test/qxBlogMongoDB/include/TestQtProperty.h new file mode 100644 index 0000000..b1a5239 --- /dev/null +++ b/test/qxBlogMongoDB/include/TestQtProperty.h @@ -0,0 +1,55 @@ +#ifndef _QX_TEST_QT_META_PROPERTY_H_ +#define _QX_TEST_QT_META_PROPERTY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include "../include/precompiled.h" // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +class QX_BLOG_DLL_EXPORT TestQtProperty : public QObject, public qx::IxPersistable +{ + + Q_OBJECT + QX_PERSISTABLE_HPP(TestQtProperty) + Q_PROPERTY(QString id READ id WRITE setId) + Q_PROPERTY(long number READ number WRITE setNumber) + Q_PROPERTY(QString desc READ desc WRITE setDesc) + Q_PROPERTY(QDateTime birthDate READ birthDate WRITE setBirthDate) + Q_PROPERTY(QVariant photo READ photo WRITE setPhoto) + +protected: + + QString m_id; + long m_number; + QString m_desc; + QDateTime m_birthDate; + QVariant m_photo; + +public: + + TestQtProperty() : QObject(), m_number(0) { ; } + virtual ~TestQtProperty() { ; } + + QString id() const { return m_id; } + long number() const { return m_number; } + QString desc() const { return m_desc; } + QDateTime birthDate() const { return m_birthDate; } + QVariant photo() const { return m_photo; } + + void setId(const QString & id) { m_id = id; } + void setNumber(long l) { m_number = l; } + void setDesc(const QString & s) { m_desc = s; } + void setBirthDate(const QDateTime & dt) { m_birthDate = dt; } + void setPhoto(const QVariant & v) { m_photo = v; } + +}; + +QX_REGISTER_PRIMARY_KEY(TestQtProperty, QString) +QX_REGISTER_HPP_QX_BLOG(TestQtProperty, QObject, 0) + +#endif // _QX_TEST_QT_META_PROPERTY_H_ diff --git a/test/qxBlogMongoDB/include/author.h b/test/qxBlogMongoDB/include/author.h new file mode 100644 index 0000000..71aca1a --- /dev/null +++ b/test/qxBlogMongoDB/include/author.h @@ -0,0 +1,33 @@ +#ifndef _QX_BLOG_AUTHOR_H_ +#define _QX_BLOG_AUTHOR_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT author +{ +public: +// -- typedef + typedef std::shared_ptr blog_ptr; + typedef std::vector list_blog; +// -- enum + enum enum_sex { male, female, unknown }; +// -- properties + QString m_id; + QString m_name; + QDate m_birthdate; + enum_sex m_sex; + list_blog m_blogX; +// -- contructor, virtual destructor + author() : m_sex(unknown) { ; } + virtual ~author() { ; } +// -- methods + int age() const; +}; + +QX_REGISTER_PRIMARY_KEY(author, QString) +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr author_ptr; +typedef qx::QxCollection list_author; + +#endif // _QX_BLOG_AUTHOR_H_ diff --git a/test/qxBlogMongoDB/include/blog.h b/test/qxBlogMongoDB/include/blog.h new file mode 100644 index 0000000..5327a68 --- /dev/null +++ b/test/qxBlogMongoDB/include/blog.h @@ -0,0 +1,29 @@ +#ifndef _QX_BLOG_BLOG_H_ +#define _QX_BLOG_BLOG_H_ + +#include "author.h" +#include "comment.h" +#include "category.h" + +class QX_BLOG_DLL_EXPORT blog +{ +public: +// -- properties + QString m_id; + QString m_text; + QDateTime m_dt_creation; + author_ptr m_author; + list_comment m_commentX; + list_category m_categoryX; +// -- contructor, virtual destructor + blog() { ; } + virtual ~blog() { ; } +}; + +QX_REGISTER_PRIMARY_KEY(blog, QString) +QX_REGISTER_HPP_QX_BLOG(blog, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr blog_ptr; +typedef std::vector list_blog; + +#endif // _QX_BLOG_BLOG_H_ diff --git a/test/qxBlogMongoDB/include/category.h b/test/qxBlogMongoDB/include/category.h new file mode 100644 index 0000000..33362c9 --- /dev/null +++ b/test/qxBlogMongoDB/include/category.h @@ -0,0 +1,28 @@ +#ifndef _QX_BLOG_CATEGORY_H_ +#define _QX_BLOG_CATEGORY_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT category +{ +public: +// -- typedef + typedef std::shared_ptr blog_ptr; + typedef qx::QxCollection list_blog; +// -- properties + QString m_id; + QString m_name; + QString m_desc; + list_blog m_blogX; +// -- contructor, virtual destructor + category() { ; } + virtual ~category() { ; } +}; + +QX_REGISTER_PRIMARY_KEY(category, QString) +QX_REGISTER_HPP_QX_BLOG(category, qx::trait::no_base_class_defined, 0) + +typedef QSharedPointer category_ptr; +typedef qx::QxCollection list_category; + +#endif // _QX_BLOG_CATEGORY_H_ diff --git a/test/qxBlogMongoDB/include/comment.h b/test/qxBlogMongoDB/include/comment.h new file mode 100644 index 0000000..78d65d5 --- /dev/null +++ b/test/qxBlogMongoDB/include/comment.h @@ -0,0 +1,27 @@ +#ifndef _QX_BLOG_COMMENT_H_ +#define _QX_BLOG_COMMENT_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT comment +{ +public: +// -- typedef + typedef std::shared_ptr blog_ptr; +// -- properties + QString m_id; + QString m_text; + QDateTime m_dt_create; + blog_ptr m_blog; +// -- contructor, virtual destructor + comment() { ; } + virtual ~comment() { ; } +}; + +QX_REGISTER_PRIMARY_KEY(comment, QString) +QX_REGISTER_HPP_QX_BLOG(comment, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr comment_ptr; +typedef QList list_comment; + +#endif // _QX_BLOG_COMMENT_H_ diff --git a/test/qxBlogMongoDB/include/export.h b/test/qxBlogMongoDB/include/export.h new file mode 100644 index 0000000..b1c11df --- /dev/null +++ b/test/qxBlogMongoDB/include/export.h @@ -0,0 +1,18 @@ +#ifndef _QX_BLOG_EXPORT_H_ +#define _QX_BLOG_EXPORT_H_ + +#ifdef _BUILDING_QX_BLOG +#define QX_BLOG_DLL_EXPORT QX_DLL_EXPORT_HELPER +#else // _BUILDING_QX_BLOG +#define QX_BLOG_DLL_EXPORT QX_DLL_IMPORT_HELPER +#endif // _BUILDING_QX_BLOG + +#ifdef _BUILDING_QX_BLOG +#define QX_REGISTER_HPP_QX_BLOG QX_REGISTER_HPP_EXPORT_DLL +#define QX_REGISTER_CPP_QX_BLOG QX_REGISTER_CPP_EXPORT_DLL +#else // _BUILDING_QX_BLOG +#define QX_REGISTER_HPP_QX_BLOG QX_REGISTER_HPP_IMPORT_DLL +#define QX_REGISTER_CPP_QX_BLOG QX_REGISTER_CPP_IMPORT_DLL +#endif // _BUILDING_QX_BLOG + +#endif // _QX_BLOG_EXPORT_H_ diff --git a/test/qxBlogMongoDB/include/precompiled.h b/test/qxBlogMongoDB/include/precompiled.h new file mode 100644 index 0000000..0d8b994 --- /dev/null +++ b/test/qxBlogMongoDB/include/precompiled.h @@ -0,0 +1,8 @@ +#ifndef _QX_BLOG_PRECOMPILED_HEADER_H_ +#define _QX_BLOG_PRECOMPILED_HEADER_H_ + +#include + +#include "export.h" + +#endif // _QX_BLOG_PRECOMPILED_HEADER_H_ diff --git a/test/qxBlogMongoDB/qt/moc/.gitignore b/test/qxBlogMongoDB/qt/moc/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogMongoDB/qt/moc/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogMongoDB/qxBlog.pro b/test/qxBlogMongoDB/qxBlog.pro new file mode 100644 index 0000000..8ec72c9 --- /dev/null +++ b/test/qxBlogMongoDB/qxBlog.pro @@ -0,0 +1,40 @@ +include(../../QxOrm.pri) + +!contains(DEFINES, _QX_ENABLE_MONGODB) { +error(unable to use QxOrm MongoDB database driver : please define _QX_ENABLE_MONGODB compilation option in QxOrm.pri configuration file) +} # !contains(DEFINES, _QX_ENABLE_MONGODB) + +TEMPLATE = app +DEFINES += _BUILDING_QX_BLOG +INCLUDEPATH += ../../../QxOrm/include/ +DESTDIR = ../../../QxOrm/test/_bin/ +LIBS += -L"../../../QxOrm/lib" + +!contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) { +PRECOMPILED_HEADER = ./include/precompiled.h +} # !contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) + +macx:CONFIG-=app_bundle + +CONFIG(debug, debug|release) { +TARGET = qxBlogMongoDBd +LIBS += -l"QxOrmd" +} else { +TARGET = qxBlogMongoDB +LIBS += -l"QxOrm" +} # CONFIG(debug, debug|release) + +HEADERS += ./include/precompiled.h +HEADERS += ./include/export.h +HEADERS += ./include/author.h +HEADERS += ./include/blog.h +HEADERS += ./include/category.h +HEADERS += ./include/comment.h +HEADERS += ./include/TestQtProperty.h + +SOURCES += ./src/author.cpp +SOURCES += ./src/blog.cpp +SOURCES += ./src/category.cpp +SOURCES += ./src/comment.cpp +SOURCES += ./src/TestQtProperty.cpp +SOURCES += ./src/main.cpp diff --git a/test/qxBlogMongoDB/release/.gitignore b/test/qxBlogMongoDB/release/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogMongoDB/release/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogMongoDB/src/TestQtProperty.cpp b/test/qxBlogMongoDB/src/TestQtProperty.cpp new file mode 100644 index 0000000..6f9866d --- /dev/null +++ b/test/qxBlogMongoDB/src/TestQtProperty.cpp @@ -0,0 +1,25 @@ +#include "../include/precompiled.h" + +#include "../include/TestQtProperty.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(TestQtProperty) +QX_REGISTER_ALL_QT_PROPERTIES(TestQtProperty, "id") +QX_PERSISTABLE_CPP(TestQtProperty) + +/* + Instead of using 'QX_REGISTER_ALL_QT_PROPERTIES(...)' macro, it's also + possible to write classic 'void qx::register_class(...)' function, like this : + + namespace qx { + template <> void register_class(QxClass & t) + { qx::register_all_qt_properties(t, "my_id"); } + } // namespace qx + + So, you can mix Qt meta-properties and classic registration data-member. + All is stored using the same interface : 'qx::IxDataMember *'. + To more details about advantages and disadvantages of using Qt meta-property, + go to the FAQ of QxOrm library : + - How to register automatically Qt meta-properties to QxOrm context ? +*/ diff --git a/test/qxBlogMongoDB/src/author.cpp b/test/qxBlogMongoDB/src/author.cpp new file mode 100644 index 0000000..e0813a2 --- /dev/null +++ b/test/qxBlogMongoDB/src/author.cpp @@ -0,0 +1,28 @@ +#include "../include/precompiled.h" + +#include "../include/author.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(author) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& author::m_id, "author_id"); + + t.data(& author::m_name, "name"); + t.data(& author::m_birthdate, "birthdate")->setIsIndex(true); // Index created automatically with qx::dao::mongodb::QxMongoDB_Helper::autoCreateIndexes() function + t.data(& author::m_sex, "sex"); + + t.relationOneToMany(& author::m_blogX, "list_blog", "author_id"); + + t.fct_0(std::mem_fn(& author::age), "age"); // using std::mem_fn() here is just a workaround for an issue with some versions of MSVC, it is not required with a full compliant C++11 compiler (http://stackoverflow.com/questions/23778883/vs2013-stdfunction-with-member-function) +}} + +int author::age() const +{ + if (! m_birthdate.isValid()) { return -1; } + return (QDate::currentDate().year() - m_birthdate.year()); +} diff --git a/test/qxBlogMongoDB/src/blog.cpp b/test/qxBlogMongoDB/src/blog.cpp new file mode 100644 index 0000000..7322f40 --- /dev/null +++ b/test/qxBlogMongoDB/src/blog.cpp @@ -0,0 +1,20 @@ +#include "../include/precompiled.h" + +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(blog) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& blog::m_id, "blog_id"); + + t.data(& blog::m_text, "blog_text"); + t.data(& blog::m_dt_creation, "date_creation"); + t.data(& blog::m_categoryX, "list_category"); + + t.relationManyToOne(& blog::m_author, "author_id"); + t.relationOneToMany(& blog::m_commentX, "list_comment", "blog_id"); +}} diff --git a/test/qxBlogMongoDB/src/category.cpp b/test/qxBlogMongoDB/src/category.cpp new file mode 100644 index 0000000..723fda5 --- /dev/null +++ b/test/qxBlogMongoDB/src/category.cpp @@ -0,0 +1,19 @@ +#include "../include/precompiled.h" + +#include "../include/category.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(category) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& category::m_id, "category_id"); + + t.data(& category::m_name, "name"); + t.data(& category::m_desc, "description"); + + t.relationManyToMany(& category::m_blogX, "list_blog", "category_blog", "category_id", "blog_id"); +}} diff --git a/test/qxBlogMongoDB/src/comment.cpp b/test/qxBlogMongoDB/src/comment.cpp new file mode 100644 index 0000000..d2d5cd5 --- /dev/null +++ b/test/qxBlogMongoDB/src/comment.cpp @@ -0,0 +1,19 @@ +#include "../include/precompiled.h" + +#include "../include/comment.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(comment) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& comment::m_id, "comment_id"); + + t.data(& comment::m_text, "comment_text"); + t.data(& comment::m_dt_create, "date_creation"); + + t.relationManyToOne(& comment::m_blog, "blog_id"); +}} diff --git a/test/qxBlogMongoDB/src/main.cpp b/test/qxBlogMongoDB/src/main.cpp new file mode 100644 index 0000000..9e6e8eb --- /dev/null +++ b/test/qxBlogMongoDB/src/main.cpp @@ -0,0 +1,480 @@ +#include "../include/precompiled.h" + +#include + +#include "../include/blog.h" +#include "../include/author.h" +#include "../include/comment.h" +#include "../include/category.h" +#include "../include/TestQtProperty.h" + +#include + +void basicCRUDOnAuthor(bool withDelete); +void basicCRUDOnCategory(); +void basicCRUDOnBlog(); +void complexQueriesOnBlog(); +void miscellaneousOperations(); +void testQtProperty(); + +int main(int argc, char * argv[]) +{ + // Qt application + QCoreApplication app(argc, argv); + + // Following command is recommanded to initialize QxOrm introspection engine + qx::QxClassX::registerAllClasses(); + + // Parameters to connect to MongoDB database + qx::QxSqlDatabase * pDatabase = qx::QxSqlDatabase::getSingleton(); + pDatabase->setDriverName("QXMONGODB"); + pDatabase->setDatabaseName("qxBlog"); + pDatabase->setHostName("localhost"); + pDatabase->setPort(27017); + pDatabase->setUserName(""); + pDatabase->setPassword(""); + + // For debug purpose : log all replies from MongoDB database + qx::dao::mongodb::QxMongoDB_Helper::setLogDatabaseReply(true); + + // Clear previous sample database + qx_query dropDB("{ \"dropDatabase\" : 1 }"); + QSqlError daoError = qx::dao::call_query(dropDB); qAssert(! daoError.isValid()); + + // To optimize queries : create automatically indexes based on relationships and properties marked as 'index' + daoError = qx::dao::mongodb::QxMongoDB_Helper::autoCreateIndexes(true); qAssert(! daoError.isValid()); + + // Basic CRUD operations + basicCRUDOnAuthor(true); + basicCRUDOnAuthor(false); + + // Populate category collection + basicCRUDOnCategory(); + + // Populate blog collection + basicCRUDOnBlog(); + + // Process some complex queries on blog collection (with relationships) + complexQueriesOnBlog(); + + // Process some others operations on database + miscellaneousOperations(); + + // Test Qt properties registered via QX_REGISTER_ALL_QT_PROPERTIES() macro (https://www.qxorm.com/qxorm_en/manual.html#manual_520) + testQtProperty(); + + return 0; +} + +void basicCRUDOnAuthor(bool withDelete) +{ + // Test delete all author + QSqlError daoError = qx::dao::delete_all(); qAssert(! daoError.isValid()); + qAssert(qx::dao::count() == 0); + + // Test insert one without id + author_ptr author_1 = std::make_shared(); + author_1->m_name = "author_1"; + author_1->m_sex = author::male; + author_1->m_birthdate = QDate(1998, 07, 12); + daoError = qx::dao::insert(author_1); qAssert(! daoError.isValid()); + qAssert(! author_1->m_id.isEmpty()); + qAssert(qx::dao::count() == 1); + + // Test insert one author with a custom id + author_ptr author_2 = std::make_shared(); + author_2->m_id = "my_custom_id_author_2"; + author_2->m_name = "author_2"; + author_2->m_sex = author::female; + author_2->m_birthdate = QDate(2003, 02, 28); + daoError = qx::dao::insert(author_2); qAssert(! daoError.isValid()); + qAssert(author_2->m_id == "my_custom_id_author_2"); + qAssert(qx::dao::count() == 2); + + // Test insert many author with/without ids + QList authorX; + author author_3; author_3.m_name = "author_3"; author_3.m_sex = author::female; author_3.m_birthdate = QDate(1968, 05, 01); authorX.append(author_3); + author author_4; author_4.m_id = "my_custom_id_author_4"; author_4.m_name = "author_4"; author_4.m_sex = author::male; author_4.m_birthdate = QDate(1980, 12, 19); authorX.append(author_4); + author author_5; author_5.m_name = "author_5"; author_5.m_sex = author::female; author_5.m_birthdate = QDate(1978, 03, 03); authorX.append(author_5); + daoError = qx::dao::insert(authorX); qAssert(! daoError.isValid()); + qAssert(qx::dao::count() == 5); + qAssert(authorX.at(1).m_id == "my_custom_id_author_4"); + QString author_3_id = authorX.at(0).m_id; qAssert(! author_3_id.isEmpty()); + QString author_5_id = authorX.at(2).m_id; qAssert(! author_5_id.isEmpty()); + + // Test update one author + author_3 = authorX.at(0); + author_3.m_name = "author_3_modified"; + daoError = qx::dao::update(author_3); qAssert(! daoError.isValid()); + + // Test update many author + authorX.clear(); + author_3.m_name = "author_3_modified_twice"; authorX.append(author_3); + author_2->m_name = "author_2_modified"; authorX.append(* author_2); + author_1->m_name = "author_1_modified"; authorX.append(* author_1); + daoError = qx::dao::update(authorX); qAssert(! daoError.isValid()); + qAssert(qx::dao::count() == 5); + + // Test fetch all author (inside QList) + authorX.clear(); + daoError = qx::dao::fetch_all(authorX); qAssert(! daoError.isValid()); + qAssert(authorX.count() == 5); + qx::dump(authorX); + + // Test fetch all author (inside QHash by id) + QHash authorXById; + daoError = qx::dao::fetch_all(authorXById); qAssert(! daoError.isValid()); + qAssert(authorXById.count() == 5); + qx::dump(authorXById); + + // Test fetch author by query (only female) + list_author list_of_female_author; + qx_query query("{ \"sex\" : " + QString::number(static_cast(author::female)) + " }"); + daoError = qx::dao::fetch_by_query(query, list_of_female_author); qAssert(! daoError.isValid()); + qAssert(list_of_female_author.count() == 3); + qx::dump(list_of_female_author); + + // Test fetch by query using MongoDB aggregation framework (only female) + list_author list_of_female_author_other; + qx_query queryAggregate("aggregate", "[ { \"$match\" : { \"sex\" : " + QString::number(static_cast(author::female)) + " } } ]"); + daoError = qx::dao::fetch_by_query(queryAggregate, list_of_female_author_other); qAssert(! daoError.isValid()); + qAssert(list_of_female_author_other.count() == 3); + qx::dump(list_of_female_author_other); + + // Test fetch by query (only female) adding 'sort', 'limit', 'skip', etc... commands (see second query QStringList parameter) + list_of_female_author_other.clear(); + qx_query queryOpts(QStringList() << "{ \"sex\" : " + QString::number(static_cast(author::female)) + " }" + << "{ \"sort\" : { \"sex\" : -1 }, \"limit\" : 2 }"); + daoError = qx::dao::fetch_by_query(queryOpts, list_of_female_author_other); qAssert(! daoError.isValid()); + qAssert(list_of_female_author_other.count() == 2); + qx::dump(list_of_female_author_other); + + // Test update one author after fetch + author_5 = authorX.at(4); + author_5.m_name = "author_5_modified_after_fetch"; + daoError = qx::dao::update(author_5); qAssert(! daoError.isValid()); + qAssert(qx::dao::count() == 5); + + // Test update many author after fetch + author_ptr pAuthor = list_of_female_author.getByIndex(0); + pAuthor->m_name = pAuthor->m_name + "_female_modified_after_fetch"; + pAuthor = list_of_female_author.getByIndex(1); + pAuthor->m_name = pAuthor->m_name + "_female_modified_after_fetch"; + pAuthor = list_of_female_author.getByIndex(2); + pAuthor->m_name = pAuthor->m_name + "_female_modified_after_fetch"; + daoError = qx::dao::update(list_of_female_author); qAssert(! daoError.isValid()); + qAssert(qx::dao::count() == 5); + + // Test fetch one author by id + pAuthor.reset(new author()); + pAuthor->m_id = "my_custom_id_author_2"; + daoError = qx::dao::fetch_by_id(pAuthor); qAssert(! daoError.isValid()); + qAssert(pAuthor->m_id == "my_custom_id_author_2"); + qAssert(! pAuthor->m_birthdate.isNull()); + + // Test fetch many author by id + authorX.clear(); + QHashIterator itr(authorXById); + while (itr.hasNext()) { itr.next(); author author_tmp; author_tmp.m_id = itr.key(); authorX.append(author_tmp); } + daoError = qx::dao::fetch_by_id(authorX); qAssert(! daoError.isValid()); + qAssert(authorX.count() == 5); + qAssert(! authorX.at(2).m_birthdate.isNull()); + qx::dump(authorX); + + if (! withDelete) { return; } + + // Test delete one author by id + pAuthor.reset(new author()); + pAuthor->m_id = "my_custom_id_author_4"; + daoError = qx::dao::delete_by_id(pAuthor); qAssert(! daoError.isValid()); + qAssert(qx::dao::count() == 4); + + // Test delete many author by id + authorX.clear(); + { author author_tmp; author_tmp.m_id = author_3_id; authorX.append(author_tmp); } + { author author_tmp; author_tmp.m_id = author_5_id; authorX.append(author_tmp); } + daoError = qx::dao::delete_by_id(authorX); qAssert(! daoError.isValid()); + qAssert(qx::dao::count() == 2); + + // Test delete author by query (all male) +#ifdef Q_COMPILER_INITIALIZER_LISTS + int iMale = static_cast(author::male); + query = qx_query{ { "sex", iMale } }; +#else // Q_COMPILER_INITIALIZER_LISTS + query = qx_query("{ \"sex\" : " + QString::number(static_cast(author::male)) + " }"); +#endif // Q_COMPILER_INITIALIZER_LISTS + daoError = qx::dao::delete_by_query(query); qAssert(! daoError.isValid()); + qAssert(qx::dao::count() == 1); + + // Now delete all author + daoError = qx::dao::delete_all(); qAssert(! daoError.isValid()); + qAssert(qx::dao::count() == 0); +} + +void basicCRUDOnCategory() +{ + // Create 3 categories + category_ptr category_1 = category_ptr(new category()); + category_ptr category_2 = category_ptr(new category()); + category_ptr category_3 = category_ptr(new category()); + + category_1->m_name = "category_1"; category_1->m_desc = "desc_1"; + category_2->m_name = "category_2"; category_2->m_desc = "desc_2"; + category_3->m_name = "category_3"; category_3->m_desc = "desc_3"; + + // Insert 3 categories into database, use 'db' parameter for the transaction + QSqlError daoError = qx::dao::insert(category_1); qAssert(! daoError.isValid()); + daoError = qx::dao::insert(category_2); qAssert(! daoError.isValid()); + daoError = qx::dao::insert(category_3); qAssert(! daoError.isValid()); + qAssert(qx::dao::count() == 3); +} + +void basicCRUDOnBlog() +{ + // Search author_1 + author_ptr author_1 = std::make_shared(); + qx_query query("{ \"name\" : \"author_1_modified\" }"); + QSqlError daoError = qx::dao::fetch_by_query(query, (* author_1)); qAssert(! daoError.isValid()); + qAssert(author_1 && ! author_1->m_id.isEmpty()); + + // Search author_2 + author_ptr author_2 = std::make_shared(); + author_2->m_id = "my_custom_id_author_2"; + daoError = qx::dao::fetch_by_id(author_2); qAssert(! daoError.isValid()); + qAssert(author_2 && ! author_2->m_id.isEmpty()); + + // Create a blog from class name (factory) + qx::any blog_any = qx::create("blog"); + blog_ptr blog_1; + try { blog_1 = qx::any_cast(blog_any); } + catch (...) { blog_1.reset(new blog()); } + blog_1->m_text = "blog_text_1"; + blog_1->m_dt_creation = QDateTime::currentDateTime(); + blog_1->m_author = author_1; + + // Insert 'blog_1' into database with 'save()' method + daoError = qx::dao::save(blog_1); qAssert(! daoError.isValid()); + qAssert(qx::dao::count() == 1); + + // Modify 'blog_1' properties and save into database + blog_1->m_text = "update blog_text_1"; + blog_1->m_author = author_2; + daoError = qx::dao::save(blog_1); qAssert(! daoError.isValid()); + qAssert(qx::dao::count() == 1); + + // Add 2 comments to 'blog_1' + comment_ptr comment_1; comment_1.reset(new comment()); + comment_ptr comment_2; comment_2.reset(new comment()); + + comment_1->m_text = "comment_1 text"; + comment_1->m_dt_create = QDateTime::currentDateTime(); + comment_1->m_blog = blog_1; + comment_2->m_text = "comment_2 text"; + comment_2->m_dt_create = QDateTime::currentDateTime(); + comment_2->m_blog = blog_1; + + daoError = qx::dao::insert(comment_1); qAssert(! daoError.isValid()); + daoError = qx::dao::insert(comment_2); qAssert(! daoError.isValid()); + qAssert(qx::dao::count() == 2); + + // Add 2 categories to 'blog_1' => 'list_category' is not defined as relationship in 'blog' class => so all categories are embedded in 'blog' collection (instead of referenced for relationships) + list_category categories; + daoError = qx::dao::fetch_all(categories); qAssert(! daoError.isValid()); + qAssert(categories.count() == 3); + + category_ptr category_1 = categories.getByIndex(0); + category_ptr category_3 = categories.getByIndex(2); + blog_1->m_categoryX.insert(category_1->m_id, category_1); + blog_1->m_categoryX.insert(category_3->m_id, category_3); + daoError = qx::dao::save(blog_1); qAssert(! daoError.isValid()); + qAssert(qx::dao::count() == 1); + + // Push a second blog + blog_ptr blog_2 = std::make_shared(); + blog_2->m_text = "blog_text_2"; + daoError = qx::dao::save(blog_2); qAssert(! daoError.isValid()); +} + +void complexQueriesOnBlog() +{ + // Fetch all blogs (without relation) + list_blog allBlogs; + QSqlError daoError = qx::dao::fetch_all(allBlogs); qAssert(! daoError.isValid()); + qx::dump(allBlogs); + qAssert(allBlogs.size() == 2); + + // Fetch blog into a new variable with all relations : 'author', 'comment' and 'category' (MongoDB version 3.6+ is required for relationships) + blog_ptr blog_tmp = std::make_shared(); + blog_tmp->m_id = allBlogs[0]->m_id; + daoError = qx::dao::fetch_by_id_with_all_relation(blog_tmp); qAssert(! daoError.isValid()); + qx::dump(blog_tmp); + qAssert(blog_tmp->m_commentX.count() == 2); + qAssert(blog_tmp->m_categoryX.count() == 2); + qAssert(blog_tmp->m_text == "update blog_text_1"); + qAssert(blog_tmp->m_author && (blog_tmp->m_author->m_id == "my_custom_id_author_2")); + + // Fetch blog into a new variable with many relations using "*->*->*->*" (4 levels of relationships) + blog_tmp.reset(new blog()); + blog_tmp->m_id = allBlogs[0]->m_id; + daoError = qx::dao::fetch_by_id_with_relation("*->*->*->*", blog_tmp); qAssert(! daoError.isValid()); + qx::dump(blog_tmp); + qAssert(blog_tmp->m_commentX.count() == 2); + qAssert(blog_tmp->m_categoryX.count() == 2); + qAssert(blog_tmp->m_text == "update blog_text_1"); + qAssert(blog_tmp->m_author && blog_tmp->m_author->m_id == "my_custom_id_author_2"); + qAssert(blog_tmp->m_author->m_blogX[0]->m_commentX.count() > 0); + + // Fetch blog into a new variable with many relations using "*->*" (2 levels of relationships) + blog_tmp.reset(new blog()); + blog_tmp->m_id = allBlogs[0]->m_id; + daoError = qx::dao::fetch_all_with_relation("*->*", blog_tmp); qAssert(! daoError.isValid()); + qx::dump(blog_tmp); + qAssert(blog_tmp->m_commentX.count() == 2); + qAssert(blog_tmp->m_categoryX.count() == 2); + qAssert(blog_tmp->m_text == "update blog_text_1"); + qAssert(blog_tmp->m_author && blog_tmp->m_author->m_id == "my_custom_id_author_2"); + qAssert(blog_tmp->m_author->m_blogX[0]->m_commentX.count() == 0); + + // Fetch only property 'm_dt_creation' of blog + QStringList lstColumns = QStringList() << "date_creation"; + list_blog lst_blog_with_only_date_creation; + daoError = qx::dao::fetch_all(lst_blog_with_only_date_creation, NULL, lstColumns); + qAssert(! daoError.isValid() && (lst_blog_with_only_date_creation.size() > 0)); + if ((lst_blog_with_only_date_creation.size() > 0) && (lst_blog_with_only_date_creation[0].get() != NULL)) + { qAssert(lst_blog_with_only_date_creation[0]->m_text.isEmpty()); } + qx::dump(lst_blog_with_only_date_creation); + + // Fetch relations defining fields to fetch with syntax { col_1, col_2, etc... } + list_blog lstBlogComplexRelation; + daoError = qx::dao::fetch_all_with_relation(QStringList() << "{ blog_text }" << "author_id { name, birthdate }" << "list_comment { comment_text } -> blog_id -> *", lstBlogComplexRelation); qAssert(! daoError.isValid()); + qx::dump(lstBlogComplexRelation); + qAssert(lstBlogComplexRelation.size() > 0); + qAssert(lstBlogComplexRelation[0]->m_text != ""); // Fetched + qAssert(lstBlogComplexRelation[0]->m_dt_creation.isNull()); // Not fetched + qAssert(lstBlogComplexRelation[0]->m_author->m_sex == author::unknown); // Not fetched + qAssert(lstBlogComplexRelation[0]->m_author->m_name != ""); // Fetched + qAssert(lstBlogComplexRelation[0]->m_commentX.size() > 0); + qAssert(lstBlogComplexRelation[0]->m_commentX[0]->m_dt_create.isNull()); // Not fetched + qAssert(lstBlogComplexRelation[0]->m_commentX[0]->m_text != ""); // Fetched + qAssert(lstBlogComplexRelation[0]->m_commentX[0]->m_blog); + + // Fetch relations defining columns to remove before fetching with syntax -{ col_1, col_2, etc... } + list_blog lstBlogComplexRelation2; + daoError = qx::dao::fetch_all_with_relation(QStringList() << "- { blog_text }" << "author_id -{ name, birthdate }" << "list_comment -{ comment_text } -> blog_id -> *", lstBlogComplexRelation2); qAssert(! daoError.isValid()); + qx::dump(lstBlogComplexRelation2); + qAssert(lstBlogComplexRelation2.size() > 0); + qAssert(lstBlogComplexRelation2[0]->m_text == ""); // Not fetched + qAssert(! lstBlogComplexRelation2[0]->m_dt_creation.isNull()); // Fetched + qAssert(lstBlogComplexRelation2[0]->m_author->m_sex != author::unknown); // Fetched + qAssert(lstBlogComplexRelation2[0]->m_author->m_name == ""); // Not fetched + qAssert(lstBlogComplexRelation2[0]->m_commentX.size() > 0); + qAssert(! lstBlogComplexRelation2[0]->m_commentX[0]->m_dt_create.isNull()); // Fetched + qAssert(lstBlogComplexRelation2[0]->m_commentX[0]->m_text == ""); // Not fetched + qAssert(lstBlogComplexRelation2[0]->m_commentX[0]->m_blog); + + // Check qx::dao::save_with_relation_recursive() function + daoError = qx::dao::save_with_relation_recursive(blog_tmp); qAssert(! daoError.isValid()); + daoError = qx::dao::save_with_relation_recursive(blog_tmp, qx::dao::save_mode::e_update_only); qAssert(! daoError.isValid()); + + // Fetch the second blog stored in database + blog_tmp = std::make_shared(); + blog_tmp->m_id = allBlogs[1]->m_id; + daoError = qx::dao::fetch_by_id_with_all_relation(blog_tmp); qAssert(! daoError.isValid()); + qx::dump(blog_tmp); + qAssert(blog_tmp->m_text == "blog_text_2"); + qAssert(blog_tmp->m_author == NULL); + qAssert(blog_tmp->m_categoryX.size() == 0); + qAssert(blog_tmp->m_commentX.size() == 0); + + // Fetch 2 blogs by id with their relationships + QList lstBlogById; + blog_tmp = std::make_shared(); blog_tmp->m_id = allBlogs[1]->m_id; lstBlogById.append(blog_tmp); + blog_tmp = std::make_shared(); blog_tmp->m_id = allBlogs[0]->m_id; lstBlogById.append(blog_tmp); + daoError = qx::dao::fetch_by_id_with_all_relation(lstBlogById); qAssert(! daoError.isValid()); + qx::dump(lstBlogById); +} + +void miscellaneousOperations() +{ + // Dump all registered classes into QxOrm context (introspection engine) + qx::QxClassX::dumpAllClasses(); + + // Call 'age()' method with class name and method name (reflexion) + author author_1; qx_bool bInvokeOk = qx::QxClassX::invoke("author", "age", author_1); qAssert(bInvokeOk); + + // Fetch all blogs (without relation) + list_blog allBlogs; QSqlError daoError = qx::dao::fetch_all(allBlogs); qAssert(! daoError.isValid()); + qx::dump(allBlogs); qAssert(allBlogs.size() == 2); + + // Test 'isDirty()' method + qx::dao::ptr blog_isdirty = qx::dao::ptr(new blog()); + blog_isdirty->m_id = allBlogs[0]->m_id; + daoError = qx::dao::fetch_by_id(blog_isdirty); + qAssert(! daoError.isValid() && ! blog_isdirty.isDirty()); + + blog_isdirty->m_text = "blog property 'text' modified => blog is dirty !!!"; + QStringList lstDiff; bool bDirty = blog_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 1) && (lstDiff.at(0) == "blog_text")); + if (bDirty) { qDebug("[QxOrm] test dirty 1 : blog is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + // Update only property 'm_text' of 'blog_isdirty' + daoError = qx::dao::update_optimized(blog_isdirty); + qAssert(! daoError.isValid() && ! blog_isdirty.isDirty()); + qx::dump(blog_isdirty); + + // Test 'isDirty()' method with a container + typedef qx::dao::ptr< QList > type_lst_author_test_is_dirty; + type_lst_author_test_is_dirty container_isdirty = type_lst_author_test_is_dirty(new QList()); + daoError = qx::dao::fetch_all(container_isdirty); + qAssert(! daoError.isValid() && ! container_isdirty.isDirty() && (container_isdirty->count() == qx::dao::count())); + + author_ptr author_ptr_dirty = container_isdirty->at(1); + author_ptr_dirty->m_name = "author name modified at index 1 => container is dirty !!!"; + bDirty = container_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 1)); + if (bDirty) { qDebug("[QxOrm] test dirty 2 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + author_ptr_dirty = container_isdirty->at(2); + author_ptr_dirty->m_birthdate = QDate(1998, 03, 06); + bDirty = container_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 2)); + if (bDirty) { qDebug("[QxOrm] test dirty 3 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + // Update only property 'm_name' at position 1, only property 'm_birthdate' at position 2 and nothing at position 0 + daoError = qx::dao::update_optimized(container_isdirty); + qAssert(! daoError.isValid() && ! container_isdirty.isDirty()); + qx::dump(container_isdirty); + + // Call a custom query and get JSON response as QVariantMap + qx_query customQuery("{ \"find\": \"author\", \"filter\": { } }"); + daoError = qx::dao::call_query(customQuery); qAssert(! daoError.isValid()); + QString responseCustomQuery = customQuery.response().toString(); + QVariantMap responseCustomQueryAsJson; + qx::serialization::json::from_string(responseCustomQueryAsJson, responseCustomQuery); + qx::dump(responseCustomQueryAsJson); + + // Call a custom query with cursor and get JSON response as QList + qx_query customQueryCursor("cursor", "{ \"find\": \"author\", \"filter\": { } }"); + daoError = qx::dao::call_query(customQueryCursor); qAssert(! daoError.isValid()); + QString responseCustomQueryCursor = customQueryCursor.response().toString(); + QList responseCustomQueryCursorAsJson; + qx::serialization::json::from_string(responseCustomQueryCursorAsJson, responseCustomQueryCursor); + qx::dump(responseCustomQueryCursorAsJson); + qAssert(responseCustomQueryCursorAsJson.count() == qx::dao::count()); + + // Test delete by query using MongoDB aggregation framework (delete all female) + qx_query queryAggregate("aggregate", "[ { \"$match\" : { \"sex\" : " + QString::number(static_cast(author::female)) + " } } ]"); + daoError = qx::dao::delete_by_query(queryAggregate); qAssert(! daoError.isValid()); + qAssert(qx::dao::count() == 2); +} + +void testQtProperty() +{ + TestQtProperty tst; + tst.setDesc("tst"); + QSqlError daoError = qx::dao::insert(tst); qAssert(! daoError.isValid()); + tst.setDesc("tst update"); + daoError = qx::dao::update(tst); qAssert(! daoError.isValid()); + daoError = qx::dao::fetch_by_id(tst); qAssert(! daoError.isValid()); + std::shared_ptr lst = qx::IxPersistable::qxFetchAll("TestQtProperty"); qAssert(lst->__count() == 1); +} diff --git a/test/qxBlogPImpl/CMakeLists.txt b/test/qxBlogPImpl/CMakeLists.txt new file mode 100644 index 0000000..b8dc627 --- /dev/null +++ b/test/qxBlogPImpl/CMakeLists.txt @@ -0,0 +1,56 @@ +cmake_minimum_required(VERSION 3.1) + +project(qxBlogPImpl LANGUAGES CXX) + +include(../../QxOrm.cmake) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_DEBUG_POSTFIX "d") + +set(HEADERS + ./include/precompiled.h + ./include/export.h + ./include/author.h + ./include/blog.h + ./include/category.h + ./include/comment.h + ) + +set(SRCS + ./src/author.cpp + ./src/blog.cpp + ./src/category.cpp + ./src/comment.cpp + ./src/main.cpp + ) + +add_executable(qxBlogPImpl ${SRCS} ${HEADERS}) + +target_compile_definitions(qxBlogPImpl PRIVATE -D_BUILDING_QX_BLOG) + +if(COMMAND target_precompile_headers) + target_precompile_headers(qxBlogPImpl PRIVATE ./include/precompiled.h) +endif() # (COMMAND target_precompile_headers) + +target_link_libraries(qxBlogPImpl ${QX_LIBRARIES} QxOrm) + +set_target_properties(qxBlogPImpl PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ) + +set_target_properties(qxBlogPImpl PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) diff --git a/test/qxBlogPImpl/debug/.gitignore b/test/qxBlogPImpl/debug/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogPImpl/debug/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogPImpl/include/author.h b/test/qxBlogPImpl/include/author.h new file mode 100644 index 0000000..4da39e1 --- /dev/null +++ b/test/qxBlogPImpl/include/author.h @@ -0,0 +1,48 @@ +#ifndef _QX_BLOG_AUTHOR_H_ +#define _QX_BLOG_AUTHOR_H_ + +class QX_BLOG_DLL_EXPORT author +{ + + QX_REGISTER_FRIEND_CLASS(author) + +private: + + struct author_impl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + +public: + + enum enum_sex { male, female, unknown }; + + author(); + virtual ~author(); + + author(const author & other); + author & operator=(const author & other); + +#ifdef Q_COMPILER_RVALUE_REFS + author(author && other) Q_DECL_NOEXCEPT; + author & operator=(author && other) Q_DECL_NOEXCEPT; +#endif // Q_COMPILER_RVALUE_REFS + + int age() const; + QString id() const; + QString name() const; + QDate birthdate() const; + enum_sex sex() const; + + void setId(const QString & s); + void setName(const QString & s); + void setBirthdate(const QDate & d); + void setSex(enum_sex e); + +}; + +QX_REGISTER_PRIMARY_KEY(author, QString) +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr author_ptr; +typedef qx::QxCollection list_author; + +#endif // _QX_BLOG_AUTHOR_H_ diff --git a/test/qxBlogPImpl/include/blog.h b/test/qxBlogPImpl/include/blog.h new file mode 100644 index 0000000..331237b --- /dev/null +++ b/test/qxBlogPImpl/include/blog.h @@ -0,0 +1,50 @@ +#ifndef _QX_BLOG_BLOG_H_ +#define _QX_BLOG_BLOG_H_ + +class author; +class comment; +class category; + +class QX_BLOG_DLL_EXPORT blog +{ + + QX_REGISTER_FRIEND_CLASS(blog) + +private: + + struct blog_impl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + +public: + + blog(); + virtual ~blog(); + + blog(const blog & other); + blog & operator=(const blog & other); + +#ifdef Q_COMPILER_RVALUE_REFS + blog(blog && other) Q_DECL_NOEXCEPT; + blog & operator=(blog && other) Q_DECL_NOEXCEPT; +#endif // Q_COMPILER_RVALUE_REFS + + long id() const; + QString text() const; + QDateTime dateCreation() const; + + void setId(long l); + void setText(const QString & s); + void setDateCreation(const QDateTime & d); + + std::shared_ptr & getAuthor(); + QList< std::shared_ptr > & listOfComments(); + qx::QxCollection > & listOfCategories(); + +}; + +QX_REGISTER_HPP_QX_BLOG(blog, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr blog_ptr; +typedef std::vector list_blog; + +#endif // _QX_BLOG_BLOG_H_ diff --git a/test/qxBlogPImpl/include/category.h b/test/qxBlogPImpl/include/category.h new file mode 100644 index 0000000..300343c --- /dev/null +++ b/test/qxBlogPImpl/include/category.h @@ -0,0 +1,46 @@ +#ifndef _QX_BLOG_CATEGORY_H_ +#define _QX_BLOG_CATEGORY_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT category +{ + + QX_REGISTER_FRIEND_CLASS(category) + +private: + + struct category_impl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + +public: + + category(); + virtual ~category(); + + category(const category & other); + category & operator=(const category & other); + +#ifdef Q_COMPILER_RVALUE_REFS + category(category && other) Q_DECL_NOEXCEPT; + category & operator=(category && other) Q_DECL_NOEXCEPT; +#endif // Q_COMPILER_RVALUE_REFS + + long id() const; + QString name() const; + QString desc() const; + + void setId(long l); + void setName(const QString & s); + void setDesc(const QString & s); + + qx::QxCollection > & listOfBlogs(); + +}; + +QX_REGISTER_HPP_QX_BLOG(category, qx::trait::no_base_class_defined, 0) + +typedef QSharedPointer category_ptr; +typedef qx::QxCollection list_category; + +#endif // _QX_BLOG_CATEGORY_H_ diff --git a/test/qxBlogPImpl/include/comment.h b/test/qxBlogPImpl/include/comment.h new file mode 100644 index 0000000..7cc277f --- /dev/null +++ b/test/qxBlogPImpl/include/comment.h @@ -0,0 +1,46 @@ +#ifndef _QX_BLOG_COMMENT_H_ +#define _QX_BLOG_COMMENT_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT comment +{ + + QX_REGISTER_FRIEND_CLASS(comment) + +private: + + struct comment_impl; + std::unique_ptr m_pImpl; //!< Private implementation idiom + +public: + + comment(); + virtual ~comment(); + + comment(const comment & other); + comment & operator=(const comment & other); + +#ifdef Q_COMPILER_RVALUE_REFS + comment(comment && other) Q_DECL_NOEXCEPT; + comment & operator=(comment && other) Q_DECL_NOEXCEPT; +#endif // Q_COMPILER_RVALUE_REFS + + long id() const; + QString text() const; + QDateTime dateCreation() const; + + void setId(long l); + void setText(const QString & s); + void setDateCreation(const QDateTime & d); + + std::shared_ptr & getBlog(); + +}; + +QX_REGISTER_HPP_QX_BLOG(comment, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr comment_ptr; +typedef QList list_comment; + +#endif // _QX_BLOG_COMMENT_H_ diff --git a/test/qxBlogPImpl/include/export.h b/test/qxBlogPImpl/include/export.h new file mode 100644 index 0000000..b1c11df --- /dev/null +++ b/test/qxBlogPImpl/include/export.h @@ -0,0 +1,18 @@ +#ifndef _QX_BLOG_EXPORT_H_ +#define _QX_BLOG_EXPORT_H_ + +#ifdef _BUILDING_QX_BLOG +#define QX_BLOG_DLL_EXPORT QX_DLL_EXPORT_HELPER +#else // _BUILDING_QX_BLOG +#define QX_BLOG_DLL_EXPORT QX_DLL_IMPORT_HELPER +#endif // _BUILDING_QX_BLOG + +#ifdef _BUILDING_QX_BLOG +#define QX_REGISTER_HPP_QX_BLOG QX_REGISTER_HPP_EXPORT_DLL +#define QX_REGISTER_CPP_QX_BLOG QX_REGISTER_CPP_EXPORT_DLL +#else // _BUILDING_QX_BLOG +#define QX_REGISTER_HPP_QX_BLOG QX_REGISTER_HPP_IMPORT_DLL +#define QX_REGISTER_CPP_QX_BLOG QX_REGISTER_CPP_IMPORT_DLL +#endif // _BUILDING_QX_BLOG + +#endif // _QX_BLOG_EXPORT_H_ diff --git a/test/qxBlogPImpl/include/precompiled.h b/test/qxBlogPImpl/include/precompiled.h new file mode 100644 index 0000000..0d8b994 --- /dev/null +++ b/test/qxBlogPImpl/include/precompiled.h @@ -0,0 +1,8 @@ +#ifndef _QX_BLOG_PRECOMPILED_HEADER_H_ +#define _QX_BLOG_PRECOMPILED_HEADER_H_ + +#include + +#include "export.h" + +#endif // _QX_BLOG_PRECOMPILED_HEADER_H_ diff --git a/test/qxBlogPImpl/qt/moc/.gitignore b/test/qxBlogPImpl/qt/moc/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogPImpl/qt/moc/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogPImpl/qxBlog.pro b/test/qxBlogPImpl/qxBlog.pro new file mode 100644 index 0000000..44ef8e8 --- /dev/null +++ b/test/qxBlogPImpl/qxBlog.pro @@ -0,0 +1,34 @@ +include(../../QxOrm.pri) + +TEMPLATE = app +DEFINES += _BUILDING_QX_BLOG +INCLUDEPATH += ../../../QxOrm/include/ +DESTDIR = ../../../QxOrm/test/_bin/ +LIBS += -L"../../../QxOrm/lib" + +!contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) { +PRECOMPILED_HEADER = ./include/precompiled.h +} # !contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) + +macx:CONFIG-=app_bundle + +CONFIG(debug, debug|release) { +TARGET = qxBlogPImpld +LIBS += -l"QxOrmd" +} else { +TARGET = qxBlogPImpl +LIBS += -l"QxOrm" +} # CONFIG(debug, debug|release) + +HEADERS += ./include/precompiled.h +HEADERS += ./include/export.h +HEADERS += ./include/author.h +HEADERS += ./include/blog.h +HEADERS += ./include/category.h +HEADERS += ./include/comment.h + +SOURCES += ./src/author.cpp +SOURCES += ./src/blog.cpp +SOURCES += ./src/category.cpp +SOURCES += ./src/comment.cpp +SOURCES += ./src/main.cpp diff --git a/test/qxBlogPImpl/release/.gitignore b/test/qxBlogPImpl/release/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogPImpl/release/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogPImpl/src/author.cpp b/test/qxBlogPImpl/src/author.cpp new file mode 100644 index 0000000..ab74dbd --- /dev/null +++ b/test/qxBlogPImpl/src/author.cpp @@ -0,0 +1,75 @@ +#include "../include/precompiled.h" + +#include "../include/author.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(author) + +struct Q_DECL_HIDDEN author::author_impl +{ + QString m_id; + QString m_name; + QDate m_birthdate; + author::enum_sex m_sex; + list_blog m_blogX; + + author_impl() : m_sex(author::unknown) { ; } + ~author_impl() { ; } +}; + +namespace qx { +template <> void register_class(QxClass & t) +{ + IxDataMember * pImpl = t.pimpl(& author::m_pImpl); + + t.id(& author::author_impl::m_id, "author_id", 0, pImpl); + + t.data(& author::author_impl::m_name, "name", 0, true, true, pImpl); + t.data(& author::author_impl::m_birthdate, "birthdate", 0, true, true, pImpl); + t.data(& author::author_impl::m_sex, "sex", 0, true, true, pImpl); + + t.relationOneToMany(& author::author_impl::m_blogX, "list_blog", "author_id", 0, pImpl); + + t.fct_0(std::mem_fn(& author::age), "age"); // using std::mem_fn() here is just a workaround for an issue with some versions of MSVC, it is not required with a full compliant C++11 compiler (http://stackoverflow.com/questions/23778883/vs2013-stdfunction-with-member-function) +}} + +author::author() : m_pImpl(new author_impl()) { ; } + +author::~author() { ; } + +author::author(const author & other) : m_pImpl(new author_impl(* other.m_pImpl)) { ; } + +author & author::operator=(const author & other) +{ + if (this != (& other)) { (* m_pImpl) = (* other.m_pImpl); } + return (* this); +} + +#ifdef Q_COMPILER_RVALUE_REFS +author::author(author && other) Q_DECL_NOEXCEPT : m_pImpl(std::move(other.m_pImpl)) { ; } +author & author::operator=(author && other) Q_DECL_NOEXCEPT { if (this != (& other)) { m_pImpl = std::move(other.m_pImpl); }; return (* this); } +#endif // Q_COMPILER_RVALUE_REFS + +int author::age() const +{ + if (! m_pImpl->m_birthdate.isValid()) { return -1; } + return (QDate::currentDate().year() - m_pImpl->m_birthdate.year()); +} + +QString author::id() const { return m_pImpl->m_id; } + +QString author::name() const { return m_pImpl->m_name; } + +QDate author::birthdate() const { return m_pImpl->m_birthdate; } + +author::enum_sex author::sex() const { return m_pImpl->m_sex; } + +void author::setId(const QString & s) { m_pImpl->m_id = s; } + +void author::setName(const QString & s) { m_pImpl->m_name = s; } + +void author::setBirthdate(const QDate & d) { m_pImpl->m_birthdate = d; } + +void author::setSex(enum_sex e) { m_pImpl->m_sex = e; } diff --git a/test/qxBlogPImpl/src/blog.cpp b/test/qxBlogPImpl/src/blog.cpp new file mode 100644 index 0000000..c0ab97c --- /dev/null +++ b/test/qxBlogPImpl/src/blog.cpp @@ -0,0 +1,73 @@ +#include "../include/precompiled.h" + +#include "../include/blog.h" +#include "../include/author.h" +#include "../include/comment.h" +#include "../include/category.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(blog) + +struct Q_DECL_HIDDEN blog::blog_impl +{ + long m_id; + QString m_text; + QDateTime m_dt_creation; + author_ptr m_author; + list_comment m_commentX; + list_category m_categoryX; + + blog_impl() : m_id(0) { ; } + ~blog_impl() { ; } +}; + +namespace qx { +template <> void register_class(QxClass & t) +{ + IxDataMember * pImpl = t.pimpl(& blog::m_pImpl); + + t.id(& blog::blog_impl::m_id, "blog_id", 0, pImpl); + + t.data(& blog::blog_impl::m_text, "blog_text", 0, true, true, pImpl); + t.data(& blog::blog_impl::m_dt_creation, "date_creation", 0, true, true, pImpl); + + t.relationManyToOne(& blog::blog_impl::m_author, "author_id", 0, pImpl); + t.relationOneToMany(& blog::blog_impl::m_commentX, "list_comment", "blog_id", 0, pImpl); + t.relationManyToMany(& blog::blog_impl::m_categoryX, "list_category", "category_blog", "blog_id", "category_id", 0, pImpl); +}} + +blog::blog() : m_pImpl(new blog_impl()) { ; } + +blog::~blog() { ; } + +blog::blog(const blog & other) : m_pImpl(new blog_impl(* other.m_pImpl)) { ; } + +blog & blog::operator=(const blog & other) +{ + if (this != (& other)) { (* m_pImpl) = (* other.m_pImpl); } + return (* this); +} + +#ifdef Q_COMPILER_RVALUE_REFS +blog::blog(blog && other) Q_DECL_NOEXCEPT : m_pImpl(std::move(other.m_pImpl)) { ; } +blog & blog::operator=(blog && other) Q_DECL_NOEXCEPT { if (this != (& other)) { m_pImpl = std::move(other.m_pImpl); }; return (* this); } +#endif // Q_COMPILER_RVALUE_REFS + +long blog::id() const { return m_pImpl->m_id; } + +QString blog::text() const { return m_pImpl->m_text; } + +QDateTime blog::dateCreation() const { return m_pImpl->m_dt_creation; } + +void blog::setId(long l) { m_pImpl->m_id = l; } + +void blog::setText(const QString & s) { m_pImpl->m_text = s; } + +void blog::setDateCreation(const QDateTime & d) { m_pImpl->m_dt_creation = d; } + +std::shared_ptr & blog::getAuthor() { return m_pImpl->m_author; } + +QList< std::shared_ptr > & blog::listOfComments() { return m_pImpl->m_commentX; } + +qx::QxCollection > & blog::listOfCategories() { return m_pImpl->m_categoryX; } diff --git a/test/qxBlogPImpl/src/category.cpp b/test/qxBlogPImpl/src/category.cpp new file mode 100644 index 0000000..c4bde55 --- /dev/null +++ b/test/qxBlogPImpl/src/category.cpp @@ -0,0 +1,65 @@ +#include "../include/precompiled.h" + +#include "../include/category.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(category) + +struct Q_DECL_HIDDEN category::category_impl +{ + typedef qx::QxCollection list_blog; + + long m_id; + QString m_name; + QString m_desc; + list_blog m_blogX; + + category_impl() : m_id(0) { ; } + ~category_impl() { ; } +}; + +namespace qx { +template <> void register_class(QxClass & t) +{ + IxDataMember * pImpl = t.pimpl(& category::m_pImpl); + + t.id(& category::category_impl::m_id, "category_id", 0, pImpl); + + t.data(& category::category_impl::m_name, "name", 0, true, true, pImpl); + t.data(& category::category_impl::m_desc, "description", 0, true, true, pImpl); + + t.relationManyToMany(& category::category_impl::m_blogX, "list_blog", "category_blog", "category_id", "blog_id", 0, pImpl); +}} + +category::category() : m_pImpl(new category_impl()) { ; } + +category::~category() { ; } + +category::category(const category & other) : m_pImpl(new category_impl(* other.m_pImpl)) { ; } + +category & category::operator=(const category & other) +{ + if (this != (& other)) { (* m_pImpl) = (* other.m_pImpl); } + return (* this); +} + +#ifdef Q_COMPILER_RVALUE_REFS +category::category(category && other) Q_DECL_NOEXCEPT : m_pImpl(std::move(other.m_pImpl)) { ; } +category & category::operator=(category && other) Q_DECL_NOEXCEPT { if (this != (& other)) { m_pImpl = std::move(other.m_pImpl); }; return (* this); } +#endif // Q_COMPILER_RVALUE_REFS + +long category::id() const { return m_pImpl->m_id; } + +QString category::name() const { return m_pImpl->m_name; } + +QString category::desc() const { return m_pImpl->m_desc; } + +void category::setId(long l) { m_pImpl->m_id = l; } + +void category::setName(const QString & s) { m_pImpl->m_name = s; } + +void category::setDesc(const QString & s) { m_pImpl->m_desc = s; } + +qx::QxCollection > & category::listOfBlogs() { return m_pImpl->m_blogX; } diff --git a/test/qxBlogPImpl/src/comment.cpp b/test/qxBlogPImpl/src/comment.cpp new file mode 100644 index 0000000..52e9995 --- /dev/null +++ b/test/qxBlogPImpl/src/comment.cpp @@ -0,0 +1,63 @@ +#include "../include/precompiled.h" + +#include "../include/comment.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(comment) + +struct Q_DECL_HIDDEN comment::comment_impl +{ + long m_id; + QString m_text; + QDateTime m_dt_create; + blog_ptr m_blog; + + comment_impl() : m_id(0) { ; } + ~comment_impl() { ; } +}; + +namespace qx { +template <> void register_class(QxClass & t) +{ + IxDataMember * pImpl = t.pimpl(& comment::m_pImpl); + + t.id(& comment::comment_impl::m_id, "comment_id", 0, pImpl); + + t.data(& comment::comment_impl::m_text, "comment_text", 0, true, true, pImpl); + t.data(& comment::comment_impl::m_dt_create, "date_creation", 0, true, true, pImpl); + + t.relationManyToOne(& comment::comment_impl::m_blog, "blog_id", 0, pImpl); +}} + +comment::comment() : m_pImpl(new comment_impl()) { ; } + +comment::~comment() { ; } + +comment::comment(const comment & other) : m_pImpl(new comment_impl(* other.m_pImpl)) { ; } + +comment & comment::operator=(const comment & other) +{ + if (this != (& other)) { (* m_pImpl) = (* other.m_pImpl); } + return (* this); +} + +#ifdef Q_COMPILER_RVALUE_REFS +comment::comment(comment && other) Q_DECL_NOEXCEPT : m_pImpl(std::move(other.m_pImpl)) { ; } +comment & comment::operator=(comment && other) Q_DECL_NOEXCEPT { if (this != (& other)) { m_pImpl = std::move(other.m_pImpl); }; return (* this); } +#endif // Q_COMPILER_RVALUE_REFS + +long comment::id() const { return m_pImpl->m_id; } + +QString comment::text() const { return m_pImpl->m_text; } + +QDateTime comment::dateCreation() const { return m_pImpl->m_dt_create; } + +void comment::setId(long l) { m_pImpl->m_id = l; } + +void comment::setText(const QString & s) { m_pImpl->m_text = s; } + +void comment::setDateCreation(const QDateTime & d) { m_pImpl->m_dt_create = d; } + +std::shared_ptr & comment::getBlog() { return m_pImpl->m_blog; } diff --git a/test/qxBlogPImpl/src/main.cpp b/test/qxBlogPImpl/src/main.cpp new file mode 100644 index 0000000..c30733c --- /dev/null +++ b/test/qxBlogPImpl/src/main.cpp @@ -0,0 +1,372 @@ +#include "../include/precompiled.h" + +#include + +#include "../include/blog.h" +#include "../include/author.h" +#include "../include/comment.h" +#include "../include/category.h" + +#include + +int main(int argc, char * argv[]) +{ + // Qt application + QCoreApplication app(argc, argv); + QFile::remove("./qxBlogPImpl.sqlite"); + + // Parameters to connect to database + qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE"); + qx::QxSqlDatabase::getSingleton()->setDatabaseName("./qxBlogPImpl.sqlite"); + qx::QxSqlDatabase::getSingleton()->setHostName("localhost"); + qx::QxSqlDatabase::getSingleton()->setUserName("root"); + qx::QxSqlDatabase::getSingleton()->setPassword(""); + qx::QxSqlDatabase::getSingleton()->setFormatSqlQueryBeforeLogging(true); + qx::QxSqlDatabase::getSingleton()->setDisplayTimerDetails(true); + + // Only for debug purpose : assert if invalid offset detected fetching a relation + qx::QxSqlDatabase::getSingleton()->setVerifyOffsetRelation(true); + + // Create all tables in database + QSqlError daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + + // Create a list of 3 author + author_ptr author_1; author_1.reset(new author()); + author_ptr author_2; author_2.reset(new author()); + author_ptr author_3; author_3.reset(new author()); + + author_1->setId("author_id_1"); author_1->setName("author_1"); + author_1->setSex(author::male); author_1->setBirthdate(QDate::currentDate()); + author_2->setId("author_id_2"); author_2->setName("author_2"); + author_2->setSex(author::female); author_2->setBirthdate(QDate::currentDate()); + author_3->setId("author_id_3"); author_3->setName("author_3"); + author_3->setSex(author::female); author_3->setBirthdate(QDate::currentDate()); + + list_author authorX; + authorX.insert(author_1->id(), author_1); + authorX.insert(author_2->id(), author_2); + authorX.insert(author_3->id(), author_3); + + // Insert list of 3 author into database + daoError = qx::dao::insert(authorX); + qAssert(qx::dao::count() == 3); + + // Clone author 2 : 'author_id_2' + author_ptr author_clone = qx::clone(* author_2); + qAssert(author_clone->id() == "author_id_2"); + qAssert(author_clone->sex() == author::female); + + // Create a query to fetch only female author : 'author_id_2' and 'author_id_3' + qx::QxSqlQuery query("WHERE author.sex = :sex"); + query.bind(":sex", author::female); + + list_author list_of_female_author; + daoError = qx::dao::fetch_by_query(query, list_of_female_author); + qAssert(list_of_female_author.count() == 2); + + // Dump list of female author (xml serialization) + qx::dump(list_of_female_author, false); + qx::dump(list_of_female_author, true); + + // Test qx::QxSqlQuery::freeText() with/without placeholders + query = qx_query(); query.freeText("WHERE author.sex = " + QString::number(static_cast(author::female))); + daoError = qx::dao::fetch_by_query(query, list_of_female_author); + qAssert(list_of_female_author.count() == 2); + query = qx_query(); query.freeText("WHERE author.sex = :sex", QVariantList() << author::female); + daoError = qx::dao::fetch_by_query(query, list_of_female_author); + qAssert(list_of_female_author.count() == 2); + query = qx_query(); query.freeText("WHERE author.sex=:sex AND author.author_id=:author_id", QVariantList() << author::female << "author_id_2"); + daoError = qx::dao::fetch_by_query(query, list_of_female_author); + qAssert(list_of_female_author.count() == 1); + query = qx_query(); query.freeText("WHERE (author.sex = :sex) AND (author.author_id = :author_id)", QVariantList() << author::female << "author_id_2"); + daoError = qx::dao::fetch_by_query(query, list_of_female_author); + qAssert(list_of_female_author.count() == 1); + + // Create 3 categories + category_ptr category_1 = category_ptr(new category()); + category_ptr category_2 = category_ptr(new category()); + category_ptr category_3 = category_ptr(new category()); + + category_1->setName("category_1"); category_1->setDesc("desc_1"); + category_2->setName("category_2"); category_2->setDesc("desc_2"); + category_3->setName("category_3"); category_3->setDesc("desc_3"); + + { // Create a scope to destroy temporary connexion to database + + // Open a transaction to database + QSqlDatabase db = qx::QxSqlDatabase::getDatabase(); + bool bCommit = db.transaction(); + + // Insert 3 categories into database, use 'db' parameter for the transaction + daoError = qx::dao::insert(category_1, (& db)); bCommit = (bCommit && ! daoError.isValid()); + daoError = qx::dao::insert(category_2, (& db)); bCommit = (bCommit && ! daoError.isValid()); + daoError = qx::dao::insert(category_3, (& db)); bCommit = (bCommit && ! daoError.isValid()); + + qAssert(bCommit); + qAssert(category_1->id() != 0); + qAssert(category_2->id() != 0); + qAssert(category_3->id() != 0); + + // Terminate transaction => commit or rollback if there is error + if (bCommit) { db.commit(); } + else { db.rollback(); } + + } // End of scope : 'db' is destroyed + + // Create a blog with the class name (factory) + qx::any blog_any = qx::create("blog"); + blog_ptr blog_1; + try { blog_1 = qx::any_cast(blog_any); } + catch (...) { blog_1.reset(new blog()); } + blog_1->setText("blog_text_1"); + blog_1->setDateCreation(QDateTime::currentDateTime()); + blog_1->getAuthor() = author_1; + + // Insert 'blog_1' into database with 'save()' method + daoError = qx::dao::save(blog_1); + + // Modify 'blog_1' properties and save into database + blog_1->setText("update blog_text_1"); + blog_1->getAuthor() = author_2; + daoError = qx::dao::save(blog_1); + + // Add 2 comments to 'blog_1' + comment_ptr comment_1; comment_1.reset(new comment()); + comment_ptr comment_2; comment_2.reset(new comment()); + + comment_1->setText("comment_1 text"); + comment_1->setDateCreation(QDateTime::currentDateTime()); + comment_1->getBlog() = blog_1; + comment_2->setText("comment_2 text"); + comment_2->setDateCreation(QDateTime::currentDateTime()); + comment_2->getBlog() = blog_1; + + daoError = qx::dao::insert(comment_1); + daoError = qx::dao::insert(comment_2); + qAssert(qx::dao::count() == 2); + + // Add 2 categories to 'blog_1' => must insert into extra-table 'category_blog' + blog_1->listOfCategories().insert(category_1->id(), category_1); + blog_1->listOfCategories().insert(category_3->id(), category_3); + daoError = qx::dao::save_with_relation("list_category", blog_1); + + // Fetch blog into a new variable with all relation : 'author', 'comment' and 'category' + blog_ptr blog_tmp; blog_tmp.reset(new blog()); + blog_tmp->setId(blog_1->id()); + daoError = qx::dao::fetch_by_id_with_all_relation(blog_tmp); + + qAssert(blog_tmp->listOfComments().count() == 2); + qAssert(blog_tmp->listOfCategories().count() == 2); + qAssert(blog_tmp->text() == "update blog_text_1"); + qAssert(blog_tmp->getAuthor() && blog_tmp->getAuthor()->id() == "author_id_2"); + + // Fetch blog into a new variable with many relations using "*->*->*->*" (4 levels of relationships) + blog_tmp.reset(new blog()); + blog_tmp->setId(blog_1->id()); + daoError = qx::dao::fetch_by_id_with_relation("*->*->*->*", blog_tmp); + + qAssert(blog_tmp->listOfComments().count() == 2); + qAssert(blog_tmp->listOfCategories().count() == 2); + qAssert(blog_tmp->text() == "update blog_text_1"); + qAssert(blog_tmp->getAuthor() && blog_tmp->getAuthor()->id() == "author_id_2"); + + // Dump 'blog_tmp' result from database (xml serialization) + qx::dump(blog_tmp, false); + qx::dump(blog_tmp, true); + + // Fetch relations defining columns to fetch with syntax { col_1, col_2, etc... } + list_blog lstBlogComplexRelation; + daoError = qx::dao::fetch_all_with_relation(QStringList() << "{ blog_text }" << "author_id { name, birthdate }" << "list_comment { comment_text } -> blog_id -> *", lstBlogComplexRelation); + qx::dump(lstBlogComplexRelation); + qAssert(lstBlogComplexRelation.size() > 0); + qAssert(lstBlogComplexRelation[0]->text() != ""); // Fetched + qAssert(lstBlogComplexRelation[0]->dateCreation().isNull()); // Not fetched + qAssert(lstBlogComplexRelation[0]->getAuthor()->sex() == author::unknown); // Not fetched + qAssert(lstBlogComplexRelation[0]->getAuthor()->name() != ""); // Fetched + qAssert(lstBlogComplexRelation[0]->listOfComments().size() > 0); + qAssert(lstBlogComplexRelation[0]->listOfComments()[0]->dateCreation().isNull()); // Not fetched + qAssert(lstBlogComplexRelation[0]->listOfComments()[0]->text() != ""); // Fetched + qAssert(lstBlogComplexRelation[0]->listOfComments()[0]->getBlog()); + + // Fetch relations defining columns to remove before fetching with syntax -{ col_1, col_2, etc... } + list_blog lstBlogComplexRelation2; + daoError = qx::dao::fetch_all_with_relation(QStringList() << "-{ blog_text }" << "author_id -{ name, birthdate }" << "list_comment -{ comment_text } -> blog_id -> *", lstBlogComplexRelation2); + qx::dump(lstBlogComplexRelation2); + qAssert(lstBlogComplexRelation2.size() > 0); + qAssert(lstBlogComplexRelation2[0]->text() == ""); // Not fetched + qAssert(! lstBlogComplexRelation2[0]->dateCreation().isNull()); // Fetched + qAssert(lstBlogComplexRelation2[0]->getAuthor()->sex() != author::unknown); // Fetched + qAssert(lstBlogComplexRelation2[0]->getAuthor()->name() == ""); // Not fetched + qAssert(lstBlogComplexRelation2[0]->listOfComments().size() > 0); + qAssert(! lstBlogComplexRelation2[0]->listOfComments()[0]->dateCreation().isNull()); // Fetched + qAssert(lstBlogComplexRelation2[0]->listOfComments()[0]->text() == ""); // Not fetched + qAssert(lstBlogComplexRelation2[0]->listOfComments()[0]->getBlog()); + +#ifndef _QX_NO_JSON + // Custom JSON serialization process + QString customJsonFull = qx::serialization::json::to_string(blog_tmp, 1); + QString customJsonFiltered = qx::serialization::json::to_string(blog_tmp, 1, "filter: { blog_text } | author_id { name, birthdate } | list_comment { comment_text } -> blog_id -> *"); + qDebug("[QxOrm] custom JSON serialization process (full) : \n%s", qPrintable(customJsonFull)); + qDebug("[QxOrm] custom JSON serialization process (filtered) : \n%s", qPrintable(customJsonFiltered)); + + blog_ptr blogFromJsonFull; blogFromJsonFull.reset(new blog()); + blog_ptr blogFromJsonFiltered; blogFromJsonFiltered.reset(new blog()); + qx::serialization::json::from_string(blogFromJsonFull, customJsonFull, 1); + qx::serialization::json::from_string(blogFromJsonFiltered, customJsonFull, 1, "filter: { blog_text } | author_id { name, birthdate } | list_comment { comment_text } -> blog_id -> *"); + + qx::dump(blogFromJsonFull); + qAssert(blogFromJsonFull->listOfComments().count() == 2); + qAssert(blogFromJsonFull->listOfCategories().count() == 2); + qAssert(blogFromJsonFull->text() == "update blog_text_1"); + qAssert(blogFromJsonFull->getAuthor() && blogFromJsonFull->getAuthor()->id() == "author_id_2"); + + qx::dump(blogFromJsonFiltered); + qAssert(blogFromJsonFiltered->text() != ""); // Fetched + qAssert(blogFromJsonFiltered->dateCreation().isNull()); // Not fetched + qAssert(blogFromJsonFiltered->getAuthor()->sex() == author::unknown); // Not fetched + qAssert(blogFromJsonFiltered->getAuthor()->name() != ""); // Fetched + qAssert(blogFromJsonFiltered->listOfComments().size() > 0); + qAssert(blogFromJsonFiltered->listOfComments()[0]->dateCreation().isNull()); // Not fetched + qAssert(blogFromJsonFiltered->listOfComments()[0]->text() != ""); // Fetched + qAssert(blogFromJsonFiltered->listOfComments()[0]->getBlog()); +#endif // _QX_NO_JSON + + // Fetch relations defining columns to fetch with syntax { col_1, col_2, etc... } + custom table alias using syntax + custom table alias suffix using syntax <..._my_alias_suffix> + list_blog lstBlogComplexRelation3; + daoError = qx::dao::fetch_all_with_relation(QStringList() << " { blog_text }" << "author_id { name, birthdate }" << "list_comment { comment_text } -> blog_id -> * <..._my_alias_suffix>", lstBlogComplexRelation3); + qx::dump(lstBlogComplexRelation3); + qAssert(lstBlogComplexRelation3.size() > 0); + qAssert(lstBlogComplexRelation3[0]->text() != ""); // Fetched + qAssert(lstBlogComplexRelation3[0]->dateCreation().isNull()); // Not fetched + qAssert(lstBlogComplexRelation3[0]->getAuthor()->sex() == author::unknown); // Not fetched + qAssert(lstBlogComplexRelation3[0]->getAuthor()->name() != ""); // Fetched + qAssert(lstBlogComplexRelation3[0]->listOfComments().size() > 0); + qAssert(lstBlogComplexRelation3[0]->listOfComments()[0]->dateCreation().isNull()); // Not fetched + qAssert(lstBlogComplexRelation3[0]->listOfComments()[0]->text() != ""); // Fetched + qAssert(lstBlogComplexRelation3[0]->listOfComments()[0]->getBlog()); + + // Test to add join SQL sub-queries (inside LEFT OUTER JOIN or INNER JOIN) + list_blog lstBlogWithJoinQueries; + query = qx_query().where("blog_alias.blog_text").isEqualTo("update blog_text_1"); + query.addJoinQuery("list_comment_alias", "AND list_comment_alias.comment_text IS NOT NULL"); + query.addJoinQuery("author_alias", qx_query().freeText("AND author_alias.sex = :sex", QVariantList() << author::female)); + daoError = qx::dao::fetch_by_query_with_relation(QStringList() << " { blog_text }" << "author_id { name, birthdate, sex }" << "list_comment { comment_text }", query, lstBlogWithJoinQueries); + qx::dump(lstBlogWithJoinQueries); + qAssert(lstBlogWithJoinQueries.size() > 0); + qAssert(lstBlogWithJoinQueries[0]->text() == "update blog_text_1"); + qAssert(lstBlogWithJoinQueries[0]->getAuthor()->sex() == author::female); + + // When join SQL sub-queries are used, then relationships should keep user defined order (in this example : 'list_comment' before 'author') + lstBlogWithJoinQueries.clear(); + query = qx_query().where("blog_alias.blog_text").isEqualTo("update blog_text_1"); + query.addJoinQuery("list_comment_alias", "AND list_comment_alias.comment_text IS NOT NULL"); + query.addJoinQuery("author_alias", qx_query("AND author_alias.sex = :sex", QVariantList() << author::female)); + daoError = qx::dao::fetch_by_query_with_relation(QStringList() << " { blog_text }" << "list_comment { comment_text }" << "author_id { name, birthdate, sex }", query, lstBlogWithJoinQueries); + qx::dump(lstBlogWithJoinQueries); + qAssert(lstBlogWithJoinQueries.size() > 0); + qAssert(lstBlogWithJoinQueries[0]->text() == "update blog_text_1"); + qAssert(lstBlogWithJoinQueries[0]->getAuthor()->sex() == author::female); + + // Check qx::dao::save_with_relation_recursive() function + daoError = qx::dao::save_with_relation_recursive(blog_tmp); + qAssert(! daoError.isValid()); + daoError = qx::dao::save_with_relation_recursive(blog_tmp, qx::dao::save_mode::e_update_only); + qAssert(! daoError.isValid()); + + // Call 'age()' method with class name and method name (reflexion) + qx_bool bInvokeOk = qx::QxClassX::invoke("author", "age", author_1); + qAssert(bInvokeOk); + + // Check count with relations and filter + long lBlogCountWithRelation = 0; qx_query queryBlogCountWithRelation; + daoError = qx::dao::count_with_relation(lBlogCountWithRelation, QStringList() << "author_id" << "list_comment -> blog_id -> *", queryBlogCountWithRelation); + qAssert(! daoError.isValid() && (lBlogCountWithRelation > 0)); + + // Test 'isDirty()' method + qx::dao::ptr blog_isdirty = qx::dao::ptr(new blog()); + blog_isdirty->setId(blog_1->id()); + daoError = qx::dao::fetch_by_id(blog_isdirty); + qAssert(! daoError.isValid() && ! blog_isdirty.isDirty()); + + blog_isdirty->setText("blog property 'text' modified => blog is dirty !!!"); + QStringList lstDiff; bool bDirty = blog_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 1) && (lstDiff.at(0) == "blog_text")); + if (bDirty) { qDebug("[QxOrm] test dirty 1 : blog is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + // Update only property 'm_text' of 'blog_isdirty' + daoError = qx::dao::update_optimized(blog_isdirty); + qAssert(! daoError.isValid() && ! blog_isdirty.isDirty()); + qx::dump(blog_isdirty, false); + qx::dump(blog_isdirty, true); + + // Test 'isDirty()' method with a container + typedef qx::dao::ptr< QList > type_lst_author_test_is_dirty; + type_lst_author_test_is_dirty container_isdirty = type_lst_author_test_is_dirty(new QList()); + daoError = qx::dao::fetch_all(container_isdirty); + qAssert(! daoError.isValid() && ! container_isdirty.isDirty() && (container_isdirty->count() == 3)); + + author_ptr author_ptr_dirty = container_isdirty->at(1); + author_ptr_dirty->setName("author name modified at index 1 => container is dirty !!!"); + bDirty = container_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 1)); + if (bDirty) { qDebug("[QxOrm] test dirty 2 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + author_ptr_dirty = container_isdirty->at(2); + author_ptr_dirty->setBirthdate(QDate(1998, 03, 06)); + bDirty = container_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 2)); + if (bDirty) { qDebug("[QxOrm] test dirty 3 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + // Update only property 'm_name' at position 1, only property 'm_birthdate' at position 2 and nothing at position 0 + daoError = qx::dao::update_optimized(container_isdirty); + qAssert(! daoError.isValid() && ! container_isdirty.isDirty()); + qx::dump(container_isdirty, false); + qx::dump(container_isdirty, true); + + // Fetch only property 'm_dt_creation' of blog + QStringList lstColumns = QStringList() << "date_creation"; + list_blog lst_blog_with_only_date_creation; + daoError = qx::dao::fetch_all(lst_blog_with_only_date_creation, NULL, lstColumns); + qAssert(! daoError.isValid() && (lst_blog_with_only_date_creation.size() > 0)); + if ((lst_blog_with_only_date_creation.size() > 0) && (lst_blog_with_only_date_creation[0].get() != NULL)) + { qAssert(lst_blog_with_only_date_creation[0]->text().isEmpty()); } + qx::dump(lst_blog_with_only_date_creation, false); + qx::dump(lst_blog_with_only_date_creation, true); + + // Dump all registered classes into QxOrm context (introspection engine) + qx::QxClassX::dumpAllClasses(); + + // Call a custom SQL query or a stored procedure + qx_query testStoredProc("SELECT * FROM author"); + daoError = qx::dao::call_query(testStoredProc); + qAssert(! daoError.isValid()); + testStoredProc.dumpSqlResult(); + + // Call a custom SQL query or a stored procedure and fetch automatically properties (with a collection of items) + qx_query testStoredProcBis("SELECT * FROM author"); + authorX.clear(); + daoError = qx::dao::execute_query(testStoredProcBis, authorX); + qAssert(! daoError.isValid()); qAssert(authorX.count() > 0); + qx::dump(authorX, false); + qx::dump(authorX, true); + + // Call a custom SQL query or a stored procedure and fetch automatically properties + qx_query testStoredProcThird("SELECT name, category_id FROM category"); + category_ptr category_tmp = category_ptr(new category()); + daoError = qx::dao::execute_query(testStoredProcThird, category_tmp); + qAssert(! daoError.isValid()); qAssert(category_tmp->id() != 0); + qx::dump(category_tmp, false); + qx::dump(category_tmp, true); + + // Test SQL DISTINCT keyword + QList listOfBlogDistinct; + qx_query queryDistinct; queryDistinct.distinct().limit(10); + daoError = qx::dao::fetch_by_query(queryDistinct, listOfBlogDistinct, NULL, QStringList() << "blog_text"); + qAssert(! daoError.isValid()); qAssert((listOfBlogDistinct.count() > 0) && (listOfBlogDistinct.at(0).id() == 0) && (! listOfBlogDistinct.at(0).text().isEmpty())); + qx::dump(listOfBlogDistinct); + + return 0; +} diff --git a/test/qxBlogRestApi/CMakeLists.txt b/test/qxBlogRestApi/CMakeLists.txt new file mode 100644 index 0000000..69e2a37 --- /dev/null +++ b/test/qxBlogRestApi/CMakeLists.txt @@ -0,0 +1,76 @@ +cmake_minimum_required(VERSION 3.1) + +project(qxBlogRestApi LANGUAGES CXX) + +include(../../QxOrm.cmake) + +if(NOT _QX_NO_JSON) + +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Sql Gui Widgets Quick Qml REQUIRED) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_DEBUG_POSTFIX "d") + +set(HEADERS + ./include/precompiled.h + ./include/export.h + ./include/author.h + ./include/blog.h + ./include/category.h + ./include/comment.h + ) + +set(SRCS + ./src/author.cpp + ./src/blog.cpp + ./src/category.cpp + ./src/comment.cpp + ./src/main.cpp + ) + +set(QRCS + ./qt/rcc/qxBlogRestApi.qrc + ) + +if(COMMAND qt_add_resources) +qt_add_resources(QRCS_HDRS ${QRCS}) +else() # (COMMAND qt_add_resources) +qt5_add_resources(QRCS_HDRS ${QRCS}) +endif() # (COMMAND qt_add_resources) + +add_executable(qxBlogRestApi ${SRCS} ${HEADERS} ${QRCS_HDRS}) + +target_compile_definitions(qxBlogRestApi PRIVATE -D_BUILDING_QX_BLOG) + +if(COMMAND target_precompile_headers) + target_precompile_headers(qxBlogRestApi PRIVATE ./include/precompiled.h) +endif() # (COMMAND target_precompile_headers) + +target_link_libraries(qxBlogRestApi ${QX_LIBRARIES} QxOrm Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Quick Qt${QT_VERSION_MAJOR}::Qml) + +set_target_properties(qxBlogRestApi PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin" + ) + +set_target_properties(qxBlogRestApi PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) + +else() # _QX_NO_JSON + +message(STATUS "qxBlogRestApi project not loaded because _QX_NO_JSON option is defined (QxOrm REST API requires JSON serialization)") + +endif() # _QX_NO_JSON diff --git a/test/qxBlogRestApi/debug/.gitignore b/test/qxBlogRestApi/debug/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogRestApi/debug/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogRestApi/include/author.h b/test/qxBlogRestApi/include/author.h new file mode 100644 index 0000000..c83ebb6 --- /dev/null +++ b/test/qxBlogRestApi/include/author.h @@ -0,0 +1,34 @@ +#ifndef _QX_BLOG_AUTHOR_H_ +#define _QX_BLOG_AUTHOR_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT author : public qx::IxPersistable +{ + QX_PERSISTABLE_HPP(author) +public: +// -- typedef + typedef std::shared_ptr blog_ptr; + typedef std::vector list_blog; +// -- enum + enum enum_sex { male, female, unknown }; +// -- properties + QString m_id; + QString m_name; + QDate m_birthdate; + enum_sex m_sex; + list_blog m_blogX; +// -- contructor, virtual destructor + author() : qx::IxPersistable(), m_id("0"), m_sex(unknown) { ; } + virtual ~author() { ; } +// -- methods + int age() const; +}; + +QX_REGISTER_PRIMARY_KEY(author, QString) +QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr author_ptr; +typedef qx::QxCollection list_author; + +#endif // _QX_BLOG_AUTHOR_H_ diff --git a/test/qxBlogRestApi/include/blog.h b/test/qxBlogRestApi/include/blog.h new file mode 100644 index 0000000..182d53c --- /dev/null +++ b/test/qxBlogRestApi/include/blog.h @@ -0,0 +1,37 @@ +#ifndef _QX_BLOG_BLOG_H_ +#define _QX_BLOG_BLOG_H_ + +#include "author.h" +#include "comment.h" +#include "category.h" + +class QX_BLOG_DLL_EXPORT blog : public qx::IxPersistable +{ + QX_PERSISTABLE_HPP(blog) + QX_REGISTER_FRIEND_CLASS(blog) +public: +// -- properties + long m_id; + QString m_text; + QDateTime m_dt_creation; + author_ptr m_author; + list_comment m_commentX; + list_category m_categoryX; +// -- contructor, virtual destructor + blog() : qx::IxPersistable(), m_id(0) { ; } + virtual ~blog() { ; } +#ifndef _QX_NO_JSON +// -- function callable by introspection and REST API + static QJsonValue helloWorld(const QJsonValue & request); +#endif // _QX_NO_JSON +private: +// -- function to validate a blog instance + void isValid(qx::QxInvalidValueX & invalidValues); +}; + +QX_REGISTER_HPP_QX_BLOG(blog, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr blog_ptr; +typedef std::vector list_blog; + +#endif // _QX_BLOG_BLOG_H_ diff --git a/test/qxBlogRestApi/include/category.h b/test/qxBlogRestApi/include/category.h new file mode 100644 index 0000000..fd23b54 --- /dev/null +++ b/test/qxBlogRestApi/include/category.h @@ -0,0 +1,28 @@ +#ifndef _QX_BLOG_CATEGORY_H_ +#define _QX_BLOG_CATEGORY_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT category : public qx::IxPersistable +{ + QX_PERSISTABLE_HPP(category) +public: +// -- typedef + typedef std::shared_ptr blog_ptr; + typedef qx::QxCollection list_blog; +// -- properties + long m_id; + QString m_name; + QString m_desc; + list_blog m_blogX; +// -- contructor, virtual destructor + category() : qx::IxPersistable(), m_id(0) { ; } + virtual ~category() { ; } +}; + +QX_REGISTER_HPP_QX_BLOG(category, qx::trait::no_base_class_defined, 0) + +typedef QSharedPointer category_ptr; +typedef qx::QxCollection list_category; + +#endif // _QX_BLOG_CATEGORY_H_ diff --git a/test/qxBlogRestApi/include/comment.h b/test/qxBlogRestApi/include/comment.h new file mode 100644 index 0000000..c0d1897 --- /dev/null +++ b/test/qxBlogRestApi/include/comment.h @@ -0,0 +1,27 @@ +#ifndef _QX_BLOG_COMMENT_H_ +#define _QX_BLOG_COMMENT_H_ + +class blog; + +class QX_BLOG_DLL_EXPORT comment : public qx::IxPersistable +{ + QX_PERSISTABLE_HPP(comment) +public: +// -- typedef + typedef std::shared_ptr blog_ptr; +// -- properties + long m_id; + QString m_text; + QDateTime m_dt_create; + blog_ptr m_blog; +// -- contructor, virtual destructor + comment() : qx::IxPersistable(), m_id(0) { ; } + virtual ~comment() { ; } +}; + +QX_REGISTER_HPP_QX_BLOG(comment, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr comment_ptr; +typedef QList list_comment; + +#endif // _QX_BLOG_COMMENT_H_ diff --git a/test/qxBlogRestApi/include/export.h b/test/qxBlogRestApi/include/export.h new file mode 100644 index 0000000..b1c11df --- /dev/null +++ b/test/qxBlogRestApi/include/export.h @@ -0,0 +1,18 @@ +#ifndef _QX_BLOG_EXPORT_H_ +#define _QX_BLOG_EXPORT_H_ + +#ifdef _BUILDING_QX_BLOG +#define QX_BLOG_DLL_EXPORT QX_DLL_EXPORT_HELPER +#else // _BUILDING_QX_BLOG +#define QX_BLOG_DLL_EXPORT QX_DLL_IMPORT_HELPER +#endif // _BUILDING_QX_BLOG + +#ifdef _BUILDING_QX_BLOG +#define QX_REGISTER_HPP_QX_BLOG QX_REGISTER_HPP_EXPORT_DLL +#define QX_REGISTER_CPP_QX_BLOG QX_REGISTER_CPP_EXPORT_DLL +#else // _BUILDING_QX_BLOG +#define QX_REGISTER_HPP_QX_BLOG QX_REGISTER_HPP_IMPORT_DLL +#define QX_REGISTER_CPP_QX_BLOG QX_REGISTER_CPP_IMPORT_DLL +#endif // _BUILDING_QX_BLOG + +#endif // _QX_BLOG_EXPORT_H_ diff --git a/test/qxBlogRestApi/include/precompiled.h b/test/qxBlogRestApi/include/precompiled.h new file mode 100644 index 0000000..0d8b994 --- /dev/null +++ b/test/qxBlogRestApi/include/precompiled.h @@ -0,0 +1,8 @@ +#ifndef _QX_BLOG_PRECOMPILED_HEADER_H_ +#define _QX_BLOG_PRECOMPILED_HEADER_H_ + +#include + +#include "export.h" + +#endif // _QX_BLOG_PRECOMPILED_HEADER_H_ diff --git a/test/qxBlogRestApi/qt/moc/.gitignore b/test/qxBlogRestApi/qt/moc/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogRestApi/qt/moc/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogRestApi/qt/rcc/documents/cert_qxorm_ca.pem b/test/qxBlogRestApi/qt/rcc/documents/cert_qxorm_ca.pem new file mode 100644 index 0000000..e0eb24e --- /dev/null +++ b/test/qxBlogRestApi/qt/rcc/documents/cert_qxorm_ca.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIEyjCCA7KgAwIBAgIJAOwBaVFFv3nHMA0GCSqGSIb3DQEBCwUAMIGeMQswCQYD +VQQGEwJGUjEUMBIGA1UECBMLTW9udHBlbGxpZXIxFDASBgNVBAcTC0JhaWxsYXJn +dWVzMQ4wDAYDVQQKEwVReE9ybTEhMB8GA1UECxMYUXhPcm0gYW5kIFF4RW50aXR5 +RWRpdG9yMQ4wDAYDVQQDEwVReE9ybTEgMB4GCSqGSIb3DQEJARYRY29udGFjdEBx +eG9ybS5jb20wHhcNMTkwMzA5MTMxNTU2WhcNMjQwMzA3MTMxNTU2WjCBnjELMAkG +A1UEBhMCRlIxFDASBgNVBAgTC01vbnRwZWxsaWVyMRQwEgYDVQQHEwtCYWlsbGFy +Z3VlczEOMAwGA1UEChMFUXhPcm0xITAfBgNVBAsTGFF4T3JtIGFuZCBReEVudGl0 +eUVkaXRvcjEOMAwGA1UEAxMFUXhPcm0xIDAeBgkqhkiG9w0BCQEWEWNvbnRhY3RA +cXhvcm0uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4AJ2A+3Q +3CrVM7ptfaXczoHPP4Nvedr+bYRqGzvgrK83nmdtIVDZBAUFLBaVqs4ry3RPsFat +YbKcsMynKuefMngUHTpOW2EXvlouiPzFDYM6+VlggiWRUpC9Ofd7h/Gph0ShTpqH +4Be20m4+tsai6dk0UO91AGnHyg4QMeGW1EbY+YSv6eX84uKcfNikZNYGGiVc27ru +UnQz92e+upnhaFN30NW7eTsnAxDBOr9q0PHEpJbkNk+o4gMPjDt1NOddRk7Cp3f6 +Uz/+eUO+AzWpZRfSPfQy4+YY7gP3r9rZmLf2AGoRw7aYiTWvObLjPB/SA1Dwaoc2 +O2xpKJD6MuAYswIDAQABo4IBBzCCAQMwHQYDVR0OBBYEFDCJhMbpmZK21Te5Bgei +56xDxXADMIHTBgNVHSMEgcswgciAFDCJhMbpmZK21Te5Bgei56xDxXADoYGkpIGh +MIGeMQswCQYDVQQGEwJGUjEUMBIGA1UECBMLTW9udHBlbGxpZXIxFDASBgNVBAcT +C0JhaWxsYXJndWVzMQ4wDAYDVQQKEwVReE9ybTEhMB8GA1UECxMYUXhPcm0gYW5k +IFF4RW50aXR5RWRpdG9yMQ4wDAYDVQQDEwVReE9ybTEgMB4GCSqGSIb3DQEJARYR +Y29udGFjdEBxeG9ybS5jb22CCQDsAWlRRb95xzAMBgNVHRMEBTADAQH/MA0GCSqG +SIb3DQEBCwUAA4IBAQBvJCXV+eJ2oSRblCNjB2kGjt99Zx+sKLXXuzMs1a+3Dn34 +n5h4UIXM7T7VtcU0b5M/ifaaftp7JZZvI1RF5ohFUC2iM826EPbCKiVgUJupWnTr +w5cY+Ac3coN4QFHbDvBAY0duuxUar2UmzhDZa6oOPs5ND+nw0YA5GBpjsG0uzsFA +ixyPQb24K280mwjxyOvo+/z1Ou6DvGIxriGuwZiJKLInnb6KrV6YRci7aWR0weMn +kZVBavtLevzk8h/zPZCaYb9wYhX2sJ5chfQVQHrr3betC0VeOxziKZem7c+oNVB4 +xTdaOZirtHk7BqJAhEJRXSX6DTXkiFVWohL6LNCf +-----END CERTIFICATE----- diff --git a/test/qxBlogRestApi/qt/rcc/documents/cert_qxorm_server.crt b/test/qxBlogRestApi/qt/rcc/documents/cert_qxorm_server.crt new file mode 100644 index 0000000..6fc082f --- /dev/null +++ b/test/qxBlogRestApi/qt/rcc/documents/cert_qxorm_server.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEEjCCAvqgAwIBAgIJALC9lov7amDkMA0GCSqGSIb3DQEBCwUAMIGeMQswCQYD +VQQGEwJGUjEUMBIGA1UECBMLTW9udHBlbGxpZXIxFDASBgNVBAcTC0JhaWxsYXJn +dWVzMQ4wDAYDVQQKEwVReE9ybTEhMB8GA1UECxMYUXhPcm0gYW5kIFF4RW50aXR5 +RWRpdG9yMQ4wDAYDVQQDEwVReE9ybTEgMB4GCSqGSIb3DQEJARYRY29udGFjdEBx +eG9ybS5jb20wHhcNMTkwMzA5MTMyMTE4WhcNMjQwMzA3MTMyMTE4WjCBnjELMAkG +A1UEBhMCRlIxFDASBgNVBAgTC01vbnRwZWxsaWVyMRQwEgYDVQQHEwtCYWlsbGFy +Z3VlczEOMAwGA1UEChMFUXhPcm0xITAfBgNVBAsTGFF4T3JtIGFuZCBReEVudGl0 +eUVkaXRvcjEOMAwGA1UEAxMFUXhPcm0xIDAeBgkqhkiG9w0BCQEWEWNvbnRhY3RA +cXhvcm0uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5gXL5XXa +aFW0LYspMhGpZpFzhGGMbwbm/+lpV1VZH7qoPBaMaxioO62vJFGthersbcWoIq6g +XuiclP63i0iPe0J5lzXic973posoNw0yCPQiIFpwSd2fNW7tq9mDahzDiAUbI9U3 +R7wgdirAzsQmumJ4j2Dnfdal4modEjnQKtH7AF5U9fNAdBayvG4p4iPg2AXzUkhR +iyfIpIMct/5d6vjPQEewO1HYocMYp2K4P65XOtJ9AHE+0543ualvoUGmtG+x+CWg +/g8uidGHhgWL1AfsHFrXZaP5B1Y3xhcN3tJNDg6emHs5SEcbhp2W2CkdpDn/nqX3 +NbQJobkeM7d4RQIDAQABo1EwTzAfBgNVHSMEGDAWgBQwiYTG6ZmSttU3uQYHoues +Q8VwAzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE8DAUBgNVHREEDTALgglxeG9ybS5j +b20wDQYJKoZIhvcNAQELBQADggEBAFY5aWMu2g7D63IigrtXPiFGsnkOSZn5p95z +Q2xacbGdq//NeWrkuzUkaYH6z9rdtVWe93kmU/pT6neT6ttVAjmfvzzgm6IRF4PT +xnKKGfyV+woorSuQlSxYogHtgOCHw7FSSgykqYVLQkPeAEWzwOrTpBAWsWYqKFLO +oBAo7XYdfxXg0zM5kuvDk3vKHH0Rc25o5QpD4BCEqxaSN/p3dSPGtM3AwSqDLyEI +vDQNiTR5slqxTAv1nKd1CuZSAES+ceHmt6q04ESnwRz7pCMOF6N5iAvRHkbIR2DY +vsmWZwhTmCbHWUhS0b8GMoL+MPIbB3n2jq4rbeRo0vPx+ZUHkk0= +-----END CERTIFICATE----- diff --git a/test/qxBlogRestApi/qt/rcc/documents/cert_qxorm_server.key b/test/qxBlogRestApi/qt/rcc/documents/cert_qxorm_server.key new file mode 100644 index 0000000..5084923 --- /dev/null +++ b/test/qxBlogRestApi/qt/rcc/documents/cert_qxorm_server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEA5gXL5XXaaFW0LYspMhGpZpFzhGGMbwbm/+lpV1VZH7qoPBaM +axioO62vJFGthersbcWoIq6gXuiclP63i0iPe0J5lzXic973posoNw0yCPQiIFpw +Sd2fNW7tq9mDahzDiAUbI9U3R7wgdirAzsQmumJ4j2Dnfdal4modEjnQKtH7AF5U +9fNAdBayvG4p4iPg2AXzUkhRiyfIpIMct/5d6vjPQEewO1HYocMYp2K4P65XOtJ9 +AHE+0543ualvoUGmtG+x+CWg/g8uidGHhgWL1AfsHFrXZaP5B1Y3xhcN3tJNDg6e +mHs5SEcbhp2W2CkdpDn/nqX3NbQJobkeM7d4RQIDAQABAoIBABmoH9wSTzBADv7d +fNTW33xPUQE9GDryHCYDVPqmzHAbk9RP942ijHFLqDN24NIFKCX+XAiyTbUIYR7F +Ypeomkh3W7SgEAg4oIr1DqFFWYViJcRSEiBLFg0RuHToqB3X439yglp219mqhDxp +/GOTwXYcsbHcbzW8EXi1uVRlGnEMELLf5FeJS0ZfavogcC+zUqrvIemMs9ltkktM +I6ABje3WxsJgOafb1uleUr9y04Fgf9CWt76FdPJucl2dBjj+kzxs9JnQw5cYC0v4 +ISK/lbnddbLaqeZ52ebEH70LAc+4uSAYEEfoodEt1STIxCAGOH7gepcJLt0PQkO5 +x4TJJqkCgYEA/PLhwy9v1AR/zAZ/JSJe5ahgMbQZy7Yt4fYWq3+yKTWO8DcTO2oL +ik2J05xZO3J2723ZeD3VEI7U/U9e7cxrW8CyxM3N2khj6kWglrSFQXWK7AaMpcDQ +pE8+G8Vb/1mwJSIh+CdiVlx0HaKLF0tyGTcEQlM/Fi+YQdYJUTjNKVsCgYEA6Mwe +HIv4yLENtq2wdgAnhBt7j91oQfUlMjmmZ2i5is9xseOg4PU5LmsDi01hfuVPdTXg +JHIFVLmiv2vIhEg8nVr4xou437giHNUyO5dpcMVWSG00N6ChuomEGFHh1FxH4N8Q +tUqc2nPtGxOQMKfg9CN+HEmEykCEucDCP3o59t8CgYBfYxAz1M2s/E1dGQ4VaNK0 +dDxiQfKAWaEP1wdRgQQkeif++iXsf97+NqNFA2669j1XqOsbZuXUKqizJZ08u5u4 +rAA+KwX3zNiw3bTjYAvoVXUf8JBVaVLEw4U+X7yDnMTKHAcHV4LD4nV/P26ISFEs +pQycHlxp92TJXveAg5UKhQKBgFmsjwjWDj5YnXy6CVhbFN25rG4tjoShxvNC68u3 +tok3AYfRtF4TeDReOp5Fb4HvGR2AZFgFp/IMFBoCjdoO7XJ74YqxtcRP7KwIn/H+ +kDpFecgqcMvgz4vIfx7TI9emuHVx18DN/f/UWghtD8T+VhQVO/Xvh/xUwAvBqdHD +5mInAoGARe2+SN/zZiAdjCULMpAcrwKkqbniKemeg90yFsfusWe0Sm60W4y+7x+U +BFFV9cqRd/vcNo/s5K+JMgLlGngEDgeAokvXwdkQeX4Loy9eHaFriraxZEAhDNCE +CGN/Ek40PNfnk5KmD5UXg2+NAYRpsILkLJytBHajiGAoSxScTsM= +-----END RSA PRIVATE KEY----- diff --git a/test/qxBlogRestApi/qt/rcc/documents/jquery.js b/test/qxBlogRestApi/qt/rcc/documents/jquery.js new file mode 100644 index 0000000..4d9b3a2 --- /dev/null +++ b/test/qxBlogRestApi/qt/rcc/documents/jquery.js @@ -0,0 +1,2 @@ +/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b="3.3.1",w=function(e,t){return new w.fn.init(e,t)},T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/\s*$/g;function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&&(o=J.access(e),a=J.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n1&&"string"==typeof y&&!h.checkClone&&je.test(y))return e.each(function(i){var o=e.eq(i);v&&(t[0]=y.call(this,i,o.html())),Re(o,t,n,r)});if(p&&(i=xe(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=w.map(ye(i,"script"),He)).length;f")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ye(s),r=0,i=(o=ye(e)).length;r0&&ve(a,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[J.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[J.expando]=void 0}n[K.expando]&&(n[K.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ie(this,e,!0)},remove:function(e){return Ie(this,e)},text:function(e){return z(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(We.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=G(t),u=Xe.test(t),l=e.style;if(u||(t=Je(s)),a=w.cssHooks[t]||w.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ue(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=G(t);return Xe.test(t)||(t=Je(s)),(a=w.cssHooks[t]||w.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Ve&&(i=Ve[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!ze.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ue,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=$e(e),a="border-box"===w.css(e,"boxSizing",!1,o),s=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),s&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Ke(e,n,s)}}}),w.cssHooks.marginLeft=_e(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Ke)}),w.fn.extend({css:function(e,t){return z(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;a1)}});function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}w.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(w.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[w.cssProps[e.prop]]&&!w.cssHooks[e.prop]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=tt.prototype.init,w.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,w.fx.interval),w.fx.tick())}function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&N(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(M);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=ht[t]||w.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}});var gt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return z(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function vt(e){return(e.match(M)||[]).join(" ")}function mt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,mt(this)))});if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,mt(this)))});if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,mt(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=xt(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=mt(this))&&J.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":J.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+vt(mt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:vt(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,v=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!wt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),d=w.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!y(i)){for(l=d.delegateType||m,wt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(i.ownerDocument||r)&&v.push(u.defaultView||u.parentWindow||e)}a=0;while((s=v[a++])&&!t.isPropagationStopped())h=s,t.type=a>1?l:d.bindType||m,(p=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&Y(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),n)||!Y(i)||c&&g(i[m])&&!y(i)&&((u=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,Tt),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,Tt),w.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,t);i||r.addEventListener(e,n,!0),J.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,t)-1;i?J.access(r,t,i):(r.removeEventListener(e,n,!0),J.remove(r,t))}}});var Ct=e.location,Et=Date.now(),kt=/\?/;w.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+t),n};var St=/\[\]$/,Dt=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||St.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Dt,"\r\n")}}):{name:t.name,value:n.replace(Dt,"\r\n")}}).get()}});var qt=/%20/g,Lt=/#.*$/,Ht=/([?&])_=[^&]*/,Ot=/^(.*?):[ \t]*([^\r\n]*)$/gm,Pt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Mt=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Bt=r.createElement("a");Bt.href=Ct.href;function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(M)||[];if(g(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!0,w.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Pt.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,w.ajaxSettings),t):zt(w.ajaxSettings,e)},ajaxPrefilter:Ft(It),ajaxTransport:Ft(Wt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=w.ajaxSetup({},n),g=h.context||h,y=h.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),m=w.Callbacks("once memory"),x=h.statusCode||{},b={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s){s={};while(t=Ot.exec(a))s[t[1].toLowerCase()]=t[2]}t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(v.promise(E),h.url=((t||h.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(M)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Bt.protocol+"//"+Bt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=w.param(h.data,h.traditional)),_t(It,h,n,E),c)return E;(f=w.event&&h.global)&&0==w.active++&&w.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Mt.test(h.type),o=h.url.replace(Lt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(qt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Ht,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(w.lastModified[o]&&E.setRequestHeader("If-Modified-Since",w.lastModified[o]),w.etag[o]&&E.setRequestHeader("If-None-Match",w.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+$t+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=_t(Wt,h,n,E)){if(E.readyState=1,f&&y.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Xt(h,E,r)),b=Ut(h,b,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(w.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(w.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=b.state,p=b.data,l=!(d=b.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?v.resolveWith(g,[p,C,E]):v.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&y.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(y.trigger("ajaxComplete",[E,h]),--w.active||w.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Gt=w.ajaxSettings.xhr();h.cors=!!Gt&&"withCredentials"in Gt,h.ajax=Gt=!!Gt,w.ajaxTransport(function(t){var n,r;if(h.cors||Gt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,o){t=w(" + + +

    Welcome to QxOrm HTTP Server - qxBlogRestApi example project

    + + + + + + + + + + + + + + + + + +
    + + + +
    + + + +

    QxOrm, QxEntityEditor +
    + +
    +

    + +
    + + + diff --git a/test/qxBlogRestApi/qt/rcc/documents/test_http_server.qml b/test/qxBlogRestApi/qt/rcc/documents/test_http_server.qml new file mode 100644 index 0000000..20e5bc1 --- /dev/null +++ b/test/qxBlogRestApi/qt/rcc/documents/test_http_server.qml @@ -0,0 +1,40 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Styles 1.0 + +Item { + width: 500 + height: 130 + Column { + anchors.fill: parent + spacing: 10 + anchors.leftMargin: 5 + anchors.rightMargin: 5 + Label { + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + text: " " + } + Label { + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + text: "!!! Test QxOrm QxRestApi module from a web page using QxHttpServer module !!!" + font.bold: true + color: "blue" + } + Label { + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + text: "Please open your web browser to this URL : http://localhost:9642/files/test_http_server.html" + } + Label { + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + text: "Note : closing this QML window means stopping QxOrm HTTP server too." + } + } +} diff --git a/test/qxBlogRestApi/qt/rcc/documents/test_http_server_qt6.qml b/test/qxBlogRestApi/qt/rcc/documents/test_http_server_qt6.qml new file mode 100644 index 0000000..8401bb0 --- /dev/null +++ b/test/qxBlogRestApi/qt/rcc/documents/test_http_server_qt6.qml @@ -0,0 +1,39 @@ +import QtQuick +import QtQuick.Controls + +Item { + width: 500 + height: 130 + Column { + anchors.fill: parent + spacing: 10 + anchors.leftMargin: 5 + anchors.rightMargin: 5 + Label { + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + text: " " + } + Label { + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + text: "!!! Test QxOrm QxRestApi module from a web page using QxHttpServer module !!!" + font.bold: true + color: "blue" + } + Label { + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + text: "Please open your web browser to this URL : http://localhost:9642/files/test_http_server.html" + } + Label { + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + text: "Note : closing this QML window means stopping QxOrm HTTP server too." + } + } +} diff --git a/test/qxBlogRestApi/qt/rcc/documents/test_rest_api.qml b/test/qxBlogRestApi/qt/rcc/documents/test_rest_api.qml new file mode 100644 index 0000000..79399a2 --- /dev/null +++ b/test/qxBlogRestApi/qt/rcc/documents/test_rest_api.qml @@ -0,0 +1,503 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Styles 1.0 + +Item { + width: 1000 + height: 700 + Row { + anchors.fill: parent + spacing: 10 + anchors.leftMargin: 5 + anchors.rightMargin: 5 + Column { + width: ((parent.width - 10) * 0.5) + height: (parent.height) + spacing: 10 + Label { + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + text: "JSON Request" + font.bold: true + color: "blue" + } + TextArea { + id: txtRequest + anchors.left: parent.left + anchors.right: parent.right + height: (parent.height * 0.4) + wrapMode: TextEdit.NoWrap + text: "" + } + Button { + anchors.left: parent.left + anchors.right: parent.right + text: "Send JSON request" + height: 45 + onClicked: onSendRequest() + style: ButtonStyle { + label: Text { + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + text: control.text + font.pointSize: 12 + font.bold: true + color: "blue" + } + } + } + Label { + anchors.left: parent.left + anchors.right: parent.right + text: "Here are some request examples :" + font.italic: true + color: "blue" + } + ScrollView { + width: parent.width + height: (parent.height * 0.4) + ListView { + id: lstRequestExample + anchors.fill: parent + flickableDirection: Flickable.VerticalFlick + boundsBehavior: Flickable.StopAtBounds + model: ListModel { + id: modelRequestExample + ListElement { key: "get_meta_data"; txt: " - get project meta-data (all classes)" } + ListElement { key: "fetch_all_blogs"; txt: " - fetch all blogs (as list)" } + ListElement { key: "fetch_all_blogs_as_collection"; txt: " - fetch all blogs (as collection key/value)" } + ListElement { key: "fetch_all_blogs_with_relationships"; txt: " - fetch all blogs with relationships (several levels)" } + ListElement { key: "fetch_all_blogs_with_relationships_output_format"; txt: " - fetch all blogs with relationships (several levels) and define output format" } + ListElement { key: "fetch_blog_by_id"; txt: " - fetch a single blog by id" } + ListElement { key: "fetch_blog_by_id_columns"; txt: " - fetch some columns of a single blog by id" } + ListElement { key: "fetch_list_of_blog_by_id"; txt: " - fetch list of blogs by id" } + ListElement { key: "fetch_list_of_blog_by_id_output_format"; txt: " - fetch list of blogs by id and define output format" } + ListElement { key: "fetch_authors_by_query"; txt: " - fetch authors using a query (only female)" } + ListElement { key: "fetch_authors_by_query_with_relationships"; txt: " - fetch authors with relationships using a query" } + ListElement { key: "fetch_authors_by_query_with_relationships_output_format"; txt: " - fetch authors with relationships using a query and define output format" } + ListElement { key: "insert_blog"; txt: " - insert a blog" } + ListElement { key: "insert_list_of_blog"; txt: " - insert list of blogs" } + ListElement { key: "insert_author"; txt: " - insert an author" } + ListElement { key: "insert_category"; txt: " - insert a category" } + ListElement { key: "update_blog"; txt: " - update a blog" } + ListElement { key: "update_blog_columns"; txt: " - update some columns of blog" } + ListElement { key: "update_author"; txt: " - update an author" } + ListElement { key: "update_list_of_author"; txt: " - update list of authors" } + ListElement { key: "update_category"; txt: " - update a category" } + ListElement { key: "save_blog"; txt: " - save (insert or update) a blog" } + ListElement { key: "save_list_of_blog"; txt: " - save (insert or update) list of blogs" } + ListElement { key: "save_blog_recursive"; txt: " - save (insert or update) a blog with relationships (recursively)" } + ListElement { key: "save_blog_recursive_insert"; txt: " - save (insert optimized) a blog with relationships (recursively)" } + ListElement { key: "exist_blog"; txt: " - check if a blog id exist" } + ListElement { key: "exist_list_of_blog"; txt: " - check if list of blogs id exist" } + ListElement { key: "exist_author"; txt: " - check if an author id exist" } + ListElement { key: "validate_blog"; txt: " - validate a blog instance" } + ListElement { key: "count_all_blog"; txt: " - count all blogs" } + ListElement { key: "count_author_with_query"; txt: " - count authors using a query" } + ListElement { key: "count_blog_with_query_and_relationships"; txt: " - count blogs using a query and relationships" } + ListElement { key: "delete_blog_by_id"; txt: " - delete a blog by id" } + ListElement { key: "delete_list_of_blog_by_id"; txt: " - delete list of blogs by id" } + ListElement { key: "delete_author_by_query"; txt: " - delete some authors using a query" } + ListElement { key: "delete_all_comment"; txt: " - delete all comments" } + ListElement { key: "call_custom_query"; txt: " - call custom database query or stored procedure" } + ListElement { key: "call_entity_function"; txt: " - call entity static function with signature : static QJsonValue myEntity::myFct(const QJsonValue & request)" } + ListElement { key: "several_requests_in_array"; txt: " - build several requests in array to send only 1 call to server (executed in a transaction commit/rollback)" } + } + delegate: Rectangle { + width: parent.width + height: 20 + color: ((index == lstRequestExample.currentIndex) ? "lightblue" : (((index % 2) == 0) ? "white" : "papayawhip")) + Column { Text { text: txt } } + MouseArea { + anchors.fill: parent + onClicked: lstRequestExample.currentIndex = index + } + } + highlight: Rectangle { color: "lightblue" } + onCurrentItemChanged: onRequestExampleSelected(modelRequestExample.get(lstRequestExample.currentIndex)) + Component.onCompleted : currentIndex = -1 + focus: true + } + } + } + Column { + width: ((parent.width - 10) * 0.5) + height: (parent.height) + spacing: 10 + Label { + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + text: "JSON Response" + font.bold: true + color: "blue" + } + TextArea { + id: txtResponse + anchors.left: parent.left + anchors.right: parent.right + height: (parent.height * 0.9) + wrapMode: TextEdit.NoWrap + text: "" + } + } + } + + function onSendRequest() { + try { + console.log("onSendRequest() called"); + qxRestApi.clearAll(); + var response = qxRestApi.processRequest(txtRequest.text); + response = JSON.parse(response); + txtResponse.text = JSON.stringify(response, null, 3); + } + catch(exc) { + txtResponse.text = "An error occurred processing request : " + exc; + } + } + + function onRequestExampleSelected(objRequestExample) { + txtResponse.text = ""; + if (! objRequestExample) { return; } + console.log("onRequestExampleSelected() : " + objRequestExample.key + " selected"); + var request = buildRequestExample(objRequestExample); + txtRequest.text = JSON.stringify(request, null, 3); + } + + function buildRequestExample(objRequestExample) { + var request = { }; + request.request_id = createGUID(); + if (objRequestExample.key == "get_meta_data") { + request.action = "get_meta_data"; + request.entity = "*"; + } + else if (objRequestExample.key == "fetch_all_blogs") { + request.action = "fetch_all"; + request.entity = "blog"; + } + else if (objRequestExample.key == "fetch_all_blogs_as_collection") { + request.action = "fetch_all"; + request.entity = "blog"; + request.data = [ { key: "", value: "" } ] + } + else if (objRequestExample.key == "fetch_all_blogs_with_relationships") { + request.action = "fetch_all"; + request.entity = "blog"; + request.relations = [ "*->*" ]; + } + else if (objRequestExample.key == "fetch_all_blogs_with_relationships_output_format") { + request.action = "fetch_all"; + request.entity = "blog"; + request.relations = [ " { blog_text }", "author_id { name, birthdate }", "list_comment { comment_text } -> blog_id -> * <..._my_alias_suffix>" ]; + request.output_format = [ "{ blog_text }", "author_id { name, birthdate }", "list_comment { comment_text } -> blog_id -> *" ]; + } + else if (objRequestExample.key == "fetch_blog_by_id") { + request.action = "fetch_by_id"; + request.entity = "blog"; + request.data = { blog_id: 1 }; + } + else if (objRequestExample.key == "fetch_blog_by_id_columns") { + request.action = "fetch_by_id"; + request.entity = "blog"; + request.data = { blog_id: 1 }; + request.columns = [ "blog_text", "date_creation" ]; + } + else if (objRequestExample.key == "fetch_list_of_blog_by_id") { + request.action = "fetch_by_id"; + request.entity = "blog"; + request.data = [ { blog_id: 1 }, { blog_id: 2 }, { blog_id: 3 } ]; + } + else if (objRequestExample.key == "fetch_list_of_blog_by_id_output_format") { + request.action = "fetch_by_id"; + request.entity = "blog"; + request.data = [ { blog_id: 1 }, { blog_id: 2 } ]; + request.relations = [ "{ blog_text }", "author_id { name, birthdate }", "list_comment { comment_text }" ]; + request.output_format = [ "{ blog_text }", "author_id { name, birthdate }", "list_comment { comment_text }" ]; + } + else if (objRequestExample.key == "fetch_authors_by_query") { + request.action = "fetch_by_query"; + request.entity = "author"; + request.query = { + sql: "WHERE author.sex = :sex", + params: [ { key: ":sex", value: 1, type: "in" } ] + }; + } + else if (objRequestExample.key == "fetch_authors_by_query_with_relationships") { + request.action = "fetch_by_query"; + request.entity = "author"; + request.query = { + sql: "WHERE author.sex = :sex", + params: [ { key: ":sex", value: 1, type: "in" } ] + }; + request.relations = [ "*" ]; + } + else if (objRequestExample.key == "fetch_authors_by_query_with_relationships_output_format") { + request.action = "fetch_by_query"; + request.entity = "author"; + request.query = { + sql: "WHERE author.sex = :sex", + params: [ { key: ":sex", value: 1, type: "in" } ] + }; + request.relations = [ "*" ]; + request.output_format = [ "{ birthdate, name }", "list_blog { blog_text, date_creation }" ]; + } + else if (objRequestExample.key == "insert_blog") { + request.action = "insert"; + request.entity = "blog"; + request.data = { + blog_text: "this is a new blog from QxOrm REST API !", + date_creation: "2018-01-30T12:42:01", + author_id: "author_id_2" + }; + } + else if (objRequestExample.key == "insert_list_of_blog") { + request.action = "insert"; + request.entity = "blog"; + request.data = [ + { + blog_text: "new blog from QxOrm REST API !", + date_creation: "2018-01-30T12:42:01", + author_id: "author_id_2" + }, + { + blog_text: "another blog from QxOrm REST API !", + date_creation: "2016-06-12T08:33:12", + author_id: "author_id_1" + } + ]; + } + else if (objRequestExample.key == "insert_author") { + request.action = "insert"; + request.entity = "author"; + request.data = { + author_id: "author_id_from_rest_api", + birthdate: "1978-05-11", + name: "new author created by QxOrm REST API", + sex: 1 + }; + } + else if (objRequestExample.key == "insert_category") { + request.action = "insert"; + request.entity = "category"; + request.data = { + description: "category from REST API", + name: "new_category" + }; + } + else if (objRequestExample.key == "update_blog") { + request.action = "update"; + request.entity = "blog"; + request.data = { + blog_id: 1, + blog_text: "modify blog from QxOrm REST API", + date_creation: "2013-11-25T09:56:33", + author_id: "author_id_1" + }; + } + else if (objRequestExample.key == "update_blog_columns") { + request.action = "update"; + request.entity = "blog"; + request.data = { + blog_id: 2, + blog_text: "modify blog from QxOrm REST API", + date_creation: "2013-11-25T09:56:33" + }; + request.columns = [ "blog_text", "date_creation" ]; + } + else if (objRequestExample.key == "update_author") { + request.action = "update"; + request.entity = "author"; + request.data = { + author_id: "author_id_from_rest_api", + birthdate: "1992-11-03", + name: "modify author from QxOrm REST API", + sex: 0 + }; + } + else if (objRequestExample.key == "update_list_of_author") { + request.action = "update"; + request.entity = "author"; + request.data = [ + { + author_id: "author_id_from_rest_api", + birthdate: "1992-11-03", + name: "modify author from QxOrm REST API", + sex: 0 + }, + { + author_id: "author_id_1", + birthdate: "1978-12-25", + name: "modify another author from QxOrm REST API", + sex: 2 + } + ]; + } + else if (objRequestExample.key == "update_category") { + request.action = "update"; + request.entity = "category"; + request.data = { + category_id: 1, + description: "category modified by REST API", + name: "modif_category" + }; + } + else if (objRequestExample.key == "save_blog") { + request.action = "save"; + request.entity = "blog"; + request.data = { + blog_id: 1, + blog_text: "modify blog from QxOrm REST API", + date_creation: "2013-11-25T09:56:33", + author_id: "author_id_1" + }; + } + else if (objRequestExample.key == "save_list_of_blog") { + request.action = "save"; + request.entity = "blog"; + request.data = [ + { + blog_id: 1, + blog_text: "save blog from QxOrm REST API !", + date_creation: "2018-01-30T12:42:01", + author_id: "author_id_2" + }, + { + blog_text: "save another blog from QxOrm REST API !", + date_creation: "2016-06-12T08:33:12", + author_id: "author_id_1" + } + ]; + } + else if (objRequestExample.key == "save_blog_recursive") { + request.action = "save"; + request.entity = "blog"; + request.data = { + blog_id: 1, + blog_text: "save recursive blog from QxOrm REST API", + date_creation: "2013-11-25T09:56:33", + author_id: { + author_id: "author_id_1", + birthdate: "1965-07-21", + name: "save recursive author from QxOrm REST API", + sex: 0 + } + }; + request.save_mode = "check_insert_or_update"; + } + else if (objRequestExample.key == "save_blog_recursive_insert") { + request.action = "save"; + request.entity = "blog"; + request.data = { + blog_text: "save recursive - new blog from QxOrm REST API", + date_creation: "2013-11-25T09:56:33", + author_id: { + author_id: "author_id_save_recursive", + birthdate: "1965-07-21", + name: "save recursive (insert only) author from QxOrm REST API", + sex: 0 + } + }; + request.save_mode = "insert_only"; + } + else if (objRequestExample.key == "exist_blog") { + request.action = "exist"; + request.entity = "blog"; + request.data = { blog_id: 1 }; + } + else if (objRequestExample.key == "exist_list_of_blog") { + request.action = "exist"; + request.entity = "blog"; + request.data = [ { blog_id: 1 }, { blog_id: 999 }, { blog_id: 3 } ]; + } + else if (objRequestExample.key == "exist_author") { + request.action = "exist"; + request.entity = "author"; + request.data = { author_id: "author_id_2" }; + } + else if (objRequestExample.key == "validate_blog") { + request.action = "validate"; + request.entity = "blog"; + request.data = { blog_id: 9999, blog_text: "" }; + } + else if (objRequestExample.key == "count_all_blog") { + request.action = "count"; + request.entity = "blog"; + } + else if (objRequestExample.key == "count_author_with_query") { + request.action = "count"; + request.entity = "author"; + request.query = { + sql: "WHERE author.sex = :sex", + params: [ { key: ":sex", value: 1 } ] + }; + } + else if (objRequestExample.key == "count_blog_with_query_and_relationships") { + request.action = "count"; + request.entity = "blog"; + request.query = { + sql: "WHERE author_alias.sex = :sex", + params: [ { key: ":sex", value: 1 } ] + }; + request.relations = [ "author_id { sex }" ]; + } + else if (objRequestExample.key == "delete_blog_by_id") { + request.action = "delete_by_id"; + request.entity = "blog"; + request.data = { blog_id: 4 }; + } + else if (objRequestExample.key == "delete_list_of_blog_by_id") { + request.action = "delete_by_id"; + request.entity = "blog"; + request.data = [ { blog_id: 3 }, { blog_id: 2 } ]; + } + else if (objRequestExample.key == "delete_author_by_query") { + request.action = "delete_by_query"; + request.entity = "author"; + request.query = { + sql: "WHERE author.sex = :sex", + params: [ { key: ":sex", value: 1 } ] + }; + } + else if (objRequestExample.key == "delete_all_comment") { + request.action = "delete_all"; + request.entity = "comment"; + } + else if (objRequestExample.key == "call_custom_query") { + request.action = "call_custom_query"; + request.query = { + sql: "INSERT INTO author (author_id, name, birthdate, sex) VALUES (:author_id, :name, :birthdate, :sex)", + params: [ + { key: ":author_id", value: "author_id_custom_query" }, + { key: ":name", value: "new author inserted by custom query" }, + { key: ":birthdate", value: "20190215" }, + { key: ":sex", value: 2 } + ] + }; + } + else if (objRequestExample.key == "call_entity_function") { + request.action = "call_entity_function"; + request.entity = "blog"; + request.fct = "helloWorld"; + request.data = { param1: "test", param2: "static fct call" }; + } + else if (objRequestExample.key == "several_requests_in_array") { + request = [ buildRequestExample(modelRequestExample.get(0)), + buildRequestExample(modelRequestExample.get(1)), + buildRequestExample(modelRequestExample.get(2)), + buildRequestExample(modelRequestExample.get(3)) ]; + } + else { + request.error = ""; + } + return request; + } + + function createGUID() { + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) { + var r = Math.random() * 16 | 0, v = c == "x" ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + } +} diff --git a/test/qxBlogRestApi/qt/rcc/documents/test_rest_api_qt6.qml b/test/qxBlogRestApi/qt/rcc/documents/test_rest_api_qt6.qml new file mode 100644 index 0000000..7dba4df --- /dev/null +++ b/test/qxBlogRestApi/qt/rcc/documents/test_rest_api_qt6.qml @@ -0,0 +1,513 @@ +import QtQuick +import QtQuick.Controls + +Item { + width: 1000 + height: 700 + Row { + anchors.fill: parent + spacing: 10 + anchors.leftMargin: 5 + anchors.rightMargin: 5 + Column { + width: ((parent.width - 10) * 0.5) + height: (parent.height) + spacing: 10 + Label { + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + text: "JSON Request" + font.bold: true + color: "blue" + } + Flickable { + anchors.left: parent.left + anchors.right: parent.right + height: (parent.height * 0.4) + TextArea.flickable: TextArea { + id: txtRequest + wrapMode: TextEdit.NoWrap + text: "" + background: Rectangle { + border.width: 1 + } + } + ScrollBar.vertical: ScrollBar { } + ScrollBar.horizontal: ScrollBar { } + } + Button { + anchors.left: parent.left + anchors.right: parent.right + height: 45 + onClicked: onSendRequest() + contentItem: Label { + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + text: "Send JSON request" + font.pointSize: 12 + font.bold: true + color: "blue" + } + } + Label { + anchors.left: parent.left + anchors.right: parent.right + text: "Here are some request examples :" + font.italic: true + color: "blue" + } + ScrollView { + width: parent.width + height: (parent.height * 0.4) + ListView { + id: lstRequestExample + anchors.fill: parent + flickableDirection: Flickable.VerticalFlick + boundsBehavior: Flickable.StopAtBounds + model: ListModel { + id: modelRequestExample + ListElement { key: "get_meta_data"; txt: " - get project meta-data (all classes)" } + ListElement { key: "fetch_all_blogs"; txt: " - fetch all blogs (as list)" } + ListElement { key: "fetch_all_blogs_as_collection"; txt: " - fetch all blogs (as collection key/value)" } + ListElement { key: "fetch_all_blogs_with_relationships"; txt: " - fetch all blogs with relationships (several levels)" } + ListElement { key: "fetch_all_blogs_with_relationships_output_format"; txt: " - fetch all blogs with relationships (several levels) and define output format" } + ListElement { key: "fetch_blog_by_id"; txt: " - fetch a single blog by id" } + ListElement { key: "fetch_blog_by_id_columns"; txt: " - fetch some columns of a single blog by id" } + ListElement { key: "fetch_list_of_blog_by_id"; txt: " - fetch list of blogs by id" } + ListElement { key: "fetch_list_of_blog_by_id_output_format"; txt: " - fetch list of blogs by id and define output format" } + ListElement { key: "fetch_authors_by_query"; txt: " - fetch authors using a query (only female)" } + ListElement { key: "fetch_authors_by_query_with_relationships"; txt: " - fetch authors with relationships using a query" } + ListElement { key: "fetch_authors_by_query_with_relationships_output_format"; txt: " - fetch authors with relationships using a query and define output format" } + ListElement { key: "insert_blog"; txt: " - insert a blog" } + ListElement { key: "insert_list_of_blog"; txt: " - insert list of blogs" } + ListElement { key: "insert_author"; txt: " - insert an author" } + ListElement { key: "insert_category"; txt: " - insert a category" } + ListElement { key: "update_blog"; txt: " - update a blog" } + ListElement { key: "update_blog_columns"; txt: " - update some columns of blog" } + ListElement { key: "update_author"; txt: " - update an author" } + ListElement { key: "update_list_of_author"; txt: " - update list of authors" } + ListElement { key: "update_category"; txt: " - update a category" } + ListElement { key: "save_blog"; txt: " - save (insert or update) a blog" } + ListElement { key: "save_list_of_blog"; txt: " - save (insert or update) list of blogs" } + ListElement { key: "save_blog_recursive"; txt: " - save (insert or update) a blog with relationships (recursively)" } + ListElement { key: "save_blog_recursive_insert"; txt: " - save (insert optimized) a blog with relationships (recursively)" } + ListElement { key: "exist_blog"; txt: " - check if a blog id exist" } + ListElement { key: "exist_list_of_blog"; txt: " - check if list of blogs id exist" } + ListElement { key: "exist_author"; txt: " - check if an author id exist" } + ListElement { key: "validate_blog"; txt: " - validate a blog instance" } + ListElement { key: "count_all_blog"; txt: " - count all blogs" } + ListElement { key: "count_author_with_query"; txt: " - count authors using a query" } + ListElement { key: "count_blog_with_query_and_relationships"; txt: " - count blogs using a query and relationships" } + ListElement { key: "delete_blog_by_id"; txt: " - delete a blog by id" } + ListElement { key: "delete_list_of_blog_by_id"; txt: " - delete list of blogs by id" } + ListElement { key: "delete_author_by_query"; txt: " - delete some authors using a query" } + ListElement { key: "delete_all_comment"; txt: " - delete all comments" } + ListElement { key: "call_custom_query"; txt: " - call custom database query or stored procedure" } + ListElement { key: "call_entity_function"; txt: " - call entity static function with signature : static QJsonValue myEntity::myFct(const QJsonValue & request)" } + ListElement { key: "several_requests_in_array"; txt: " - build several requests in array to send only 1 call to server (executed in a transaction commit/rollback)" } + } + delegate: Rectangle { + width: parent.width + height: 20 + color: ((index == lstRequestExample.currentIndex) ? "lightblue" : (((index % 2) == 0) ? "white" : "papayawhip")) + Column { Text { text: txt } } + MouseArea { + anchors.fill: parent + onClicked: lstRequestExample.currentIndex = index + } + } + highlight: Rectangle { color: "lightblue" } + onCurrentItemChanged: onRequestExampleSelected(modelRequestExample.get(lstRequestExample.currentIndex)) + Component.onCompleted : currentIndex = -1 + focus: true + } + } + } + Column { + width: ((parent.width - 10) * 0.5) + height: (parent.height) + spacing: 10 + Label { + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + text: "JSON Response" + font.bold: true + color: "blue" + } + Flickable { + anchors.left: parent.left + anchors.right: parent.right + height: (parent.height * 0.9) + TextArea.flickable: TextArea { + id: txtResponse + wrapMode: TextEdit.NoWrap + text: "" + background: Rectangle { + border.width: 1 + } + } + ScrollBar.vertical: ScrollBar { } + ScrollBar.horizontal: ScrollBar { } + } + } + } + + function onSendRequest() { + try { + console.log("onSendRequest() called"); + qxRestApi.clearAll(); + var response = qxRestApi.processRequest(txtRequest.text); + response = JSON.parse(response); + txtResponse.text = JSON.stringify(response, null, 3); + } + catch(exc) { + txtResponse.text = "An error occurred processing request : " + exc; + } + } + + function onRequestExampleSelected(objRequestExample) { + txtResponse.text = ""; + if (! objRequestExample) { return; } + console.log("onRequestExampleSelected() : " + objRequestExample.key + " selected"); + var request = buildRequestExample(objRequestExample); + txtRequest.text = JSON.stringify(request, null, 3); + } + + function buildRequestExample(objRequestExample) { + var request = { }; + request.request_id = createGUID(); + if (objRequestExample.key == "get_meta_data") { + request.action = "get_meta_data"; + request.entity = "*"; + } + else if (objRequestExample.key == "fetch_all_blogs") { + request.action = "fetch_all"; + request.entity = "blog"; + } + else if (objRequestExample.key == "fetch_all_blogs_as_collection") { + request.action = "fetch_all"; + request.entity = "blog"; + request.data = [ { key: "", value: "" } ] + } + else if (objRequestExample.key == "fetch_all_blogs_with_relationships") { + request.action = "fetch_all"; + request.entity = "blog"; + request.relations = [ "*->*" ]; + } + else if (objRequestExample.key == "fetch_all_blogs_with_relationships_output_format") { + request.action = "fetch_all"; + request.entity = "blog"; + request.relations = [ " { blog_text }", "author_id { name, birthdate }", "list_comment { comment_text } -> blog_id -> * <..._my_alias_suffix>" ]; + request.output_format = [ "{ blog_text }", "author_id { name, birthdate }", "list_comment { comment_text } -> blog_id -> *" ]; + } + else if (objRequestExample.key == "fetch_blog_by_id") { + request.action = "fetch_by_id"; + request.entity = "blog"; + request.data = { blog_id: 1 }; + } + else if (objRequestExample.key == "fetch_blog_by_id_columns") { + request.action = "fetch_by_id"; + request.entity = "blog"; + request.data = { blog_id: 1 }; + request.columns = [ "blog_text", "date_creation" ]; + } + else if (objRequestExample.key == "fetch_list_of_blog_by_id") { + request.action = "fetch_by_id"; + request.entity = "blog"; + request.data = [ { blog_id: 1 }, { blog_id: 2 }, { blog_id: 3 } ]; + } + else if (objRequestExample.key == "fetch_list_of_blog_by_id_output_format") { + request.action = "fetch_by_id"; + request.entity = "blog"; + request.data = [ { blog_id: 1 }, { blog_id: 2 } ]; + request.relations = [ "{ blog_text }", "author_id { name, birthdate }", "list_comment { comment_text }" ]; + request.output_format = [ "{ blog_text }", "author_id { name, birthdate }", "list_comment { comment_text }" ]; + } + else if (objRequestExample.key == "fetch_authors_by_query") { + request.action = "fetch_by_query"; + request.entity = "author"; + request.query = { + sql: "WHERE author.sex = :sex", + params: [ { key: ":sex", value: 1, type: "in" } ] + }; + } + else if (objRequestExample.key == "fetch_authors_by_query_with_relationships") { + request.action = "fetch_by_query"; + request.entity = "author"; + request.query = { + sql: "WHERE author.sex = :sex", + params: [ { key: ":sex", value: 1, type: "in" } ] + }; + request.relations = [ "*" ]; + } + else if (objRequestExample.key == "fetch_authors_by_query_with_relationships_output_format") { + request.action = "fetch_by_query"; + request.entity = "author"; + request.query = { + sql: "WHERE author.sex = :sex", + params: [ { key: ":sex", value: 1, type: "in" } ] + }; + request.relations = [ "*" ]; + request.output_format = [ "{ birthdate, name }", "list_blog { blog_text, date_creation }" ]; + } + else if (objRequestExample.key == "insert_blog") { + request.action = "insert"; + request.entity = "blog"; + request.data = { + blog_text: "this is a new blog from QxOrm REST API !", + date_creation: "2018-01-30T12:42:01", + author_id: "author_id_2" + }; + } + else if (objRequestExample.key == "insert_list_of_blog") { + request.action = "insert"; + request.entity = "blog"; + request.data = [ + { + blog_text: "new blog from QxOrm REST API !", + date_creation: "2018-01-30T12:42:01", + author_id: "author_id_2" + }, + { + blog_text: "another blog from QxOrm REST API !", + date_creation: "2016-06-12T08:33:12", + author_id: "author_id_1" + } + ]; + } + else if (objRequestExample.key == "insert_author") { + request.action = "insert"; + request.entity = "author"; + request.data = { + author_id: "author_id_from_rest_api", + birthdate: "1978-05-11", + name: "new author created by QxOrm REST API", + sex: 1 + }; + } + else if (objRequestExample.key == "insert_category") { + request.action = "insert"; + request.entity = "category"; + request.data = { + description: "category from REST API", + name: "new_category" + }; + } + else if (objRequestExample.key == "update_blog") { + request.action = "update"; + request.entity = "blog"; + request.data = { + blog_id: 1, + blog_text: "modify blog from QxOrm REST API", + date_creation: "2013-11-25T09:56:33", + author_id: "author_id_1" + }; + } + else if (objRequestExample.key == "update_blog_columns") { + request.action = "update"; + request.entity = "blog"; + request.data = { + blog_id: 2, + blog_text: "modify blog from QxOrm REST API", + date_creation: "2013-11-25T09:56:33" + }; + request.columns = [ "blog_text", "date_creation" ]; + } + else if (objRequestExample.key == "update_author") { + request.action = "update"; + request.entity = "author"; + request.data = { + author_id: "author_id_from_rest_api", + birthdate: "1992-11-03", + name: "modify author from QxOrm REST API", + sex: 0 + }; + } + else if (objRequestExample.key == "update_list_of_author") { + request.action = "update"; + request.entity = "author"; + request.data = [ + { + author_id: "author_id_from_rest_api", + birthdate: "1992-11-03", + name: "modify author from QxOrm REST API", + sex: 0 + }, + { + author_id: "author_id_1", + birthdate: "1978-12-25", + name: "modify another author from QxOrm REST API", + sex: 2 + } + ]; + } + else if (objRequestExample.key == "update_category") { + request.action = "update"; + request.entity = "category"; + request.data = { + category_id: 1, + description: "category modified by REST API", + name: "modif_category" + }; + } + else if (objRequestExample.key == "save_blog") { + request.action = "save"; + request.entity = "blog"; + request.data = { + blog_id: 1, + blog_text: "modify blog from QxOrm REST API", + date_creation: "2013-11-25T09:56:33", + author_id: "author_id_1" + }; + } + else if (objRequestExample.key == "save_list_of_blog") { + request.action = "save"; + request.entity = "blog"; + request.data = [ + { + blog_id: 1, + blog_text: "save blog from QxOrm REST API !", + date_creation: "2018-01-30T12:42:01", + author_id: "author_id_2" + }, + { + blog_text: "save another blog from QxOrm REST API !", + date_creation: "2016-06-12T08:33:12", + author_id: "author_id_1" + } + ]; + } + else if (objRequestExample.key == "save_blog_recursive") { + request.action = "save"; + request.entity = "blog"; + request.data = { + blog_id: 1, + blog_text: "save recursive blog from QxOrm REST API", + date_creation: "2013-11-25T09:56:33", + author_id: { + author_id: "author_id_1", + birthdate: "1965-07-21", + name: "save recursive author from QxOrm REST API", + sex: 0 + } + }; + request.save_mode = "check_insert_or_update"; + } + else if (objRequestExample.key == "save_blog_recursive_insert") { + request.action = "save"; + request.entity = "blog"; + request.data = { + blog_text: "save recursive - new blog from QxOrm REST API", + date_creation: "2013-11-25T09:56:33", + author_id: { + author_id: "author_id_save_recursive", + birthdate: "1965-07-21", + name: "save recursive (insert only) author from QxOrm REST API", + sex: 0 + } + }; + request.save_mode = "insert_only"; + } + else if (objRequestExample.key == "exist_blog") { + request.action = "exist"; + request.entity = "blog"; + request.data = { blog_id: 1 }; + } + else if (objRequestExample.key == "exist_list_of_blog") { + request.action = "exist"; + request.entity = "blog"; + request.data = [ { blog_id: 1 }, { blog_id: 999 }, { blog_id: 3 } ]; + } + else if (objRequestExample.key == "exist_author") { + request.action = "exist"; + request.entity = "author"; + request.data = { author_id: "author_id_2" }; + } + else if (objRequestExample.key == "validate_blog") { + request.action = "validate"; + request.entity = "blog"; + request.data = { blog_id: 9999, blog_text: "" }; + } + else if (objRequestExample.key == "count_all_blog") { + request.action = "count"; + request.entity = "blog"; + } + else if (objRequestExample.key == "count_author_with_query") { + request.action = "count"; + request.entity = "author"; + request.query = { + sql: "WHERE author.sex = :sex", + params: [ { key: ":sex", value: 1 } ] + }; + } + else if (objRequestExample.key == "count_blog_with_query_and_relationships") { + request.action = "count"; + request.entity = "blog"; + request.query = { + sql: "WHERE author_alias.sex = :sex", + params: [ { key: ":sex", value: 1 } ] + }; + request.relations = [ "author_id { sex }" ]; + } + else if (objRequestExample.key == "delete_blog_by_id") { + request.action = "delete_by_id"; + request.entity = "blog"; + request.data = { blog_id: 4 }; + } + else if (objRequestExample.key == "delete_list_of_blog_by_id") { + request.action = "delete_by_id"; + request.entity = "blog"; + request.data = [ { blog_id: 3 }, { blog_id: 2 } ]; + } + else if (objRequestExample.key == "delete_author_by_query") { + request.action = "delete_by_query"; + request.entity = "author"; + request.query = { + sql: "WHERE author.sex = :sex", + params: [ { key: ":sex", value: 1 } ] + }; + } + else if (objRequestExample.key == "delete_all_comment") { + request.action = "delete_all"; + request.entity = "comment"; + } + else if (objRequestExample.key == "call_custom_query") { + request.action = "call_custom_query"; + request.query = { + sql: "INSERT INTO author (author_id, name, birthdate, sex) VALUES (:author_id, :name, :birthdate, :sex)", + params: [ + { key: ":author_id", value: "author_id_custom_query" }, + { key: ":name", value: "new author inserted by custom query" }, + { key: ":birthdate", value: "20190215" }, + { key: ":sex", value: 2 } + ] + }; + } + else if (objRequestExample.key == "call_entity_function") { + request.action = "call_entity_function"; + request.entity = "blog"; + request.fct = "helloWorld"; + request.data = { param1: "test", param2: "static fct call" }; + } + else if (objRequestExample.key == "several_requests_in_array") { + request = [ buildRequestExample(modelRequestExample.get(0)), + buildRequestExample(modelRequestExample.get(1)), + buildRequestExample(modelRequestExample.get(2)), + buildRequestExample(modelRequestExample.get(3)) ]; + } + else { + request.error = ""; + } + return request; + } + + function createGUID() { + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) { + var r = Math.random() * 16 | 0, v = c == "x" ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + } +} diff --git a/test/qxBlogRestApi/qt/rcc/images/.gitignore b/test/qxBlogRestApi/qt/rcc/images/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogRestApi/qt/rcc/images/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogRestApi/qt/rcc/qxBlogRestApi.qrc b/test/qxBlogRestApi/qt/rcc/qxBlogRestApi.qrc new file mode 100644 index 0000000..96735df --- /dev/null +++ b/test/qxBlogRestApi/qt/rcc/qxBlogRestApi.qrc @@ -0,0 +1,14 @@ + + + documents/test_rest_api.qml + documents/test_http_server.qml + documents/test_rest_api_qt6.qml + documents/test_http_server_qt6.qml + documents/test_http_server.html + documents/logo_qxorm_and_qxee.png + documents/jquery.js + documents/cert_qxorm_ca.pem + documents/cert_qxorm_server.crt + documents/cert_qxorm_server.key + + diff --git a/test/qxBlogRestApi/qt/rcc/src/.gitignore b/test/qxBlogRestApi/qt/rcc/src/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogRestApi/qt/rcc/src/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogRestApi/qxBlog.pro b/test/qxBlogRestApi/qxBlog.pro new file mode 100644 index 0000000..b435b9d --- /dev/null +++ b/test/qxBlogRestApi/qxBlog.pro @@ -0,0 +1,44 @@ +include(../../QxOrm.pri) + +TEMPLATE = app +DEFINES += _BUILDING_QX_BLOG +INCLUDEPATH += ../../../QxOrm/include/ +DESTDIR = ../../../QxOrm/test/_bin/ +LIBS += -L"../../../QxOrm/lib" + +!contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) { +PRECOMPILED_HEADER = ./include/precompiled.h +} # !contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) + +greaterThan(QT_MAJOR_VERSION, 4) { +QT += widgets +QT += quick +QT += qml +} else { +QT += declarative +} + +macx:CONFIG-=app_bundle + +CONFIG(debug, debug|release) { +TARGET = qxBlogRestApid +LIBS += -l"QxOrmd" +} else { +TARGET = qxBlogRestApi +LIBS += -l"QxOrm" +} # CONFIG(debug, debug|release) + +HEADERS += ./include/precompiled.h +HEADERS += ./include/export.h +HEADERS += ./include/author.h +HEADERS += ./include/blog.h +HEADERS += ./include/category.h +HEADERS += ./include/comment.h + +SOURCES += ./src/author.cpp +SOURCES += ./src/blog.cpp +SOURCES += ./src/category.cpp +SOURCES += ./src/comment.cpp +SOURCES += ./src/main.cpp + +RESOURCES += ./qt/rcc/qxBlogRestApi.qrc diff --git a/test/qxBlogRestApi/release/.gitignore b/test/qxBlogRestApi/release/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxBlogRestApi/release/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxBlogRestApi/src/author.cpp b/test/qxBlogRestApi/src/author.cpp new file mode 100644 index 0000000..868ed1d --- /dev/null +++ b/test/qxBlogRestApi/src/author.cpp @@ -0,0 +1,29 @@ +#include "../include/precompiled.h" + +#include "../include/author.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(author) +QX_PERSISTABLE_CPP(author) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& author::m_id, "author_id"); + + t.data(& author::m_name, "name"); + t.data(& author::m_birthdate, "birthdate"); + t.data(& author::m_sex, "sex"); + + t.relationOneToMany(& author::m_blogX, "list_blog", "author_id"); + + t.fct_0(std::mem_fn(& author::age), "age"); // using std::mem_fn() here is just a workaround for an issue with some versions of MSVC, it is not required with a full compliant C++11 compiler (http://stackoverflow.com/questions/23778883/vs2013-stdfunction-with-member-function) +}} + +int author::age() const +{ + if (! m_birthdate.isValid()) { return -1; } + return (QDate::currentDate().year() - m_birthdate.year()); +} diff --git a/test/qxBlogRestApi/src/blog.cpp b/test/qxBlogRestApi/src/blog.cpp new file mode 100644 index 0000000..6af3c13 --- /dev/null +++ b/test/qxBlogRestApi/src/blog.cpp @@ -0,0 +1,43 @@ +#include "../include/precompiled.h" + +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(blog) +QX_PERSISTABLE_CPP(blog) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& blog::m_id, "blog_id"); + + t.data(& blog::m_text, "blog_text"); + t.data(& blog::m_dt_creation, "date_creation"); + + t.relationManyToOne(& blog::m_author, "author_id"); + t.relationOneToMany(& blog::m_commentX, "list_comment", "blog_id"); + t.relationManyToMany(& blog::m_categoryX, "list_category", "category_blog", "blog_id", "category_id"); + +#ifndef _QX_NO_JSON + t.fctStatic_1(& blog::helloWorld, "helloWorld"); +#endif // _QX_NO_JSON + + QxValidatorX * pAllValidator = t.getAllValidator(); + pAllValidator->add_CustomValidator(std::mem_fn(& blog::isValid)); // using std::mem_fn() here is just a workaround for an issue with some versions of MSVC, it is not required with a full compliant C++11 compiler (http://stackoverflow.com/questions/23778883/vs2013-stdfunction-with-member-function) +}} + +#ifndef _QX_NO_JSON +QJsonValue blog::helloWorld(const QJsonValue & request) +{ + QJsonObject response; + response.insert("request", request); + response.insert("response", QString("Hello World !")); + return response; +} +#endif // _QX_NO_JSON + +void blog::isValid(qx::QxInvalidValueX & invalidValues) +{ + if (m_text.isEmpty()) { invalidValues.insert("'blog_text' property cannot be empty"); } +} diff --git a/test/qxBlogRestApi/src/category.cpp b/test/qxBlogRestApi/src/category.cpp new file mode 100644 index 0000000..0396cf2 --- /dev/null +++ b/test/qxBlogRestApi/src/category.cpp @@ -0,0 +1,20 @@ +#include "../include/precompiled.h" + +#include "../include/category.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(category) +QX_PERSISTABLE_CPP(category) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& category::m_id, "category_id"); + + t.data(& category::m_name, "name"); + t.data(& category::m_desc, "description"); + + t.relationManyToMany(& category::m_blogX, "list_blog", "category_blog", "category_id", "blog_id"); +}} diff --git a/test/qxBlogRestApi/src/comment.cpp b/test/qxBlogRestApi/src/comment.cpp new file mode 100644 index 0000000..2357579 --- /dev/null +++ b/test/qxBlogRestApi/src/comment.cpp @@ -0,0 +1,20 @@ +#include "../include/precompiled.h" + +#include "../include/comment.h" +#include "../include/blog.h" + +#include + +QX_REGISTER_CPP_QX_BLOG(comment) +QX_PERSISTABLE_CPP(comment) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& comment::m_id, "comment_id"); + + t.data(& comment::m_text, "comment_text"); + t.data(& comment::m_dt_create, "date_creation"); + + t.relationManyToOne(& comment::m_blog, "blog_id"); +}} diff --git a/test/qxBlogRestApi/src/main.cpp b/test/qxBlogRestApi/src/main.cpp new file mode 100644 index 0000000..d938bf7 --- /dev/null +++ b/test/qxBlogRestApi/src/main.cpp @@ -0,0 +1,490 @@ +#include "../include/precompiled.h" + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#include +#include +#include +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#include +#include +#include +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + +#include + +#include "../include/blog.h" +#include "../include/author.h" +#include "../include/comment.h" +#include "../include/category.h" + +#include + +int main(int argc, char * argv[]) +{ + // Qt application + QApplication app(argc, argv); + QFile::remove("./qxBlogRestApi.sqlite"); + + // Parameters to connect to database + qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE"); + qx::QxSqlDatabase::getSingleton()->setDatabaseName("./qxBlogRestApi.sqlite"); + qx::QxSqlDatabase::getSingleton()->setHostName("localhost"); + qx::QxSqlDatabase::getSingleton()->setUserName("root"); + qx::QxSqlDatabase::getSingleton()->setPassword(""); + qx::QxSqlDatabase::getSingleton()->setFormatSqlQueryBeforeLogging(true); + + // Only for debug purpose : assert if invalid offset detected fetching a relation + qx::QxSqlDatabase::getSingleton()->setVerifyOffsetRelation(true); + + // Create all tables in database + QSqlError daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + + // Create a list of 3 author + author_ptr author_1; author_1.reset(new author()); + author_ptr author_2; author_2.reset(new author()); + author_ptr author_3; author_3.reset(new author()); + + author_1->m_id = "author_id_1"; author_1->m_name = "author_1"; + author_1->m_sex = author::male; author_1->m_birthdate = QDate::currentDate(); + author_2->m_id = "author_id_2"; author_2->m_name = "author_2"; + author_2->m_sex = author::female; author_2->m_birthdate = QDate::currentDate(); + author_3->m_id = "author_id_3"; author_3->m_name = "author_3"; + author_3->m_sex = author::female; author_3->m_birthdate = QDate::currentDate(); + + list_author authorX; + authorX.insert(author_1->m_id, author_1); + authorX.insert(author_2->m_id, author_2); + authorX.insert(author_3->m_id, author_3); + + // Insert list of 3 author into database + daoError = qx::dao::insert(authorX); + qAssert(qx::dao::count() == 3); + + // Clone author 2 : 'author_id_2' + author_ptr author_clone = qx::clone(* author_2); + qAssert(author_clone->m_id == "author_id_2"); + qAssert(author_clone->m_sex == author::female); + + // Create a query to fetch only female author : 'author_id_2' and 'author_id_3' + qx::QxSqlQuery query("WHERE author.sex = :sex"); + query.bind(":sex", author::female); + + list_author list_of_female_author; + daoError = qx::dao::fetch_by_query(query, list_of_female_author); + qAssert(list_of_female_author.count() == 2); + + // Dump list of female author (xml serialization) + qx::dump(list_of_female_author, false); + qx::dump(list_of_female_author, true); + + // Create 3 categories + category_ptr category_1 = category_ptr(new category()); + category_ptr category_2 = category_ptr(new category()); + category_ptr category_3 = category_ptr(new category()); + + category_1->m_name = "category_1"; category_1->m_desc = "desc_1"; + category_2->m_name = "category_2"; category_2->m_desc = "desc_2"; + category_3->m_name = "category_3"; category_3->m_desc = "desc_3"; + + { // Create a scope to destroy temporary connexion to database + + // Open a transaction to database + QSqlDatabase db = qx::QxSqlDatabase::getDatabase(); + bool bCommit = db.transaction(); + + // Insert 3 categories into database, use 'db' parameter for the transaction + daoError = qx::dao::insert(category_1, (& db)); bCommit = (bCommit && ! daoError.isValid()); + daoError = qx::dao::insert(category_2, (& db)); bCommit = (bCommit && ! daoError.isValid()); + daoError = qx::dao::insert(category_3, (& db)); bCommit = (bCommit && ! daoError.isValid()); + + qAssert(bCommit); + qAssert(category_1->m_id != 0); + qAssert(category_2->m_id != 0); + qAssert(category_3->m_id != 0); + + // Terminate transaction => commit or rollback if there is error + if (bCommit) { db.commit(); } + else { db.rollback(); } + + } // End of scope : 'db' is destroyed + + // Create a blog with the class name (factory) + qx::any blog_any = qx::create("blog"); + blog_ptr blog_1; + try { blog_1 = qx::any_cast(blog_any); } + catch (...) { blog_1.reset(new blog()); } + blog_1->m_text = "blog_text_1"; + blog_1->m_dt_creation = QDateTime::currentDateTime(); + blog_1->m_author = author_1; + + // Insert 'blog_1' into database with 'save()' method + daoError = qx::dao::save(blog_1); + + // Modify 'blog_1' properties and save into database + blog_1->m_text = "update blog_text_1"; + blog_1->m_author = author_2; + daoError = qx::dao::save(blog_1); + + // Add 2 comments to 'blog_1' + comment_ptr comment_1; comment_1.reset(new comment()); + comment_ptr comment_2; comment_2.reset(new comment()); + + comment_1->m_text = "comment_1 text"; + comment_1->m_dt_create = QDateTime::currentDateTime(); + comment_1->m_blog = blog_1; + comment_2->m_text = "comment_2 text"; + comment_2->m_dt_create = QDateTime::currentDateTime(); + comment_2->m_blog = blog_1; + + daoError = qx::dao::insert(comment_1); + daoError = qx::dao::insert(comment_2); + qAssert(qx::dao::count() == 2); + + // Add 2 categories to 'blog_1' => must insert into extra-table 'category_blog' + blog_1->m_categoryX.insert(category_1->m_id, category_1); + blog_1->m_categoryX.insert(category_3->m_id, category_3); + daoError = qx::dao::save_with_relation("list_category", blog_1); + + // Fetch blog into a new variable with all relation : 'author', 'comment' and 'category' + blog_ptr blog_tmp; blog_tmp.reset(new blog()); + blog_tmp->m_id = blog_1->m_id; + daoError = qx::dao::fetch_by_id_with_all_relation(blog_tmp); + + qAssert(blog_tmp->m_commentX.count() == 2); + qAssert(blog_tmp->m_categoryX.count() == 2); + qAssert(blog_tmp->m_text == "update blog_text_1"); + qAssert(blog_tmp->m_author && blog_tmp->m_author->m_id == "author_id_2"); + + // Fetch blog into a new variable with many relations using "*->*->*->*" (4 levels of relationships) + blog_tmp.reset(new blog()); + blog_tmp->m_id = blog_1->m_id; + daoError = qx::dao::fetch_by_id_with_relation("*->*->*->*", blog_tmp); + + qAssert(blog_tmp->m_commentX.count() == 2); + qAssert(blog_tmp->m_categoryX.count() == 2); + qAssert(blog_tmp->m_text == "update blog_text_1"); + qAssert(blog_tmp->m_author && blog_tmp->m_author->m_id == "author_id_2"); + + // Dump 'blog_tmp' result from database (xml serialization) + qx::dump(blog_tmp, false); + qx::dump(blog_tmp, true); + + // Fetch relations defining columns to fetch with syntax { col_1, col_2, etc... } + list_blog lstBlogComplexRelation; + daoError = qx::dao::fetch_all_with_relation(QStringList() << "{ blog_text }" << "author_id { name, birthdate }" << "list_comment { comment_text } -> blog_id -> *", lstBlogComplexRelation); + qx::dump(lstBlogComplexRelation); + qAssert(lstBlogComplexRelation.size() > 0); + qAssert(lstBlogComplexRelation[0]->m_text != ""); // Fetched + qAssert(lstBlogComplexRelation[0]->m_dt_creation.isNull()); // Not fetched + qAssert(lstBlogComplexRelation[0]->m_author->m_sex == author::unknown); // Not fetched + qAssert(lstBlogComplexRelation[0]->m_author->m_name != ""); // Fetched + qAssert(lstBlogComplexRelation[0]->m_commentX.size() > 0); + qAssert(lstBlogComplexRelation[0]->m_commentX[0]->m_dt_create.isNull()); // Not fetched + qAssert(lstBlogComplexRelation[0]->m_commentX[0]->m_text != ""); // Fetched + qAssert(lstBlogComplexRelation[0]->m_commentX[0]->m_blog); + + // Fetch relations defining columns to remove before fetching with syntax -{ col_1, col_2, etc... } + list_blog lstBlogComplexRelation2; + daoError = qx::dao::fetch_all_with_relation(QStringList() << "-{ blog_text }" << "author_id -{ name, birthdate }" << "list_comment -{ comment_text } -> blog_id -> *", lstBlogComplexRelation2); + qx::dump(lstBlogComplexRelation2); + qAssert(lstBlogComplexRelation2.size() > 0); + qAssert(lstBlogComplexRelation2[0]->m_text == ""); // Not fetched + qAssert(! lstBlogComplexRelation2[0]->m_dt_creation.isNull()); // Fetched + qAssert(lstBlogComplexRelation2[0]->m_author->m_sex != author::unknown); // Fetched + qAssert(lstBlogComplexRelation2[0]->m_author->m_name == ""); // Not fetched + qAssert(lstBlogComplexRelation2[0]->m_commentX.size() > 0); + qAssert(! lstBlogComplexRelation2[0]->m_commentX[0]->m_dt_create.isNull()); // Fetched + qAssert(lstBlogComplexRelation2[0]->m_commentX[0]->m_text == ""); // Not fetched + qAssert(lstBlogComplexRelation2[0]->m_commentX[0]->m_blog); + +#ifndef _QX_NO_JSON + // Custom JSON serialization process + QString customJsonFull = qx::serialization::json::to_string(blog_tmp, 1); + QString customJsonFiltered = qx::serialization::json::to_string(blog_tmp, 1, "filter: { blog_text } | author_id { name, birthdate } | list_comment { comment_text } -> blog_id -> *"); + qDebug("[QxOrm] custom JSON serialization process (full) : \n%s", qPrintable(customJsonFull)); + qDebug("[QxOrm] custom JSON serialization process (filtered) : \n%s", qPrintable(customJsonFiltered)); + + blog_ptr blogFromJsonFull; blogFromJsonFull.reset(new blog()); + blog_ptr blogFromJsonFiltered; blogFromJsonFiltered.reset(new blog()); + qx::serialization::json::from_string(blogFromJsonFull, customJsonFull, 1); + qx::serialization::json::from_string(blogFromJsonFiltered, customJsonFull, 1, "filter: { blog_text } | author_id { name, birthdate } | list_comment { comment_text } -> blog_id -> *"); + + qx::dump(blogFromJsonFull); + qAssert(blogFromJsonFull->m_commentX.count() == 2); + qAssert(blogFromJsonFull->m_categoryX.count() == 2); + qAssert(blogFromJsonFull->m_text == "update blog_text_1"); + qAssert(blogFromJsonFull->m_author && blogFromJsonFull->m_author->m_id == "author_id_2"); + + qx::dump(blogFromJsonFiltered); + qAssert(blogFromJsonFiltered->m_text != ""); // Fetched + qAssert(blogFromJsonFiltered->m_dt_creation.isNull()); // Not fetched + qAssert(blogFromJsonFiltered->m_author->m_sex == author::unknown); // Not fetched + qAssert(blogFromJsonFiltered->m_author->m_name != ""); // Fetched + qAssert(blogFromJsonFiltered->m_commentX.size() > 0); + qAssert(blogFromJsonFiltered->m_commentX[0]->m_dt_create.isNull()); // Not fetched + qAssert(blogFromJsonFiltered->m_commentX[0]->m_text != ""); // Fetched + qAssert(blogFromJsonFiltered->m_commentX[0]->m_blog); +#endif // _QX_NO_JSON + + // Fetch relations defining columns to fetch with syntax { col_1, col_2, etc... } + custom table alias using syntax + custom table alias suffix using syntax <..._my_alias_suffix> + list_blog lstBlogComplexRelation3; + daoError = qx::dao::fetch_all_with_relation(QStringList() << " { blog_text }" << "author_id { name, birthdate }" << "list_comment { comment_text } -> blog_id -> * <..._my_alias_suffix>", lstBlogComplexRelation3); + qx::dump(lstBlogComplexRelation3); + qAssert(lstBlogComplexRelation3.size() > 0); + qAssert(lstBlogComplexRelation3[0]->m_text != ""); // Fetched + qAssert(lstBlogComplexRelation3[0]->m_dt_creation.isNull()); // Not fetched + qAssert(lstBlogComplexRelation3[0]->m_author->m_sex == author::unknown); // Not fetched + qAssert(lstBlogComplexRelation3[0]->m_author->m_name != ""); // Fetched + qAssert(lstBlogComplexRelation3[0]->m_commentX.size() > 0); + qAssert(lstBlogComplexRelation3[0]->m_commentX[0]->m_dt_create.isNull()); // Not fetched + qAssert(lstBlogComplexRelation3[0]->m_commentX[0]->m_text != ""); // Fetched + qAssert(lstBlogComplexRelation3[0]->m_commentX[0]->m_blog); + + // Check qx::dao::save_with_relation_recursive() function + daoError = qx::dao::save_with_relation_recursive(blog_tmp); + qAssert(! daoError.isValid()); + daoError = qx::dao::save_with_relation_recursive(blog_tmp, qx::dao::save_mode::e_update_only); + qAssert(! daoError.isValid()); + + // Call 'age()' method with class name and method name (reflexion) + qx_bool bInvokeOk = qx::QxClassX::invoke("author", "age", author_1); + qAssert(bInvokeOk); + + // Check count with relations and filter + long lBlogCountWithRelation = 0; qx_query queryBlogCountWithRelation; + daoError = qx::dao::count_with_relation(lBlogCountWithRelation, QStringList() << "author_id" << "list_comment -> blog_id -> *", queryBlogCountWithRelation); + qAssert(! daoError.isValid() && (lBlogCountWithRelation > 0)); + + // Test 'isDirty()' method + qx::dao::ptr blog_isdirty = qx::dao::ptr(new blog()); + blog_isdirty->m_id = blog_1->m_id; + daoError = qx::dao::fetch_by_id(blog_isdirty); + qAssert(! daoError.isValid() && ! blog_isdirty.isDirty()); + + blog_isdirty->m_text = "blog property 'text' modified => blog is dirty !!!"; + QStringList lstDiff; bool bDirty = blog_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 1) && (lstDiff.at(0) == "blog_text")); + if (bDirty) { qDebug("[QxOrm] test dirty 1 : blog is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + // Update only property 'm_text' of 'blog_isdirty' + daoError = qx::dao::update_optimized(blog_isdirty); + qAssert(! daoError.isValid() && ! blog_isdirty.isDirty()); + qx::dump(blog_isdirty, false); + qx::dump(blog_isdirty, true); + + // Test 'isDirty()' method with a container + typedef qx::dao::ptr< QList > type_lst_author_test_is_dirty; + type_lst_author_test_is_dirty container_isdirty = type_lst_author_test_is_dirty(new QList()); + daoError = qx::dao::fetch_all(container_isdirty); + qAssert(! daoError.isValid() && ! container_isdirty.isDirty() && (container_isdirty->count() == 3)); + + author_ptr author_ptr_dirty = container_isdirty->at(1); + author_ptr_dirty->m_name = "author name modified at index 1 => container is dirty !!!"; + bDirty = container_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 1)); + if (bDirty) { qDebug("[QxOrm] test dirty 2 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + author_ptr_dirty = container_isdirty->at(2); + author_ptr_dirty->m_birthdate = QDate(1998, 03, 06); + bDirty = container_isdirty.isDirty(lstDiff); + qAssert(bDirty && (lstDiff.count() == 2)); + if (bDirty) { qDebug("[QxOrm] test dirty 3 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); } + + // Update only property 'm_name' at position 1, only property 'm_birthdate' at position 2 and nothing at position 0 + daoError = qx::dao::update_optimized(container_isdirty); + qAssert(! daoError.isValid() && ! container_isdirty.isDirty()); + qx::dump(container_isdirty, false); + qx::dump(container_isdirty, true); + + // Fetch only property 'm_dt_creation' of blog + QStringList lstColumns = QStringList() << "date_creation"; + list_blog lst_blog_with_only_date_creation; + daoError = qx::dao::fetch_all(lst_blog_with_only_date_creation, NULL, lstColumns); + qAssert(! daoError.isValid() && (lst_blog_with_only_date_creation.size() > 0)); + if ((lst_blog_with_only_date_creation.size() > 0) && (lst_blog_with_only_date_creation[0].get() != NULL)) + { qAssert(lst_blog_with_only_date_creation[0]->m_text.isEmpty()); } + qx::dump(lst_blog_with_only_date_creation, false); + qx::dump(lst_blog_with_only_date_creation, true); + + // Dump all registered classes into QxOrm context (introspection engine) + qx::QxClassX::dumpAllClasses(); + + // Call a custom SQL query or a stored procedure + qx_query testStoredProc("SELECT * FROM author"); + daoError = qx::dao::call_query(testStoredProc); + qAssert(! daoError.isValid()); + testStoredProc.dumpSqlResult(); + + // Call a custom SQL query or a stored procedure and fetch automatically properties (with a collection of items) + qx_query testStoredProcBis("SELECT * FROM author"); + authorX.clear(); + daoError = qx::dao::execute_query(testStoredProcBis, authorX); + qAssert(! daoError.isValid()); qAssert(authorX.count() > 0); + qx::dump(authorX, false); + qx::dump(authorX, true); + + // Call a custom SQL query or a stored procedure and fetch automatically properties + qx_query testStoredProcThird("SELECT name, category_id FROM category"); + category_ptr category_tmp = category_ptr(new category()); + daoError = qx::dao::execute_query(testStoredProcThird, category_tmp); + qAssert(! daoError.isValid()); qAssert(category_tmp->m_id != 0); + qx::dump(category_tmp, false); + qx::dump(category_tmp, true); + + // Create several blogs + blog_tmp.reset(new blog()); + blog_tmp->m_id = blog_1->m_id; + daoError = qx::dao::fetch_by_id_with_relation("*", blog_tmp); + blog_ptr blog_cloned = qx::clone(* blog_tmp); + daoError = qx::dao::save_with_relation_recursive(blog_cloned, qx::dao::save_mode::e_insert_only); qAssert(! daoError.isValid()); + blog_cloned = qx::clone(* blog_tmp); + daoError = qx::dao::save_with_relation_recursive(blog_cloned, qx::dao::save_mode::e_insert_only); qAssert(! daoError.isValid()); + blog_cloned = qx::clone(* blog_tmp); + daoError = qx::dao::save_with_relation_recursive(blog_cloned, qx::dao::save_mode::e_insert_only); qAssert(! daoError.isValid()); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + + { + qx::QxRestApi api; + + QQuickView qmlView; +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QString sQmlFile = "qrc:/documents/test_rest_api_qt6.qml"; +#else // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QString sQmlFile = "qrc:/documents/test_rest_api.qml"; +#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + + qmlView.rootContext()->setContextProperty("qxRestApi", (& api)); + qmlView.setResizeMode(QQuickView::SizeRootObjectToView); + qmlView.setSource(QUrl(sQmlFile)); + qmlView.show(); + qApp->exec(); + } + +#ifdef _QX_ENABLE_QT_NETWORK + + { + // Just to be sure to have static files on the system + QDir appPath(QDir::currentPath()); appPath.mkdir("files"); + QFile::copy(":/documents/test_http_server.html", appPath.filePath("files/test_http_server.html")); + QFile::copy(":/documents/logo_qxorm_and_qxee.png", appPath.filePath("files/logo_qxorm_and_qxee.png")); + QFile::copy(":/documents/jquery.js", appPath.filePath("files/jquery.js")); + + // HTTP server settings + qx::service::QxConnect * serverSettings = qx::service::QxConnect::getSingleton(); + serverSettings->setKeepAlive(5000); + serverSettings->setCompressData(true); + serverSettings->setThreadCount(50); + serverSettings->setPort(9642); + +#ifndef QT_NO_SSL + if (false) // If you want to test SSL/TLS secured connection, just force this condition to 'true' + { + // Certificates created with this tutorial : https://deliciousbrains.com/ssl-certificate-authority-for-local-https-development/ + QFile::copy(":/documents/cert_qxorm_ca.pem", appPath.filePath("files/cert_qxorm_ca.pem")); + QFile::copy(":/documents/cert_qxorm_server.crt", appPath.filePath("files/cert_qxorm_server.crt")); + QFile::copy(":/documents/cert_qxorm_server.key", appPath.filePath("files/cert_qxorm_server.key")); + + QFile fileCertCA(appPath.filePath("files/cert_qxorm_ca.pem")); + fileCertCA.open(QIODevice::ReadOnly); + QList certCA; certCA << QSslCertificate(fileCertCA.readAll()); + + QFile fileCertServerPublic(appPath.filePath("files/cert_qxorm_server.crt")); + fileCertServerPublic.open(QIODevice::ReadOnly); + QSslCertificate certServerPublic(fileCertServerPublic.readAll()); + + QFile fileCertServerPrivate(appPath.filePath("files/cert_qxorm_server.key")); + fileCertServerPrivate.open(QIODevice::ReadOnly); + QSslKey certServerPrivate(fileCertServerPrivate.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, "qxorm"); + + serverSettings->setSSLEnabled(true); + serverSettings->setSSLCACertificates(certCA); + serverSettings->setSSLLocalCertificate(certServerPublic); + serverSettings->setSSLPrivateKey(certServerPrivate); + } +#endif // QT_NO_SSL + + // Create a QxOrm HTTP server instance + qx::QxHttpServer httpServer; + + // Define all HTTP server routes (dispatcher) to handle requests + // Each callback is executed in a dedicated thread, so QxOrm HTTP server can handle several requests in parallel + httpServer.dispatch("GET", "/files/*", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) { + qx::QxHttpServer::buildResponseStaticFile(request, response, QDir::currentPath(), 5000); + }); + httpServer.dispatch("POST", "/qx", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) { + qx::QxHttpServer::buildResponseQxRestApi(request, response); + + // Not useful here but this is how to get a server session per user + // If this is the first time to access to session, then a cookie is created automatically and attached to the response + // Then each request sent by web browser will contain a cookie with the session id + // The session expires on server side after qx::service::QxConnect::setSessionTimeOut() milliseconds + qx::QxHttpSession_ptr session = qx::QxHttpSessionManager::getSession(request, response); + if (session) { session->set("last_request_per_user", QDateTime::currentDateTime()); } + }); + httpServer.dispatch("GET", "/params//", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) { + response.data() = "Test URL dispatch parameters :\r\n"; + response.data() += " - var1 = " + request.dispatchParams().value("var1").toByteArray() + "\r\n"; + response.data() += " - var2 = " + request.dispatchParams().value("var2").toByteArray() + "\r\n"; + }); + httpServer.dispatch("GET", "/test_big_json", [](qx::QxHttpRequest & request, qx::QxHttpResponse & response) { + // To compare with this benchmark : https://blog.binaryspaceship.com/2017/cpp-rest-api-frameworks-benchmark/ + // This is more a JSON benchmark than HTTP server benchmark (RapidJSON is faster than Qt QJson engine) + QJsonArray arr; Q_UNUSED(request); + for (int i = 0; i < 10000; ++i) + { + QJsonObject item; + item.insert("id", QString::number(i)); + item.insert("name", QString("Hello World")); + item.insert("type", QString("application")); + arr.append(item); + } + QJsonDocument doc(arr); + response.headers().insert("Content-Type", "application/json; charset=utf-8"); + response.data() = doc.toJson(QJsonDocument::Compact); + }); + + // Start HTTP server + httpServer.startServer(); + + // Open default web browser to connect to QxOrm HTTP server instance +#ifndef QT_NO_SSL + if (serverSettings->getSSLEnabled()) { QDesktopServices::openUrl(QUrl("https://localhost:9642/files/test_http_server.html")); } + else { QDesktopServices::openUrl(QUrl("http://localhost:9642/files/test_http_server.html")); } +#else // QT_NO_SSL + QDesktopServices::openUrl(QUrl("http://localhost:9642/files/test_http_server.html")); +#endif // QT_NO_SSL + + QQuickView qmlView; +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QString sQmlFile = "qrc:/documents/test_http_server_qt6.qml"; +#else // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QString sQmlFile = "qrc:/documents/test_http_server.qml"; +#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + + qmlView.rootContext()->setContextProperty("qxHttpServer", (& httpServer)); + qmlView.setResizeMode(QQuickView::SizeRootObjectToView); + qmlView.setSource(QUrl(sQmlFile)); + qmlView.show(); + qApp->exec(); + } + +#else // _QX_ENABLE_QT_NETWORK + + qDebug("[QxOrm] qxBlogRestApi example project : %s", "cannot start QxOrm HTTP server because _QX_ENABLE_QT_NETWORK compilation option is not defined"); + +#endif // _QX_ENABLE_QT_NETWORK + +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + + qDebug("[QxOrm] %s", "qxBlogRestApi example project only works with Qt5 or +"); + +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + + return 0; +} diff --git a/test/qxClientServer/CMakeLists.txt b/test/qxClientServer/CMakeLists.txt new file mode 100644 index 0000000..5a7c01e --- /dev/null +++ b/test/qxClientServer/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.1) + +project(qxClientServer LANGUAGES CXX) + +include(../../QxOrm.cmake) + +if(_QX_ENABLE_QT_NETWORK) + +add_subdirectory(qxService/qxServiceClient) +add_subdirectory(qxService/qxServiceServer) +add_subdirectory(qxClient) +add_subdirectory(qxServer) + +else() # _QX_ENABLE_QT_NETWORK + +message(STATUS "qxClientServer project not loaded because _QX_ENABLE_QT_NETWORK option not enabled (QxOrm QxService module is disabled)") + +endif() # _QX_ENABLE_QT_NETWORK diff --git a/test/qxClientServer/qxClient/CMakeLists.txt b/test/qxClientServer/qxClient/CMakeLists.txt new file mode 100644 index 0000000..ea95b71 --- /dev/null +++ b/test/qxClientServer/qxClient/CMakeLists.txt @@ -0,0 +1,70 @@ +cmake_minimum_required(VERSION 3.1) + +project(qxClient LANGUAGES CXX) + +include(../../../QxOrm.cmake) + +if(_QX_ENABLE_QT_NETWORK) + +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Sql Gui Widgets REQUIRED) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_DEBUG_POSTFIX "d") + +set(HEADERS + ./include/precompiled.h + ./include/export.h + ./include/main_dlg.h + ) + +set(SRCS + ./src/main_dlg.cpp + ./src/main.cpp + ) + +set(UIS + ./qt/ui/qxClient.ui + ) + +if(COMMAND qt_wrap_ui) +qt_wrap_ui(UIS_HDRS ${UIS}) +else() # (COMMAND qt_wrap_ui) +qt5_wrap_ui(UIS_HDRS ${UIS}) +endif() # (COMMAND qt_wrap_ui) + +add_executable(qxClient ${SRCS} ${HEADERS} ${UIS_HDRS}) + +target_compile_definitions(qxClient PRIVATE -D_BUILDING_QX_CLIENT) + +if(COMMAND target_precompile_headers) + target_precompile_headers(qxClient PRIVATE ./include/precompiled.h) +endif() # (COMMAND target_precompile_headers) + +target_link_libraries(qxClient ${QX_LIBRARIES} Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets QxOrm qxServiceClient) + +set_target_properties(qxClient PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ) + +set_target_properties(qxClient PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) + +else() # _QX_ENABLE_QT_NETWORK + +message(STATUS "qxClient project not loaded because _QX_ENABLE_QT_NETWORK option not enabled (QxOrm QxService module is disabled)") + +endif() # _QX_ENABLE_QT_NETWORK diff --git a/test/qxClientServer/qxClient/debug/.gitignore b/test/qxClientServer/qxClient/debug/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxClientServer/qxClient/debug/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxClientServer/qxClient/include/export.h b/test/qxClientServer/qxClient/include/export.h new file mode 100644 index 0000000..9dfb9c5 --- /dev/null +++ b/test/qxClientServer/qxClient/include/export.h @@ -0,0 +1,20 @@ +#ifndef _QX_CLIENT_EXPORT_H_ +#define _QX_CLIENT_EXPORT_H_ + +#include "../../qxService/include/export.h" + +#ifdef _BUILDING_QX_CLIENT +#define QX_CLIENT_DLL_EXPORT QX_DLL_EXPORT_HELPER +#else // _BUILDING_QX_CLIENT +#define QX_CLIENT_DLL_EXPORT QX_DLL_IMPORT_HELPER +#endif // _BUILDING_QX_CLIENT + +#ifdef _BUILDING_QX_CLIENT +#define QX_REGISTER_HPP_QX_CLIENT QX_REGISTER_HPP_EXPORT_DLL +#define QX_REGISTER_CPP_QX_CLIENT QX_REGISTER_CPP_EXPORT_DLL +#else // _BUILDING_QX_CLIENT +#define QX_REGISTER_HPP_QX_CLIENT QX_REGISTER_HPP_IMPORT_DLL +#define QX_REGISTER_CPP_QX_CLIENT QX_REGISTER_CPP_IMPORT_DLL +#endif // _BUILDING_QX_CLIENT + +#endif // _QX_CLIENT_EXPORT_H_ diff --git a/test/qxClientServer/qxClient/include/main_dlg.h b/test/qxClientServer/qxClient/include/main_dlg.h new file mode 100644 index 0000000..cab3f05 --- /dev/null +++ b/test/qxClientServer/qxClient/include/main_dlg.h @@ -0,0 +1,56 @@ +#ifndef _QX_CLIENT_MAIN_DLG_H_ +#define _QX_CLIENT_MAIN_DLG_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include "../include/precompiled.h" // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +#include "../qt/ui/include/ui_qxClient.h" + +#include "../../qxService/include/business_object/user.h" +#include "../../qxService/include/business_object/user_search.h" + +class main_dlg : public QWidget, private Ui::dlgClient +{ + + Q_OBJECT + +private: + + qx::service::QxClientAsync_ptr m_pDateTimeAsync; // To retrieve current server date-time without blocking GUI (async transaction) + +public: + + main_dlg(QWidget * parent = NULL) : QWidget(parent), Ui::dlgClient() { main_dlg::init(); } + virtual ~main_dlg() { ; } + +private: + + void init(); + void updateLastTransactionLog(qx::service::QxTransaction_ptr transaction); + void fillUser(user_ptr user); + user_ptr fileUser(); + +private Q_SLOTS: + + void onClickBtnDateTime(); + void onClickBtnDateTimeAsync(); + void onClickBtnAddUser(); + void onClickBtnUpdateUser(); + void onClickBtnRemoveUser(); + void onClickBtnRemoveAllUsers(); + void onClickBtnFetchUser(); + void onClickBtnGetAllUsers(); + void onClickBtnSearchUsers(); + void onDateTimeAsyncFinished(); + void onUpdateServerConnection(); + +}; + +#endif // _QX_CLIENT_MAIN_DLG_H_ diff --git a/test/qxClientServer/qxClient/include/precompiled.h b/test/qxClientServer/qxClient/include/precompiled.h new file mode 100644 index 0000000..27be75a --- /dev/null +++ b/test/qxClientServer/qxClient/include/precompiled.h @@ -0,0 +1,9 @@ +#ifndef _QX_CLIENT_PRECOMPILED_HEADER_H_ +#define _QX_CLIENT_PRECOMPILED_HEADER_H_ + +#include +#include + +#include "export.h" + +#endif // _QX_CLIENT_PRECOMPILED_HEADER_H_ diff --git a/test/qxClientServer/qxClient/qt/moc/.gitignore b/test/qxClientServer/qxClient/qt/moc/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxClientServer/qxClient/qt/moc/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxClientServer/qxClient/qt/rcc/src/.gitignore b/test/qxClientServer/qxClient/qt/rcc/src/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxClientServer/qxClient/qt/rcc/src/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxClientServer/qxClient/qt/ui/qxClient.ui b/test/qxClientServer/qxClient/qt/ui/qxClient.ui new file mode 100644 index 0000000..bab3b3b --- /dev/null +++ b/test/qxClientServer/qxClient/qt/ui/qxClient.ui @@ -0,0 +1,448 @@ + + + dlgClient + + + + 0 + 0 + 541 + 371 + + + + + 541 + 371 + + + + + 541 + 371 + + + + qxClient + + + + + 230 + 10 + 301 + 51 + + + + Server infos transaction + + + + + 10 + 20 + 121 + 23 + + + + Get Server DateTime + + + Get Server DateTime + + + + + + 140 + 20 + 151 + 23 + + + + Get Server DateTime Async + + + Get Server DateTime Async + + + + + + + 10 + 170 + 521 + 191 + + + + Log last client-server reply-request transaction + + + + + 10 + 20 + 501 + 161 + + + + Qt::ScrollBarAlwaysOn + + + Qt::ScrollBarAlwaysOn + + + QPlainTextEdit::NoWrap + + + true + + + + + + + 10 + 70 + 521 + 91 + + + + User transaction + + + + + 380 + 20 + 61 + 23 + + + + Get All Users + + + Get All + + + + + + 10 + 20 + 51 + 23 + + + + Add User + + + Add + + + + + + 70 + 20 + 61 + 23 + + + + Update User + + + Update + + + + + + 140 + 20 + 61 + 23 + + + + Remove User + + + Remove + + + + + + 210 + 20 + 81 + 23 + + + + Remove All Users + + + Remove All + + + + + + 300 + 20 + 71 + 23 + + + + Fetch User + + + Fetch + + + + + + 10 + 40 + 501 + 20 + + + + Qt::Horizontal + + + + + + 10 + 60 + 21 + 16 + + + + ID + + + ID : + + + + + + 100 + 60 + 61 + 16 + + + + First Name + + + First Name : + + + + + + 240 + 60 + 61 + 16 + + + + Last Name + + + Last Name : + + + + + + 380 + 60 + 51 + 16 + + + + Birthdate + + + Birthdate : + + + + + + 30 + 60 + 61 + 20 + + + + ID + + + + + + 160 + 60 + 71 + 20 + + + + First Name + + + + + + 300 + 60 + 71 + 20 + + + + Last Name + + + + + + 440 + 60 + 71 + 20 + + + + Birthdate + + + + + + 450 + 20 + 61 + 23 + + + + Search Users + + + Search + + + + + + + 10 + 10 + 211 + 51 + + + + Server connection + + + + + 10 + 20 + 21 + 16 + + + + Ip + + + Ip : + + + + + + 120 + 20 + 31 + 16 + + + + Port + + + Port : + + + + + + 150 + 20 + 51 + 22 + + + + Port + + + 1 + + + 32000 + + + + + + 30 + 20 + 81 + 20 + + + + Ip + + + 127.0.0.1 + + + Qt::AlignCenter + + + + + + + diff --git a/test/qxClientServer/qxClient/qt/ui/src/.gitignore b/test/qxClientServer/qxClient/qt/ui/src/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxClientServer/qxClient/qt/ui/src/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxClientServer/qxClient/qxClient.pro b/test/qxClientServer/qxClient/qxClient.pro new file mode 100644 index 0000000..7d3c986 --- /dev/null +++ b/test/qxClientServer/qxClient/qxClient.pro @@ -0,0 +1,39 @@ +include(../../../QxOrm.pri) + +!contains(DEFINES, _QX_ENABLE_QT_NETWORK) { +error(unable to use QxOrm QxService module : please define _QX_ENABLE_QT_NETWORK compilation option in QxOrm.pri configuration file) +} # !contains(DEFINES, _QX_ENABLE_QT_NETWORK) + +TEMPLATE = app +DEFINES += _BUILDING_QX_CLIENT +INCLUDEPATH += ../../../../QxOrm/include/ +DESTDIR = ../../../../QxOrm/test/_bin/ + +QT += gui +greaterThan(QT_MAJOR_VERSION, 4) { QT += widgets } + +!contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) { +PRECOMPILED_HEADER = ./include/precompiled.h +} # !contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) + +LIBS += -L"../../../../QxOrm/lib" +LIBS += -L"../../../../QxOrm/test/_bin" + +CONFIG(debug, debug|release) { +TARGET = qxClientd +LIBS += -l"QxOrmd" +LIBS += -l"qxServiceClientd" +} else { +TARGET = qxClient +LIBS += -l"QxOrm" +LIBS += -l"qxServiceClient" +} # CONFIG(debug, debug|release) + +HEADERS += ./include/precompiled.h +HEADERS += ./include/export.h +HEADERS += ./include/main_dlg.h + +SOURCES += ./src/main_dlg.cpp +SOURCES += ./src/main.cpp + +FORMS += ./qt/ui/qxClient.ui diff --git a/test/qxClientServer/qxClient/release/.gitignore b/test/qxClientServer/qxClient/release/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxClientServer/qxClient/release/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxClientServer/qxClient/src/main.cpp b/test/qxClientServer/qxClient/src/main.cpp new file mode 100644 index 0000000..54655c9 --- /dev/null +++ b/test/qxClientServer/qxClient/src/main.cpp @@ -0,0 +1,22 @@ +#include + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + +#include "../include/precompiled.h" +#include "../include/main_dlg.h" + +#include + +int main(int argc, char * argv[]) +{ + QApplication app(argc, argv); + + main_dlg dlg; + dlg.show(); + + return app.exec(); +} diff --git a/test/qxClientServer/qxClient/src/main_dlg.cpp b/test/qxClientServer/qxClient/src/main_dlg.cpp new file mode 100644 index 0000000..bd984c8 --- /dev/null +++ b/test/qxClientServer/qxClient/src/main_dlg.cpp @@ -0,0 +1,223 @@ +#include "../include/precompiled.h" + +#include + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + +#include "../include/main_dlg.h" + +#include "../../qxService/include/service/server_infos.h" +#include "../../qxService/include/service/user_service.h" + +#include + +#define QX_CLIENT_BIRTHDATE_QDATETIME_FORMAT "dd/MM/yyyy" + +void main_dlg::init() +{ + setupUi(this); + + QObject::connect(btnDateTime, SIGNAL(clicked()), this, SLOT(onClickBtnDateTime())); + QObject::connect(btnDateTime_Async, SIGNAL(clicked()), this, SLOT(onClickBtnDateTimeAsync())); + QObject::connect(btnAddUser, SIGNAL(clicked()), this, SLOT(onClickBtnAddUser())); + QObject::connect(btnUpdateUser, SIGNAL(clicked()), this, SLOT(onClickBtnUpdateUser())); + QObject::connect(btnRemoveUser, SIGNAL(clicked()), this, SLOT(onClickBtnRemoveUser())); + QObject::connect(btnRemoveAllUser, SIGNAL(clicked()), this, SLOT(onClickBtnRemoveAllUsers())); + QObject::connect(btnFetchUser, SIGNAL(clicked()), this, SLOT(onClickBtnFetchUser())); + QObject::connect(btnGetAllUser, SIGNAL(clicked()), this, SLOT(onClickBtnGetAllUsers())); + QObject::connect(btnSearchUser, SIGNAL(clicked()), this, SLOT(onClickBtnSearchUsers())); + QObject::connect(txtIp, SIGNAL(editingFinished()), this, SLOT(onUpdateServerConnection())); + QObject::connect(spinPort, SIGNAL(editingFinished()), this, SLOT(onUpdateServerConnection())); + + txtBirthDate->setText(QX_CLIENT_BIRTHDATE_QDATETIME_FORMAT); + txtBirthDate->setToolTip(QString("Birthdate : ") + QX_CLIENT_BIRTHDATE_QDATETIME_FORMAT); + txtIp->setText("127.0.0.1"); + spinPort->setValue(7694); + onUpdateServerConnection(); +} + +void main_dlg::updateLastTransactionLog(qx::service::QxTransaction_ptr transaction) +{ + if (! transaction) { txtTransaction->setPlainText(""); return; } +#ifdef _QX_ENABLE_BOOST_SERIALIZATION_XML + QString text = qx::serialization::xml::to_string(* transaction); +#else // _QX_ENABLE_BOOST_SERIALIZATION_XML + QString text = transaction->getInfos(); +#endif // _QX_ENABLE_BOOST_SERIALIZATION_XML + txtTransaction->setPlainText(text.replace("\t", " ")); + qx_bool bMsgReturn = transaction->getMessageReturn(); + if (! bMsgReturn.getValue() && ! bMsgReturn.getDesc().isEmpty()) + { QMessageBox::warning(this, "qxClient - transaction error", bMsgReturn.getDesc()); } +} + +void main_dlg::fillUser(user_ptr user) +{ + if (! user) { return; } + txtId->setText(QString::number(user->id)); + txtFirstName->setText(user->first_name); + txtLastName->setText(user->last_name); + txtBirthDate->setText(user->birth_date.toString(QX_CLIENT_BIRTHDATE_QDATETIME_FORMAT)); +} + +user_ptr main_dlg::fileUser() +{ + user_ptr user_to_file = user_ptr(new user()); + user_to_file->id = txtId->text().toLong(); + user_to_file->first_name = txtFirstName->text(); + user_to_file->last_name = txtLastName->text(); + user_to_file->birth_date = QDateTime::fromString(txtBirthDate->text(), QX_CLIENT_BIRTHDATE_QDATETIME_FORMAT); + return user_to_file; +} + +void main_dlg::onUpdateServerConnection() +{ + qx::service::QxConnect::getSingleton()->setIp(txtIp->text()); + qx::service::QxConnect::getSingleton()->setPort(spinPort->value()); +} + +void main_dlg::onClickBtnDateTime() +{ + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + // Create service and call method to retrieve current server date-time + server_infos service; + service.get_current_date_time(); + // Update transaction log + updateLastTransactionLog(service.getTransaction()); + QApplication::restoreOverrideCursor(); +} + +void main_dlg::onClickBtnDateTimeAsync() +{ + if (m_pDateTimeAsync) { qDebug("[QxOrm] '%s' transaction is already running", "server_infos::get_current_date_time"); return; } + // Create service and call method to retrieve current server date-time (async mode) + server_infos_ptr service = server_infos_ptr(new server_infos()); + m_pDateTimeAsync.reset(new qx::service::QxClientAsync()); + QObject::connect(m_pDateTimeAsync.get(), SIGNAL(finished()), this, SLOT(onDateTimeAsyncFinished())); + m_pDateTimeAsync->setService(service, "get_current_date_time"); + m_pDateTimeAsync->start(); +} + +void main_dlg::onDateTimeAsyncFinished() +{ + if (! m_pDateTimeAsync || ! m_pDateTimeAsync->getService()) { return; } + updateLastTransactionLog(m_pDateTimeAsync->getService()->getTransaction()); + m_pDateTimeAsync.reset(); +} + +void main_dlg::onClickBtnAddUser() +{ + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + // Create input parameters with user to add + user_service_input_ptr input = user_service_input_ptr(new user_service_input()); + input->user = fileUser(); + // Create service to call and set input parameters + user_service service; + service.setInputParameter(input); + service.insert(); + // If transaction is ok => display user with new id added to database + user_ptr output = (service.isValidWithOutput() ? service.getOutputParameter()->user : user_ptr()); + if (output) { fillUser(output); } + // Update transaction log + updateLastTransactionLog(service.getTransaction()); + QApplication::restoreOverrideCursor(); +} + +void main_dlg::onClickBtnUpdateUser() +{ + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + // Create input parameters with user to update + user_service_input_ptr input = user_service_input_ptr(new user_service_input()); + input->user = fileUser(); + // Create service to call and set input parameters + user_service service; + service.setInputParameter(input); + service.update(); + // Update transaction log + updateLastTransactionLog(service.getTransaction()); + QApplication::restoreOverrideCursor(); +} + +void main_dlg::onClickBtnRemoveUser() +{ + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + // Create input parameters with user id to remove + user_service_input_ptr input = user_service_input_ptr(new user_service_input()); + input->id = txtId->text().toLong(); + // Create service to call and set input parameters + user_service service; + service.setInputParameter(input); + service.remove(); + // Update transaction log + updateLastTransactionLog(service.getTransaction()); + QApplication::restoreOverrideCursor(); +} + +void main_dlg::onClickBtnRemoveAllUsers() +{ + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + // Create service to call + user_service service; + service.remove_all(); + // Update transaction log + updateLastTransactionLog(service.getTransaction()); + QApplication::restoreOverrideCursor(); +} + +void main_dlg::onClickBtnFetchUser() +{ + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + // Create input parameters with user id to fetch + user_service_input_ptr input = user_service_input_ptr(new user_service_input()); + input->id = txtId->text().toLong(); + // Create service to call and set input parameters + user_service service; + service.setInputParameter(input); + service.fetch_by_id(); + // If transaction is ok => display user fetched on GUI + user_ptr output = (service.isValidWithOutput() ? service.getOutputParameter()->user : user_ptr()); + if (output) { fillUser(output); } + // Update transaction log + updateLastTransactionLog(service.getTransaction()); + QApplication::restoreOverrideCursor(); +} + +void main_dlg::onClickBtnGetAllUsers() +{ + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + // Create service to call + user_service service; + service.fetch_all(); + // If transaction is ok => display in a message box the number of users fetched from database + list_of_users_ptr output = (service.isValidWithOutput() ? service.getOutputParameter()->list_of_users : list_of_users_ptr()); + if (output) { QMessageBox::information(this, "qxClient - get all users", "database contains '" + QString::number(output->size()) + "' user(s)."); } + // Update transaction log + updateLastTransactionLog(service.getTransaction()); + QApplication::restoreOverrideCursor(); +} + +void main_dlg::onClickBtnSearchUsers() +{ + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + // Create criteria to search users + user_search_ptr criteria = user_search_ptr(new user_search()); + criteria->first_name = txtFirstName->text(); + criteria->last_name = txtLastName->text(); + criteria->birth_date = QDateTime::fromString(txtBirthDate->text(), QX_CLIENT_BIRTHDATE_QDATETIME_FORMAT); + // Create input parameters with criteria to search users + user_service_input_ptr input = user_service_input_ptr(new user_service_input()); + input->criteria = criteria; + // Create service to call and set input parameters + user_service service; + service.setInputParameter(input); + service.get_by_criteria(); + // If transaction is ok => display in a message box the number of users fetched from database + list_of_users_ptr output = (service.isValidWithOutput() ? service.getOutputParameter()->list_of_users : list_of_users_ptr()); + if (output) { QMessageBox::information(this, "qxClient - search users", "database contains '" + QString::number(output->size()) + "' user(s) with input criteria."); } + // Update transaction log + updateLastTransactionLog(service.getTransaction()); + QApplication::restoreOverrideCursor(); +} diff --git a/test/qxClientServer/qxServer/CMakeLists.txt b/test/qxClientServer/qxServer/CMakeLists.txt new file mode 100644 index 0000000..b54c86f --- /dev/null +++ b/test/qxClientServer/qxServer/CMakeLists.txt @@ -0,0 +1,76 @@ +cmake_minimum_required(VERSION 3.1) + +project(qxServer LANGUAGES CXX) + +include(../../../QxOrm.cmake) + +if(_QX_ENABLE_QT_NETWORK) + +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Sql Gui Widgets REQUIRED) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_DEBUG_POSTFIX "d") + +set(HEADERS + ./include/precompiled.h + ./include/export.h + ./include/main_dlg.h + ) + +set(SRCS + ./src/main_dlg.cpp + ./src/main.cpp + ) + +set(UIS + ./qt/ui/qxServer.ui + ) + +set(QRCS + ./qt/rcc/_qxServer.qrc + ) + +if(COMMAND qt_wrap_ui) +qt_wrap_ui(UIS_HDRS ${UIS}) +qt_add_resources(QRCS_HDRS ${QRCS}) +else() # (COMMAND qt_wrap_ui) +qt5_wrap_ui(UIS_HDRS ${UIS}) +qt5_add_resources(QRCS_HDRS ${QRCS}) +endif() # (COMMAND qt_wrap_ui) + +add_executable(qxServer ${SRCS} ${HEADERS} ${UIS_HDRS} ${QRCS_HDRS}) + +target_compile_definitions(qxServer PRIVATE -D_BUILDING_QX_SERVER) + +if(COMMAND target_precompile_headers) + target_precompile_headers(qxServer PRIVATE ./include/precompiled.h) +endif() # (COMMAND target_precompile_headers) + +target_link_libraries(qxServer ${QX_LIBRARIES} Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets QxOrm qxServiceServer) + +set_target_properties(qxServer PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ) + +set_target_properties(qxServer PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) + +else() # _QX_ENABLE_QT_NETWORK + +message(STATUS "qxServer project not loaded because _QX_ENABLE_QT_NETWORK option not enabled (QxOrm QxService module is disabled)") + +endif() # _QX_ENABLE_QT_NETWORK diff --git a/test/qxClientServer/qxServer/debug/.gitignore b/test/qxClientServer/qxServer/debug/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxClientServer/qxServer/debug/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxClientServer/qxServer/include/export.h b/test/qxClientServer/qxServer/include/export.h new file mode 100644 index 0000000..1635cc1 --- /dev/null +++ b/test/qxClientServer/qxServer/include/export.h @@ -0,0 +1,20 @@ +#ifndef _QX_SERVER_EXPORT_H_ +#define _QX_SERVER_EXPORT_H_ + +#include "../../qxService/include/export.h" + +#ifdef _BUILDING_QX_SERVER +#define QX_SERVER_DLL_EXPORT QX_DLL_EXPORT_HELPER +#else // _BUILDING_QX_SERVER +#define QX_SERVER_DLL_EXPORT QX_DLL_IMPORT_HELPER +#endif // _BUILDING_QX_SERVER + +#ifdef _BUILDING_QX_SERVER +#define QX_REGISTER_HPP_QX_SERVER QX_REGISTER_HPP_EXPORT_DLL +#define QX_REGISTER_CPP_QX_SERVER QX_REGISTER_CPP_EXPORT_DLL +#else // _BUILDING_QX_SERVER +#define QX_REGISTER_HPP_QX_SERVER QX_REGISTER_HPP_IMPORT_DLL +#define QX_REGISTER_CPP_QX_SERVER QX_REGISTER_CPP_IMPORT_DLL +#endif // _BUILDING_QX_SERVER + +#endif // _QX_SERVER_EXPORT_H_ diff --git a/test/qxClientServer/qxServer/include/main_dlg.h b/test/qxClientServer/qxServer/include/main_dlg.h new file mode 100644 index 0000000..cbcc63e --- /dev/null +++ b/test/qxClientServer/qxServer/include/main_dlg.h @@ -0,0 +1,47 @@ +#ifndef _QX_SERVER_MAIN_DLG_H_ +#define _QX_SERVER_MAIN_DLG_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include "../include/precompiled.h" // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +#include "../qt/ui/include/ui_qxServer.h" + +class main_dlg : public QWidget, private Ui::dlgServer +{ + + Q_OBJECT + +private: + + qx::service::QxThreadPool_ptr m_pThreadPool; // Server thread pool to receive all requests + qx::QxDaoAsync m_daoAsync; // To test to run queries in a different thread + +public: + + main_dlg(QWidget * parent = NULL) : QWidget(parent), Ui::dlgServer() { main_dlg::init(); } + virtual ~main_dlg() { ; } + +private: + + void init(); + void loadServices(); + +private Q_SLOTS: + + void onClickStartStop(); + void onCboIndexChanged(int index); + void onError(const QString & err, qx::service::QxTransaction_ptr transaction); + void onServerIsRunning(bool bIsRunning, qx::service::QxServer * pServer); + void onTransactionFinished(qx::service::QxTransaction_ptr transaction); + void onQueryFinished(const QSqlError & daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams); + +}; + +#endif // _QX_SERVER_MAIN_DLG_H_ diff --git a/test/qxClientServer/qxServer/include/precompiled.h b/test/qxClientServer/qxServer/include/precompiled.h new file mode 100644 index 0000000..4520c67 --- /dev/null +++ b/test/qxClientServer/qxServer/include/precompiled.h @@ -0,0 +1,9 @@ +#ifndef _QX_SERVER_PRECOMPILED_HEADER_H_ +#define _QX_SERVER_PRECOMPILED_HEADER_H_ + +#include +#include + +#include "export.h" + +#endif // _QX_SERVER_PRECOMPILED_HEADER_H_ diff --git a/test/qxClientServer/qxServer/qt/moc/.gitignore b/test/qxClientServer/qxServer/qt/moc/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxClientServer/qxServer/qt/moc/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxClientServer/qxServer/qt/rcc/_qxServer.qrc b/test/qxClientServer/qxServer/qt/rcc/_qxServer.qrc new file mode 100644 index 0000000..1a3433a --- /dev/null +++ b/test/qxClientServer/qxServer/qt/rcc/_qxServer.qrc @@ -0,0 +1 @@ + ./stop.png ./run.png \ No newline at end of file diff --git a/test/qxClientServer/qxServer/qt/rcc/run.png b/test/qxClientServer/qxServer/qt/rcc/run.png new file mode 100644 index 0000000000000000000000000000000000000000..49a78380a0d50bd2dd905fea14b5bf48c573fb5a GIT binary patch literal 1739 zcmV;+1~mDJP)2^s`@WKAeN|Oe3mO_4^oIpLRUAEfw4(I)IDup`DZL`q z2aMudODV@QfDi&9!~}JHQ~tw41}|NL$)WzQKqL7P21W;F1TLiMPslbc~d^z$Zjctt#Wrk3tiY|0?bOr;1h>T$OH+K^(3*xxWeHEBBfwt>yyzn2{LYm10Nk=@AkcHSiS+rnF#LJ{J6rUAPeKbvsPeWbbuF6zDO#U zVx_T?mv+3wibX2`NM@4keR41BtJmW=4k{|b6G$mBHIrj~$B3CRYU8!!oE&&l?}B?0 zckqxo_(exIL;UbfF1TGbi+e84);s={SSQL2BpMQn#uOfOk9JrR8cf zUBjRAS)vWkby>4u4V}sJblp17!S26u?q)l6OY7M6)dtepH1B-TM7oehL7B1_OiSRi zL_0{>x_lD=eItDw>~7*><~-+8AJdyVL*0rkln0_z1Q#-p@1-qyii17xAsxvNS3W~o zNTqYQgUfd=qoK{t0~t33L8yu?2B0I+M#pFibHYA6!^IOYYNhB-b?~R5y<8tl;#dmW zEss<#MP;y(6|p6Jaq}wIvqP+&w|aIS2C{s-y ziEP4*fEM;}MFHVRR7E3fmVt412+g_=tS(;-26T+H0`Rr+bwo_qFt3)4!R>6W{ucWO zpW#~mB0JXX;a@!;;Cir1TSds2!}a`C(%fQR03yD*xWb)XAE>YT4)KdGb9&?$SJJ(# ztE}aFq*EQuU>c#AqBsA^bc4mos>Po+(0?iks3mQFrVv3`HHcTJ+a@jd1*cA z!W|9|{}ES%y6ARdW(-$2Ggm-LiRx2%{_FcVe`*W^isyOg zx{j)<_a{&sD8dswR<#YwwQ$_KYv@P^)zi>@8b{=BiK>e!SK@TppK#K8lXLDVtOD#Z z_ONUDZnAC~DJ6zsP*zq3;N6!%=~T;${KUELDHOacAIMiZuN)_$%;${vH*R`YxMSrA z4=>;++OPQjil1S5d4v$?x=t_{#Pd9|*=+v49=O-ZM)wSkCH_pa|L-h{$Fasd#*#i3 z6gE>QH?bx9G?n3MvTk}DJP-&F4u?r366A6@dsYfe&X$*_KJp{x+^*o`fj3!c*6?lr zFA0T$n1+cZ3ye9VNGZp`1AzdBVQ}Nd4H=C_FV5Hl(`8Ln**t$cHDk5hag#(t5iDVm zbu;5`F9kPE6M##XE)`tY?S3EyiaF=%gg2V%j6b<9f}5s^uIt#g&A`9_ zU0q$Gy}jM(>+35>Df@*G{nIKiG&I!S*x0zvbzM=~wI+Yrl;&csD0jbeEz6RbOeT{| zCKIVtDr?(zzouykUDx|mRqZ%*=+LE7Or@k&ckbL7R}>{Gr|OwA3{_QCMNu+Bh})S= hMm9G$7w+}^e*oral9W0cXz~C6002ovPDHLkV1jY|N0$Hq literal 0 HcmV?d00001 diff --git a/test/qxClientServer/qxServer/qt/rcc/stop.png b/test/qxClientServer/qxServer/qt/rcc/stop.png new file mode 100644 index 0000000000000000000000000000000000000000..646acf9f3dd9df44e4a503f1bc20190bdd48d245 GIT binary patch literal 796 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTCmUKs7M+S!VC(K#9UIO`&C9V-A z!TD(=<%vb942~)JNvR5+xryniL8*x;m4zo$Z5SAs)_J-(hE&{2`t$$4{ow#M2F4>6 zS8N#s9GvelGcdM1YAkVcvsN(uD9_ydD4|40nE7zDLa6EI|No~N7&b6CeLo6yuBqQcJ7q?_y4!~fpzIDs7yftHo&a?H zktW@}jhFu3&%Y0}T-IL0hT+KnhL`gNyGbJGc-y7 z?cY=Rz@Gc`f|VxG+7$U1 zm@-aOGbl8)?O`XI*^#R8gXfO?0Y!FT?EI+tR?krZG%=zeGeJ5|B literal 0 HcmV?d00001 diff --git a/test/qxClientServer/qxServer/qt/ui/qxServer.ui b/test/qxClientServer/qxServer/qt/ui/qxServer.ui new file mode 100644 index 0000000..10afa1a --- /dev/null +++ b/test/qxClientServer/qxServer/qt/ui/qxServer.ui @@ -0,0 +1,325 @@ + + + dlgServer + + + + 0 + 0 + 552 + 392 + + + + + 552 + 392 + + + + + 552 + 392 + + + + qxServer + + + + + 10 + 310 + 531 + 71 + + + + Log last server error + + + + + 10 + 20 + 511 + 41 + + + + + + + + + 170 + 0 + 0 + + + + + + + + + 170 + 0 + 0 + + + + + + + + + 112 + 111 + 113 + + + + + + + + Qt::ScrollBarAlwaysOn + + + true + + + + + + + 10 + 10 + 531 + 81 + + + + Server parameters + + + + + 180 + 15 + 81 + 16 + + + + Thread Count + + + Thread Count : + + + + + + 10 + 50 + 91 + 16 + + + + Serialization Type + + + Serialization Type : + + + + + + 110 + 50 + 51 + 20 + + + + Serialization Type + + + + + + 10 + 20 + 81 + 16 + + + + Port Number + + + Port Number : + + + + + + 110 + 20 + 51 + 22 + + + + Port Number + + + 1 + + + 32000 + + + + + + 180 + 40 + 101 + 17 + + + + Compress Data + + + Compress Data + + + + + + 330 + 20 + 111 + 51 + + + + + 75 + true + + + + Start Server / +Stop Server + + + + + + 320 + 10 + 3 + 61 + + + + Qt::Vertical + + + + + + 450 + 20 + 71 + 51 + + + + [ is running ? ] + + + Qt::AlignCenter + + + + + + 260 + 15 + 51 + 22 + + + + Thread Count + + + 1 + + + 999 + + + + + + 180 + 55 + 101 + 17 + + + + Encrypt Data + + + Encrypt Data + + + + + + + 10 + 100 + 531 + 201 + + + + Log last client-server reply-request transaction + + + + + 10 + 20 + 511 + 171 + + + + Qt::ScrollBarAlwaysOn + + + Qt::ScrollBarAlwaysOn + + + QPlainTextEdit::NoWrap + + + true + + + + + + + diff --git a/test/qxClientServer/qxServer/qt/ui/src/.gitignore b/test/qxClientServer/qxServer/qt/ui/src/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxClientServer/qxServer/qt/ui/src/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxClientServer/qxServer/qxServer.pro b/test/qxClientServer/qxServer/qxServer.pro new file mode 100644 index 0000000..43ee5f9 --- /dev/null +++ b/test/qxClientServer/qxServer/qxServer.pro @@ -0,0 +1,41 @@ +include(../../../QxOrm.pri) + +!contains(DEFINES, _QX_ENABLE_QT_NETWORK) { +error(unable to use QxOrm QxService module : please define _QX_ENABLE_QT_NETWORK compilation option in QxOrm.pri configuration file) +} # !contains(DEFINES, _QX_ENABLE_QT_NETWORK) + +TEMPLATE = app +DEFINES += _BUILDING_QX_SERVER +INCLUDEPATH += ../../../../QxOrm/include/ +DESTDIR = ../../../../QxOrm/test/_bin/ + +QT += gui +greaterThan(QT_MAJOR_VERSION, 4) { QT += widgets } + +!contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) { +PRECOMPILED_HEADER = ./include/precompiled.h +} # !contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) + +LIBS += -L"../../../../QxOrm/lib" +LIBS += -L"../../../../QxOrm/test/_bin" + +CONFIG(debug, debug|release) { +TARGET = qxServerd +LIBS += -l"QxOrmd" +LIBS += -l"qxServiceServerd" +} else { +TARGET = qxServer +LIBS += -l"QxOrm" +LIBS += -l"qxServiceServer" +} # CONFIG(debug, debug|release) + +HEADERS += ./include/precompiled.h +HEADERS += ./include/export.h +HEADERS += ./include/main_dlg.h + +SOURCES += ./src/main_dlg.cpp +SOURCES += ./src/main.cpp + +FORMS += ./qt/ui/qxServer.ui + +RESOURCES += ./qt/rcc/_qxServer.qrc diff --git a/test/qxClientServer/qxServer/release/.gitignore b/test/qxClientServer/qxServer/release/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxClientServer/qxServer/release/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxClientServer/qxServer/src/main.cpp b/test/qxClientServer/qxServer/src/main.cpp new file mode 100644 index 0000000..54655c9 --- /dev/null +++ b/test/qxClientServer/qxServer/src/main.cpp @@ -0,0 +1,22 @@ +#include + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + +#include "../include/precompiled.h" +#include "../include/main_dlg.h" + +#include + +int main(int argc, char * argv[]) +{ + QApplication app(argc, argv); + + main_dlg dlg; + dlg.show(); + + return app.exec(); +} diff --git a/test/qxClientServer/qxServer/src/main_dlg.cpp b/test/qxClientServer/qxServer/src/main_dlg.cpp new file mode 100644 index 0000000..2eb6fdc --- /dev/null +++ b/test/qxClientServer/qxServer/src/main_dlg.cpp @@ -0,0 +1,132 @@ +#include "../include/precompiled.h" + +#include "../include/main_dlg.h" + +#include "../../qxService/include/service/server_infos.h" +#include "../../qxService/include/dao/user_manager.h" + +#include + +void main_dlg::init() +{ + setupUi(this); + + QObject::connect(btnStartStop, SIGNAL(clicked()), this, SLOT(onClickStartStop())); + QObject::connect(cboSerializationType, SIGNAL(currentIndexChanged(int)), this, SLOT(onCboIndexChanged(int))); + QObject::connect((& m_daoAsync), SIGNAL(queryFinished(const QSqlError &, qx::dao::detail::QxDaoAsyncParams_ptr)), this, SLOT(onQueryFinished(const QSqlError &, qx::dao::detail::QxDaoAsyncParams_ptr))); + + cboSerializationType->addItem("0- serialization_binary", QVariant((int)qx::service::QxConnect::serialization_binary)); + cboSerializationType->addItem("1- serialization_xml", QVariant((int)qx::service::QxConnect::serialization_xml)); + cboSerializationType->addItem("2- serialization_text", QVariant((int)qx::service::QxConnect::serialization_text)); + cboSerializationType->addItem("3- serialization_portable_binary", QVariant((int)qx::service::QxConnect::serialization_portable_binary)); + cboSerializationType->addItem("4- serialization_wide_binary", QVariant((int)qx::service::QxConnect::serialization_wide_binary)); + cboSerializationType->addItem("5- serialization_wide_xml", QVariant((int)qx::service::QxConnect::serialization_wide_xml)); + cboSerializationType->addItem("6- serialization_wide_text", QVariant((int)qx::service::QxConnect::serialization_wide_text)); + cboSerializationType->addItem("7- serialization_polymorphic_binary", QVariant((int)qx::service::QxConnect::serialization_polymorphic_binary)); + cboSerializationType->addItem("8- serialization_polymorphic_xml", QVariant((int)qx::service::QxConnect::serialization_polymorphic_xml)); + cboSerializationType->addItem("9- serialization_polymorphic_text", QVariant((int)qx::service::QxConnect::serialization_polymorphic_text)); + cboSerializationType->addItem("10- serialization_qt", QVariant((int)qx::service::QxConnect::serialization_qt)); + cboSerializationType->addItem("11- serialization_json", QVariant((int)qx::service::QxConnect::serialization_json)); + cboSerializationType->setCurrentIndex(cboSerializationType->findData(QVariant((int)qx::service::QxConnect::getSingleton()->getSerializationType()))); + + spinPortNumber->setValue(7694); + spinThreadCount->setValue(qx::service::QxConnect::getSingleton()->getThreadCount()); + onServerIsRunning(false, NULL); + onClickStartStop(); +} + +void main_dlg::loadServices() +{ + // Required to be sure to load all services dll : create a dummy service for each dll + // It is also possible to create a 'plugin system' to load services + server_infos dummy_01; Q_UNUSED(dummy_01); +} + +void main_dlg::onClickStartStop() +{ + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + if (m_pThreadPool) + { + m_pThreadPool.reset(); + txtError->setPlainText(""); + txtTransaction->setPlainText(""); + onServerIsRunning(false, NULL); + } + else + { + qx::service::QxConnect::getSingleton()->setPort(spinPortNumber->value()); + qx::service::QxConnect::getSingleton()->setThreadCount(spinThreadCount->value()); + qx::service::QxConnect::getSingleton()->setSerializationType((qx::service::QxConnect::serialization_type)(cboSerializationType->itemData(cboSerializationType->currentIndex()).toInt())); + qx::service::QxConnect::getSingleton()->setCompressData(chkCompressData->isChecked()); + qx::service::QxConnect::getSingleton()->setEncryptData(chkEncryptData->isChecked()); + + m_pThreadPool.reset(new qx::service::QxThreadPool()); + QObject::connect(m_pThreadPool.get(), SIGNAL(error(const QString &, qx::service::QxTransaction_ptr)), this, SLOT(onError(const QString &, qx::service::QxTransaction_ptr))); + QObject::connect(m_pThreadPool.get(), SIGNAL(serverIsRunning(bool, qx::service::QxServer *)), this, SLOT(onServerIsRunning(bool, qx::service::QxServer *))); + QObject::connect(m_pThreadPool.get(), SIGNAL(transactionFinished(qx::service::QxTransaction_ptr)), this, SLOT(onTransactionFinished(qx::service::QxTransaction_ptr))); + m_pThreadPool->start(); + } + QApplication::restoreOverrideCursor(); +} + +void main_dlg::onCboIndexChanged(int index) +{ + if (index < 0) { cboSerializationType->setToolTip(""); } + else { cboSerializationType->setToolTip(cboSerializationType->itemText(cboSerializationType->currentIndex())); } + + // To test to run queries in a different thread : see 'onQueryFinished()' method to see the result + if (cboSerializationType->count() <= 1) { return; } + user_manager dummy; Q_UNUSED(dummy); // To init database parameters + qx_query query = "SELECT * FROM user"; + m_daoAsync.asyncCallQuery(query); +} + +void main_dlg::onServerIsRunning(bool bIsRunning, qx::service::QxServer * pServer) +{ + Q_UNUSED(pServer); + imgIsRunning->setText(""); + imgIsRunning->setPixmap(bIsRunning ? QPixmap(":/run") : QPixmap(":/stop")); + btnStartStop->setText(bIsRunning ? "Stop Server" : "Start Server"); + + spinPortNumber->setEnabled(! bIsRunning); + spinThreadCount->setEnabled(! bIsRunning); + cboSerializationType->setEnabled(! bIsRunning); + chkCompressData->setEnabled(! bIsRunning); + chkEncryptData->setEnabled(! bIsRunning); +} + +void main_dlg::onError(const QString & err, qx::service::QxTransaction_ptr transaction) +{ + if (err.isEmpty()) { txtError->setPlainText(""); return; } + QString errText = QDateTime::currentDateTime().toString("dd.MM.yyyy hh:mm") + " : " + err; +#ifdef _QX_ENABLE_BOOST_SERIALIZATION_XML + if (transaction) { errText += QString("\r\n\r\n") + qx::serialization::xml::to_string(* transaction); } +#else // _QX_ENABLE_BOOST_SERIALIZATION_XML + if (transaction) { errText += QString("\r\n\r\n") + transaction->getInfos(); } +#endif // _QX_ENABLE_BOOST_SERIALIZATION_XML + txtError->setPlainText(errText.replace("\t", " ")); +} + +void main_dlg::onTransactionFinished(qx::service::QxTransaction_ptr transaction) +{ + if (! transaction) { txtTransaction->setPlainText(""); return; } +#ifdef _QX_ENABLE_BOOST_SERIALIZATION_XML + QString text = qx::serialization::xml::to_string(* transaction); +#else // _QX_ENABLE_BOOST_SERIALIZATION_XML + QString text = transaction->getInfos(); +#endif // _QX_ENABLE_BOOST_SERIALIZATION_XML + txtTransaction->setPlainText(text.replace("\t", " ")); +} + +void main_dlg::onQueryFinished(const QSqlError & daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams) +{ + if (! pDaoParams) { return; } + qx::QxSqlQuery query = pDaoParams->query; + if (! daoError.isValid()) { /* ... */ } + // If the async query is associated to a simple object, just use 'pDaoParams->pInstance' method + qx::IxPersistable_ptr ptr = pDaoParams->pInstance; + // If the async query is associated to a list of objects, just use 'pDaoParams->pListOfInstances' method + qx::IxPersistableCollection_ptr lst = pDaoParams->pListOfInstances; + // etc... + Q_UNUSED(query); Q_UNUSED(ptr); Q_UNUSED(lst); +} diff --git a/test/qxClientServer/qxService/debug/debug_client/.gitignore b/test/qxClientServer/qxService/debug/debug_client/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxClientServer/qxService/debug/debug_client/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxClientServer/qxService/debug/debug_server/.gitignore b/test/qxClientServer/qxService/debug/debug_server/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxClientServer/qxService/debug/debug_server/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxClientServer/qxService/include/business_object/user.h b/test/qxClientServer/qxService/include/business_object/user.h new file mode 100644 index 0000000..0e4b7ab --- /dev/null +++ b/test/qxClientServer/qxService/include/business_object/user.h @@ -0,0 +1,23 @@ +#ifndef _QX_SERVICE_BO_USER_H_ +#define _QX_SERVICE_BO_USER_H_ + +class QX_SERVICE_DLL_EXPORT user +{ +public: +// -- contructor, virtual destructor + user() : id(0) { ; } + virtual ~user() { ; } +// -- properties + long id; + QString first_name; + QString last_name; + QDateTime birth_date; +}; + +QX_REGISTER_HPP_QX_SERVICE(user, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr user_ptr; +typedef qx::QxCollection list_of_users; +typedef std::shared_ptr list_of_users_ptr; + +#endif // _QX_SERVICE_BO_USER_H_ diff --git a/test/qxClientServer/qxService/include/business_object/user_search.h b/test/qxClientServer/qxService/include/business_object/user_search.h new file mode 100644 index 0000000..fa98602 --- /dev/null +++ b/test/qxClientServer/qxService/include/business_object/user_search.h @@ -0,0 +1,23 @@ +#ifndef _QX_SERVICE_BO_USER_SEARCH_H_ +#define _QX_SERVICE_BO_USER_SEARCH_H_ + +class QX_SERVICE_DLL_EXPORT user_search +{ +public: +// -- contructor, virtual destructor + user_search() { ; } + virtual ~user_search() { ; } +// -- properties + QString first_name; + QString last_name; + QDateTime birth_date; +// -- methods + bool empty() const + { return (first_name.isEmpty() && last_name.isEmpty() && ! birth_date.isValid()); } +}; + +QX_REGISTER_HPP_QX_SERVICE(user_search, qx::trait::no_base_class_defined, 0) + +typedef std::shared_ptr user_search_ptr; + +#endif // _QX_SERVICE_BO_USER_SEARCH_H_ diff --git a/test/qxClientServer/qxService/include/dao/user_manager.h b/test/qxClientServer/qxService/include/dao/user_manager.h new file mode 100644 index 0000000..0997e06 --- /dev/null +++ b/test/qxClientServer/qxService/include/dao/user_manager.h @@ -0,0 +1,25 @@ +#ifndef _QX_SERVICE_MODE_CLIENT +#ifndef _QX_SERVICE_USER_MANAGER_H_ +#define _QX_SERVICE_USER_MANAGER_H_ + +#include "../../include/business_object/user.h" +#include "../../include/business_object/user_search.h" + +class QX_SERVICE_DLL_EXPORT user_manager +{ +public: + user_manager() { user_manager::init_database(); } + ~user_manager() { ; } + QSqlError insert(user_ptr p); + QSqlError update(user_ptr p); + QSqlError remove(user_ptr p); + QSqlError remove_all(); + QSqlError fetch_by_id(user_ptr p); + QSqlError fetch_all(list_of_users_ptr lst); + QSqlError get_by_criteria(user_search_ptr criteria, list_of_users_ptr lst); +private: + static void init_database(); +}; + +#endif // _QX_SERVICE_USER_MANAGER_H_ +#endif // _QX_SERVICE_MODE_CLIENT diff --git a/test/qxClientServer/qxService/include/export.h b/test/qxClientServer/qxService/include/export.h new file mode 100644 index 0000000..75930e0 --- /dev/null +++ b/test/qxClientServer/qxService/include/export.h @@ -0,0 +1,18 @@ +#ifndef _QX_SERVICE_EXPORT_H_ +#define _QX_SERVICE_EXPORT_H_ + +#ifdef _BUILDING_QX_SERVICE +#define QX_SERVICE_DLL_EXPORT QX_DLL_EXPORT_HELPER +#else // _BUILDING_QX_SERVICE +#define QX_SERVICE_DLL_EXPORT QX_DLL_IMPORT_HELPER +#endif // _BUILDING_QX_SERVICE + +#ifdef _BUILDING_QX_SERVICE +#define QX_REGISTER_HPP_QX_SERVICE QX_REGISTER_HPP_EXPORT_DLL +#define QX_REGISTER_CPP_QX_SERVICE QX_REGISTER_CPP_EXPORT_DLL +#else // _BUILDING_QX_SERVICE +#define QX_REGISTER_HPP_QX_SERVICE QX_REGISTER_HPP_IMPORT_DLL +#define QX_REGISTER_CPP_QX_SERVICE QX_REGISTER_CPP_IMPORT_DLL +#endif // _BUILDING_QX_SERVICE + +#endif // _QX_SERVICE_EXPORT_H_ diff --git a/test/qxClientServer/qxService/include/precompiled.h b/test/qxClientServer/qxService/include/precompiled.h new file mode 100644 index 0000000..d030c41 --- /dev/null +++ b/test/qxClientServer/qxService/include/precompiled.h @@ -0,0 +1,9 @@ +#ifndef _QX_SERVICE_PRECOMPILED_HEADER_H_ +#define _QX_SERVICE_PRECOMPILED_HEADER_H_ + +#include +#include + +#include "export.h" + +#endif // _QX_SERVICE_PRECOMPILED_HEADER_H_ diff --git a/test/qxClientServer/qxService/include/service/server_infos.h b/test/qxClientServer/qxService/include/service/server_infos.h new file mode 100644 index 0000000..b05ed33 --- /dev/null +++ b/test/qxClientServer/qxService/include/service/server_infos.h @@ -0,0 +1,34 @@ +#ifndef _QX_SERVICE_SERVER_INFOS_H_ +#define _QX_SERVICE_SERVER_INFOS_H_ + +/* -- Service Input Parameters -- */ + +class QX_SERVICE_DLL_EXPORT server_infos_input : public qx::service::IxParameter +{ QX_SERVICE_IX_PARAMETER_SERIALIZATION_HPP(server_infos_input); }; + +QX_REGISTER_HPP_QX_SERVICE(server_infos_input, qx::service::IxParameter, 0) +typedef std::shared_ptr server_infos_input_ptr; + +/* -- Service Output Parameters -- */ + +class QX_SERVICE_DLL_EXPORT server_infos_output : public qx::service::IxParameter +{ public: QDateTime current_date_time; QX_SERVICE_IX_PARAMETER_SERIALIZATION_HPP(server_infos_output); }; + +QX_REGISTER_HPP_QX_SERVICE(server_infos_output, qx::service::IxParameter, 0) +typedef std::shared_ptr server_infos_output_ptr; + +/* -- Service Definition -- */ + +typedef qx::service::QxService server_infos_base_class; +class QX_SERVICE_DLL_EXPORT server_infos : public server_infos_base_class +{ +public: + server_infos() : server_infos_base_class("server_infos") { ; } + virtual ~server_infos() { ; } + void get_current_date_time(); +}; + +QX_REGISTER_HPP_QX_SERVICE(server_infos, qx::service::IxService, 0) +typedef std::shared_ptr server_infos_ptr; + +#endif // _QX_SERVICE_SERVER_INFOS_H_ diff --git a/test/qxClientServer/qxService/include/service/user_service.h b/test/qxClientServer/qxService/include/service/user_service.h new file mode 100644 index 0000000..2bf2b7f --- /dev/null +++ b/test/qxClientServer/qxService/include/service/user_service.h @@ -0,0 +1,56 @@ +#ifndef _QX_SERVICE_USER_SERVICE_H_ +#define _QX_SERVICE_USER_SERVICE_H_ + +#include "../../include/business_object/user.h" +#include "../../include/business_object/user_search.h" + +/* -- Service Input Parameters -- */ + +class QX_SERVICE_DLL_EXPORT user_service_input : public qx::service::IxParameter +{ + QX_SERVICE_IX_PARAMETER_SERIALIZATION_HPP(user_service_input); +public: + user_service_input() : id(0) { ; } + virtual ~user_service_input() { ; } + long id; + user_ptr user; + user_search_ptr criteria; +}; + +QX_REGISTER_HPP_QX_SERVICE(user_service_input, qx::service::IxParameter, 0) +typedef std::shared_ptr user_service_input_ptr; + +/* -- Service Output Parameters -- */ + +class QX_SERVICE_DLL_EXPORT user_service_output : public qx::service::IxParameter +{ + QX_SERVICE_IX_PARAMETER_SERIALIZATION_HPP(user_service_output); +public: + user_ptr user; + list_of_users_ptr list_of_users; +}; + +QX_REGISTER_HPP_QX_SERVICE(user_service_output, qx::service::IxParameter, 0) +typedef std::shared_ptr user_service_output_ptr; + +/* -- Service Definition -- */ + +typedef qx::service::QxService user_service_base_class; +class QX_SERVICE_DLL_EXPORT user_service : public user_service_base_class +{ +public: + user_service() : user_service_base_class("user_service") { ; } + virtual ~user_service() { ; } + void insert(); + void update(); + void remove(); + void remove_all(); + void fetch_by_id(); + void fetch_all(); + void get_by_criteria(); +}; + +QX_REGISTER_HPP_QX_SERVICE(user_service, qx::service::IxService, 0) +typedef std::shared_ptr user_service_ptr; + +#endif // _QX_SERVICE_USER_SERVICE_H_ diff --git a/test/qxClientServer/qxService/qt/moc/.gitignore b/test/qxClientServer/qxService/qt/moc/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxClientServer/qxService/qt/moc/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxClientServer/qxService/qt/rcc/src/.gitignore b/test/qxClientServer/qxService/qt/rcc/src/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxClientServer/qxService/qt/rcc/src/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxClientServer/qxService/qt/ui/include/.gitignore b/test/qxClientServer/qxService/qt/ui/include/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxClientServer/qxService/qt/ui/include/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxClientServer/qxService/qt/ui/src/.gitignore b/test/qxClientServer/qxService/qt/ui/src/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxClientServer/qxService/qt/ui/src/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxClientServer/qxService/qxService.pri b/test/qxClientServer/qxService/qxService.pri new file mode 100644 index 0000000..6cbb6ed --- /dev/null +++ b/test/qxClientServer/qxService/qxService.pri @@ -0,0 +1,37 @@ +include(../../../QxOrm.pri) + +!contains(DEFINES, _QX_ENABLE_QT_NETWORK) { +error(unable to use QxOrm QxService module : please define _QX_ENABLE_QT_NETWORK compilation option in QxOrm.pri configuration file) +} # !contains(DEFINES, _QX_ENABLE_QT_NETWORK) + +TEMPLATE = lib +CONFIG += dll +DEFINES += _BUILDING_QX_SERVICE +INCLUDEPATH += ../../../../QxOrm/include/ +DESTDIR = ../../../../QxOrm/test/_bin/ +LIBS += -L"../../../../QxOrm/lib" + +!contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) { +PRECOMPILED_HEADER = ./include/precompiled.h +} # !contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) + +CONFIG(debug, debug|release) { +LIBS += -l"QxOrmd" +} else { +LIBS += -l"QxOrm" +} # CONFIG(debug, debug|release) + +HEADERS += ./include/precompiled.h +HEADERS += ./include/export.h +HEADERS += ./include/service/server_infos.h +HEADERS += ./include/service/user_service.h +HEADERS += ./include/business_object/user.h +HEADERS += ./include/business_object/user_search.h +HEADERS += ./include/dao/user_manager.h + +SOURCES += ./src/service/server_infos.cpp +SOURCES += ./src/service/user_service.cpp +SOURCES += ./src/business_object/user.cpp +SOURCES += ./src/business_object/user_search.cpp +SOURCES += ./src/dao/user_manager.cpp +SOURCES += ./src/main.cpp diff --git a/test/qxClientServer/qxService/qxServiceClient.pro b/test/qxClientServer/qxService/qxServiceClient.pro new file mode 100644 index 0000000..6a93543 --- /dev/null +++ b/test/qxClientServer/qxService/qxServiceClient.pro @@ -0,0 +1,11 @@ +include(./qxService.pri) + +DEFINES += _QX_SERVICE_MODE_CLIENT + +CONFIG(debug, debug|release) { +OBJECTS_DIR = ./debug/debug_client/ +TARGET = qxServiceClientd +} else { +OBJECTS_DIR = ./release/release_client/ +TARGET = qxServiceClient +} # CONFIG(debug, debug|release) diff --git a/test/qxClientServer/qxService/qxServiceClient/CMakeLists.txt b/test/qxClientServer/qxService/qxServiceClient/CMakeLists.txt new file mode 100644 index 0000000..27de8b4 --- /dev/null +++ b/test/qxClientServer/qxService/qxServiceClient/CMakeLists.txt @@ -0,0 +1,64 @@ +cmake_minimum_required(VERSION 3.1) + +project(qxServiceClient LANGUAGES CXX) + +include(../../../../QxOrm.cmake) + +if(_QX_ENABLE_QT_NETWORK) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_DEBUG_POSTFIX "d") + +set(HEADERS + ../include/precompiled.h + ../include/export.h + ../include/service/server_infos.h + ../include/service/user_service.h + ../include/business_object/user.h + ../include/business_object/user_search.h + ../include/dao/user_manager.h + ) + +set(SRCS + ../src/service/server_infos.cpp + ../src/service/user_service.cpp + ../src/business_object/user.cpp + ../src/business_object/user_search.cpp + ../src/dao/user_manager.cpp + ../src/main.cpp + ) + +add_library(qxServiceClient SHARED ${SRCS} ${HEADERS}) + +target_compile_definitions(qxServiceClient PRIVATE -D_BUILDING_QX_SERVICE -D_QX_SERVICE_MODE_CLIENT) + +if(COMMAND target_precompile_headers) + target_precompile_headers(qxServiceClient PRIVATE ../include/precompiled.h) +endif() # (COMMAND target_precompile_headers) + +target_link_libraries(qxServiceClient ${QX_LIBRARIES} QxOrm) + +set_target_properties(qxServiceClient PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + ) + +else() # _QX_ENABLE_QT_NETWORK + +message(STATUS "qxServiceClient project not loaded because _QX_ENABLE_QT_NETWORK option not enabled (QxOrm QxService module is disabled)") + +endif() # _QX_ENABLE_QT_NETWORK diff --git a/test/qxClientServer/qxService/qxServiceServer.pro b/test/qxClientServer/qxService/qxServiceServer.pro new file mode 100644 index 0000000..6465cfc --- /dev/null +++ b/test/qxClientServer/qxService/qxServiceServer.pro @@ -0,0 +1,9 @@ +include(./qxService.pri) + +CONFIG(debug, debug|release) { +OBJECTS_DIR = ./debug/debug_server/ +TARGET = qxServiceServerd +} else { +OBJECTS_DIR = ./release/release_server/ +TARGET = qxServiceServer +} # CONFIG(debug, debug|release) diff --git a/test/qxClientServer/qxService/qxServiceServer/CMakeLists.txt b/test/qxClientServer/qxService/qxServiceServer/CMakeLists.txt new file mode 100644 index 0000000..9a6de6e --- /dev/null +++ b/test/qxClientServer/qxService/qxServiceServer/CMakeLists.txt @@ -0,0 +1,64 @@ +cmake_minimum_required(VERSION 3.1) + +project(qxServiceServer LANGUAGES CXX) + +include(../../../../QxOrm.cmake) + +if(_QX_ENABLE_QT_NETWORK) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_DEBUG_POSTFIX "d") + +set(HEADERS + ../include/precompiled.h + ../include/export.h + ../include/service/server_infos.h + ../include/service/user_service.h + ../include/business_object/user.h + ../include/business_object/user_search.h + ../include/dao/user_manager.h + ) + +set(SRCS + ../src/service/server_infos.cpp + ../src/service/user_service.cpp + ../src/business_object/user.cpp + ../src/business_object/user_search.cpp + ../src/dao/user_manager.cpp + ../src/main.cpp + ) + +add_library(qxServiceServer SHARED ${SRCS} ${HEADERS}) + +target_compile_definitions(qxServiceServer PRIVATE -D_BUILDING_QX_SERVICE) + +if(COMMAND target_precompile_headers) + target_precompile_headers(qxServiceServer PRIVATE ../include/precompiled.h) +endif() # (COMMAND target_precompile_headers) + +target_link_libraries(qxServiceServer ${QX_LIBRARIES} QxOrm) + +set_target_properties(qxServiceServer PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../../_bin" + ) + +else() # _QX_ENABLE_QT_NETWORK + +message(STATUS "qxServiceServer project not loaded because _QX_ENABLE_QT_NETWORK option not enabled (QxOrm QxService module is disabled)") + +endif() # _QX_ENABLE_QT_NETWORK diff --git a/test/qxClientServer/qxService/release/release_client/.gitignore b/test/qxClientServer/qxService/release/release_client/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxClientServer/qxService/release/release_client/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxClientServer/qxService/release/release_server/.gitignore b/test/qxClientServer/qxService/release/release_server/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxClientServer/qxService/release/release_server/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxClientServer/qxService/src/business_object/user.cpp b/test/qxClientServer/qxService/src/business_object/user.cpp new file mode 100644 index 0000000..3c312ed --- /dev/null +++ b/test/qxClientServer/qxService/src/business_object/user.cpp @@ -0,0 +1,17 @@ +#include "../../include/precompiled.h" + +#include "../../include/business_object/user.h" + +#include + +QX_REGISTER_CPP_QX_SERVICE(user) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.id(& user::id, "id"); + + t.data(& user::first_name, "first_name"); + t.data(& user::last_name, "last_name"); + t.data(& user::birth_date, "birth_date"); +}} diff --git a/test/qxClientServer/qxService/src/business_object/user_search.cpp b/test/qxClientServer/qxService/src/business_object/user_search.cpp new file mode 100644 index 0000000..eb639f6 --- /dev/null +++ b/test/qxClientServer/qxService/src/business_object/user_search.cpp @@ -0,0 +1,15 @@ +#include "../../include/precompiled.h" + +#include "../../include/business_object/user_search.h" + +#include + +QX_REGISTER_CPP_QX_SERVICE(user_search) + +namespace qx { +template <> void register_class(QxClass & t) +{ + t.data(& user_search::first_name, "first_name"); + t.data(& user_search::last_name, "last_name"); + t.data(& user_search::birth_date, "birth_date"); +}} diff --git a/test/qxClientServer/qxService/src/dao/user_manager.cpp b/test/qxClientServer/qxService/src/dao/user_manager.cpp new file mode 100644 index 0000000..2845879 --- /dev/null +++ b/test/qxClientServer/qxService/src/dao/user_manager.cpp @@ -0,0 +1,90 @@ +#ifndef _QX_SERVICE_MODE_CLIENT + +#include "../../include/precompiled.h" + +#include "../../include/dao/user_manager.h" + +#include + +void user_manager::init_database() +{ + static QMutex mutex; + static bool bInitDone = false; + if (bInitDone) { return; } + QMutexLocker locker(& mutex); + if (bInitDone) { return; } + bInitDone = true; + + QFile::remove("./user.db"); + qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE"); + qx::QxSqlDatabase::getSingleton()->setDatabaseName("user.db"); + qx::QxSqlDatabase::getSingleton()->setHostName("localhost"); + qx::QxSqlDatabase::getSingleton()->setUserName("root"); + qx::QxSqlDatabase::getSingleton()->setPassword(""); + qx::dao::create_table(); +} + +QSqlError user_manager::insert(user_ptr p) +{ + if (! p) { return QSqlError("cannot insert user : invalid user (null pointer)", "", QSqlError::UnknownError); } + if (p->first_name.trimmed().isEmpty()) { return QSqlError("cannot insert user : 'first_name' is required", "", QSqlError::UnknownError); } + if (p->last_name.trimmed().isEmpty()) { return QSqlError("cannot insert user : 'last_name' is required", "", QSqlError::UnknownError); } + return qx::dao::insert(p); +} + +QSqlError user_manager::update(user_ptr p) +{ + if (! p) { return QSqlError("cannot update user : invalid user (null pointer)", "", QSqlError::UnknownError); } + if (p->id == 0) { return QSqlError("cannot update user : 'id' is required", "", QSqlError::UnknownError); } + if (p->first_name.trimmed().isEmpty()) { return QSqlError("cannot update user : 'first_name' is required", "", QSqlError::UnknownError); } + if (p->last_name.trimmed().isEmpty()) { return QSqlError("cannot update user : 'last_name' is required", "", QSqlError::UnknownError); } + if (! qx::dao::exist(p)) { return QSqlError("cannot update user : user doesn't exist in database", "", QSqlError::UnknownError); } + return qx::dao::update(p); +} + +QSqlError user_manager::remove(user_ptr p) +{ + if (! p) { return QSqlError("cannot remove user : invalid user (null pointer)", "", QSqlError::UnknownError); } + if (p->id == 0) { return QSqlError("cannot remove user : 'id' is required", "", QSqlError::UnknownError); } + if (! qx::dao::exist(p)) { return QSqlError("cannot remove user : user doesn't exist in database", "", QSqlError::UnknownError); } + return qx::dao::delete_by_id(p); +} + +QSqlError user_manager::remove_all() +{ + return qx::dao::delete_all(); +} + +QSqlError user_manager::fetch_by_id(user_ptr p) +{ + if (! p) { return QSqlError("cannot fetch user : invalid user (null pointer)", "", QSqlError::UnknownError); } + if (p->id == 0) { return QSqlError("cannot fetch user : 'id' is required", "", QSqlError::UnknownError); } + if (! qx::dao::exist(p)) { return QSqlError("cannot fetch user : user doesn't exist in database", "", QSqlError::UnknownError); } + return qx::dao::fetch_by_id(p); +} + +QSqlError user_manager::fetch_all(list_of_users_ptr lst) +{ + return qx::dao::fetch_all(lst); +} + +QSqlError user_manager::get_by_criteria(user_search_ptr criteria, list_of_users_ptr lst) +{ + if (! criteria) { return QSqlError("cannot search users : invalid criteria (null pointer)", "", QSqlError::UnknownError); } + if (criteria->empty()) { return fetch_all(lst); } + + QString sql = "WHERE "; + if (! criteria->first_name.isEmpty()) { sql += "user.first_name LIKE :first_name AND "; } + if (! criteria->last_name.isEmpty()) { sql += "user.last_name LIKE :last_name AND "; } + if (criteria->birth_date.isValid()) { sql += "user.birth_date = :birth_date AND "; } + sql = sql.left(sql.count() - 5); // Remove last " AND " + + qx::QxSqlQuery query(sql); + if (! criteria->first_name.isEmpty()) { query.bind(":first_name", criteria->first_name); } + if (! criteria->last_name.isEmpty()) { query.bind(":last_name", criteria->last_name); } + if (criteria->birth_date.isValid()) { query.bind(":birth_date", criteria->birth_date); } + + return qx::dao::fetch_by_query(query, lst); +} + +#endif // _QX_SERVICE_MODE_CLIENT diff --git a/test/qxClientServer/qxService/src/main.cpp b/test/qxClientServer/qxService/src/main.cpp new file mode 100644 index 0000000..3420dba --- /dev/null +++ b/test/qxClientServer/qxService/src/main.cpp @@ -0,0 +1,20 @@ +#ifdef _MSC_VER + +#include + +extern "C" +int WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /* lpReservedt */) +{ + (void)hInstance; + switch (dwReason) + { + case DLL_PROCESS_ATTACH: ::OutputDebugStringA("qxService.DllMain() ---> DLL_PROCESS_ATTACH\n"); break; + case DLL_PROCESS_DETACH: ::OutputDebugStringA("qxService.DllMain() ---> DLL_PROCESS_DETACH\n"); break; + case DLL_THREAD_ATTACH: ::OutputDebugStringA("qxService.DllMain() ---> DLL_THREAD_ATTACH\n"); break; + case DLL_THREAD_DETACH: ::OutputDebugStringA("qxService.DllMain() ---> DLL_THREAD_DETACH\n"); break; + } + + return 1; +} + +#endif // _MSC_VER diff --git a/test/qxClientServer/qxService/src/service/server_infos.cpp b/test/qxClientServer/qxService/src/service/server_infos.cpp new file mode 100644 index 0000000..51588bc --- /dev/null +++ b/test/qxClientServer/qxService/src/service/server_infos.cpp @@ -0,0 +1,42 @@ +#include "../../include/precompiled.h" + +#include "../../include/service/server_infos.h" + +#include + +QX_REGISTER_CPP_QX_SERVICE(server_infos_input) +QX_REGISTER_CPP_QX_SERVICE(server_infos_output) +QX_REGISTER_CPP_QX_SERVICE(server_infos) + +QX_SERVICE_IX_PARAMETER_SERIALIZATION_CPP(server_infos_input) +QX_SERVICE_IX_PARAMETER_SERIALIZATION_CPP(server_infos_output) + +namespace qx { + +template <> void register_class(QxClass & t) +{ Q_UNUSED(t); } + +template <> void register_class(QxClass & t) +{ t.data(& server_infos_output::current_date_time, "current_date_time"); } + +template <> void register_class(QxClass & t) +{ t.fct_0(std::mem_fn(& server_infos::get_current_date_time), "get_current_date_time"); } // using std::mem_fn() here is just a workaround for an issue with some versions of MSVC, it is not required with a full compliant C++11 compiler (http://stackoverflow.com/questions/23778883/vs2013-stdfunction-with-member-function) + +} // namespace qx + +#ifdef _QX_SERVICE_MODE_CLIENT + +void server_infos::get_current_date_time() +{ qx::service::execute_client(this, "get_current_date_time"); } + +#else // _QX_SERVICE_MODE_CLIENT + +void server_infos::get_current_date_time() +{ + server_infos_output_ptr output = server_infos_output_ptr(new server_infos_output()); + output->current_date_time = QDateTime::currentDateTime(); + setOutputParameter(output); + setMessageReturn(true); +} + +#endif // _QX_SERVICE_MODE_CLIENT diff --git a/test/qxClientServer/qxService/src/service/user_service.cpp b/test/qxClientServer/qxService/src/service/user_service.cpp new file mode 100644 index 0000000..1ed5e71 --- /dev/null +++ b/test/qxClientServer/qxService/src/service/user_service.cpp @@ -0,0 +1,133 @@ +#include "../../include/precompiled.h" + +#include "../../include/service/user_service.h" + +#include "../../include/dao/user_manager.h" + +#include + +QX_REGISTER_CPP_QX_SERVICE(user_service_input) +QX_REGISTER_CPP_QX_SERVICE(user_service_output) +QX_REGISTER_CPP_QX_SERVICE(user_service) + +QX_SERVICE_IX_PARAMETER_SERIALIZATION_CPP(user_service_input) +QX_SERVICE_IX_PARAMETER_SERIALIZATION_CPP(user_service_output) + +namespace qx { + +template <> void register_class(QxClass & t) +{ + t.data(& user_service_input::id, "id"); + t.data(& user_service_input::user, "user"); + t.data(& user_service_input::criteria, "criteria"); +} + +template <> void register_class(QxClass & t) +{ + t.data(& user_service_output::user, "user"); + t.data(& user_service_output::list_of_users, "list_of_users"); +} + +template <> void register_class(QxClass & t) +{ + t.fct_0(std::mem_fn(& user_service::insert), "insert"); // using std::mem_fn() here is just a workaround for an issue with some versions of MSVC, it is not required with a full compliant C++11 compiler (http://stackoverflow.com/questions/23778883/vs2013-stdfunction-with-member-function) + t.fct_0(std::mem_fn(& user_service::update), "update"); + t.fct_0(std::mem_fn(& user_service::remove), "remove"); + t.fct_0(std::mem_fn(& user_service::remove_all), "remove_all"); + t.fct_0(std::mem_fn(& user_service::fetch_by_id), "fetch_by_id"); + t.fct_0(std::mem_fn(& user_service::fetch_all), "fetch_all"); + t.fct_0(std::mem_fn(& user_service::get_by_criteria), "get_by_criteria"); +} + +} // namespace qx + +#ifdef _QX_SERVICE_MODE_CLIENT + +void user_service::insert() { qx::service::execute_client(this, "insert"); } +void user_service::update() { qx::service::execute_client(this, "update"); } +void user_service::remove() { qx::service::execute_client(this, "remove"); } +void user_service::remove_all() { qx::service::execute_client(this, "remove_all"); } +void user_service::fetch_by_id() { qx::service::execute_client(this, "fetch_by_id"); } +void user_service::fetch_all() { qx::service::execute_client(this, "fetch_all"); } +void user_service::get_by_criteria() { qx::service::execute_client(this, "get_by_criteria"); } + +#else // _QX_SERVICE_MODE_CLIENT + +void user_service::insert() +{ + user_service_input_ptr input = getInputParameter(); + if (! input) { setMessageReturn(0, "invalid input parameter to call service 'user_service::insert()'"); return; } + QSqlError err = user_manager().insert(input->user); + if (err.isValid()) { setMessageReturn(0, err.text()); return; } + user_service_output_ptr output = user_service_output_ptr(new user_service_output()); + output->user = input->user; + setOutputParameter(output); + setMessageReturn(true); +} + +void user_service::update() +{ + user_service_input_ptr input = getInputParameter(); + if (! input) { setMessageReturn(0, "invalid input parameter to call service 'user_service::update()'"); return; } + QSqlError err = user_manager().update(input->user); + if (err.isValid()) { setMessageReturn(0, err.text()); } + else { setMessageReturn(true); } +} + +void user_service::remove() +{ + user_service_input_ptr input = getInputParameter(); + if (! input) { setMessageReturn(0, "invalid input parameter to call service 'user_service::remove()'"); return; } + user_ptr user_tmp = user_ptr(new user()); + user_tmp->id = input->id; + QSqlError err = user_manager().remove(user_tmp); + if (err.isValid()) { setMessageReturn(0, err.text()); } + else { setMessageReturn(true); } +} + +void user_service::remove_all() +{ + QSqlError err = user_manager().remove_all(); + if (err.isValid()) { setMessageReturn(0, err.text()); } + else { setMessageReturn(true); } +} + +void user_service::fetch_by_id() +{ + user_service_input_ptr input = getInputParameter(); + if (! input) { setMessageReturn(0, "invalid input parameter to call service 'user_service::fetch_by_id()'"); return; } + user_ptr user_output = user_ptr(new user()); + user_output->id = input->id; + QSqlError err = user_manager().fetch_by_id(user_output); + if (err.isValid()) { setMessageReturn(0, err.text()); return; } + user_service_output_ptr output = user_service_output_ptr(new user_service_output()); + output->user = user_output; + setOutputParameter(output); + setMessageReturn(true); +} + +void user_service::fetch_all() +{ + list_of_users_ptr list_of_users_output = list_of_users_ptr(new list_of_users()); + QSqlError err = user_manager().fetch_all(list_of_users_output); + if (err.isValid()) { setMessageReturn(0, err.text()); return; } + user_service_output_ptr output = user_service_output_ptr(new user_service_output()); + output->list_of_users = list_of_users_output; + setOutputParameter(output); + setMessageReturn(true); +} + +void user_service::get_by_criteria() +{ + user_service_input_ptr input = getInputParameter(); + if (! input) { setMessageReturn(0, "invalid input parameter to call service 'user_service::get_by_criteria()'"); return; } + list_of_users_ptr list_of_users_output = list_of_users_ptr(new list_of_users()); + QSqlError err = user_manager().get_by_criteria(input->criteria, list_of_users_output); + if (err.isValid()) { setMessageReturn(0, err.text()); return; } + user_service_output_ptr output = user_service_output_ptr(new user_service_output()); + output->list_of_users = list_of_users_output; + setOutputParameter(output); + setMessageReturn(true); +} + +#endif // _QX_SERVICE_MODE_CLIENT diff --git a/test/qxClientServer/qxService/tools/build_debug.bat b/test/qxClientServer/qxService/tools/build_debug.bat new file mode 100644 index 0000000..12466c4 --- /dev/null +++ b/test/qxClientServer/qxService/tools/build_debug.bat @@ -0,0 +1,4 @@ +qmake %QXORM_QMAKE_PARAMS% "./qxServiceClient.pro" +nmake debug +qmake %QXORM_QMAKE_PARAMS% "./qxServiceServer.pro" +nmake debug diff --git a/test/qxClientServer/qxService/tools/build_release.bat b/test/qxClientServer/qxService/tools/build_release.bat new file mode 100644 index 0000000..9339ef4 --- /dev/null +++ b/test/qxClientServer/qxService/tools/build_release.bat @@ -0,0 +1,4 @@ +qmake %QXORM_QMAKE_PARAMS% "./qxServiceClient.pro" +nmake release +qmake %QXORM_QMAKE_PARAMS% "./qxServiceServer.pro" +nmake release diff --git a/test/qxClientServer/qxService/tools/clean_debug.bat b/test/qxClientServer/qxService/tools/clean_debug.bat new file mode 100644 index 0000000..e2007d2 --- /dev/null +++ b/test/qxClientServer/qxService/tools/clean_debug.bat @@ -0,0 +1,4 @@ +cd "./debug/" +del /F /S /Q "./*.*" +cd "../" +del /F /S /Q "./vc90.pdb" diff --git a/test/qxClientServer/qxService/tools/clean_release.bat b/test/qxClientServer/qxService/tools/clean_release.bat new file mode 100644 index 0000000..e85cbe4 --- /dev/null +++ b/test/qxClientServer/qxService/tools/clean_release.bat @@ -0,0 +1,2 @@ +cd "./release/" +del /F /S /Q "./*.*" diff --git a/test/qxDllSample/CMakeLists.txt b/test/qxDllSample/CMakeLists.txt new file mode 100644 index 0000000..aafcf2d --- /dev/null +++ b/test/qxDllSample/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.1) + +project(qxDllSample LANGUAGES CXX) + +add_subdirectory(dll1) +add_subdirectory(dll2) +add_subdirectory(exe) diff --git a/test/qxDllSample/dll1/CMakeLists.txt b/test/qxDllSample/dll1/CMakeLists.txt new file mode 100644 index 0000000..bda5a27 --- /dev/null +++ b/test/qxDllSample/dll1/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.1) + +project(dll1 LANGUAGES CXX) + +include(../../../QxOrm.cmake) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_DEBUG_POSTFIX "d") + +set(HEADERS + ./include/precompiled.h + ./include/export.h + ./include/CPerson.h + ./include/TestQtProperty.h + ./include/QxPersistable.h + ) + +set(SRCS + ./qx/CPerson.qx.cpp + ./src/CPerson.cpp + ./src/TestQtProperty.cpp + ./src/QxPersistable.cpp + ./src/main.cpp + ) + +add_library(dll1 SHARED ${SRCS} ${HEADERS}) + +target_compile_definitions(dll1 PRIVATE -D_QX_BUILDING_DLL1) + +if(COMMAND target_precompile_headers) + target_precompile_headers(dll1 PRIVATE ./include/precompiled.h) +endif() # (COMMAND target_precompile_headers) + +target_link_libraries(dll1 ${QX_LIBRARIES} QxOrm) + +set_target_properties(dll1 PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ) diff --git a/test/qxDllSample/dll1/debug/.gitignore b/test/qxDllSample/dll1/debug/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxDllSample/dll1/debug/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxDllSample/dll1/dll1.pro b/test/qxDllSample/dll1/dll1.pro new file mode 100644 index 0000000..1430d54 --- /dev/null +++ b/test/qxDllSample/dll1/dll1.pro @@ -0,0 +1,38 @@ +include(../../../QxOrm.pri) + +TEMPLATE = lib +CONFIG += dll +DEFINES += _QX_BUILDING_DLL1 +INCLUDEPATH += ../../../../QxOrm/include/ +DESTDIR = ../../../../QxOrm/test/_bin/ + +!contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) { +PRECOMPILED_HEADER = ./include/precompiled.h +} # !contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) + +CONFIG(debug, debug|release) { +TARGET = dll1d +} else { +TARGET = dll1 +} # CONFIG(debug, debug|release) + +LIBS += -L"../../../../QxOrm/lib" + +CONFIG(debug, debug|release) { +LIBS += -l"QxOrmd" +} else { +LIBS += -l"QxOrm" +} # CONFIG(debug, debug|release) + +HEADERS += ./include/precompiled.h +HEADERS += ./include/export.h +HEADERS += ./include/CPerson.h +HEADERS += ./include/TestQtProperty.h +HEADERS += ./include/QxPersistable.h + +SOURCES += ./qx/CPerson.qx.cpp + +SOURCES += ./src/CPerson.cpp +SOURCES += ./src/TestQtProperty.cpp +SOURCES += ./src/QxPersistable.cpp +SOURCES += ./src/main.cpp diff --git a/test/qxDllSample/dll1/include/CPerson.h b/test/qxDllSample/dll1/include/CPerson.h new file mode 100644 index 0000000..b30c549 --- /dev/null +++ b/test/qxDllSample/dll1/include/CPerson.h @@ -0,0 +1,66 @@ +#ifndef _QX_CLASS_PERSON_H_ +#define _QX_CLASS_PERSON_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include "../include/precompiled.h" // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +namespace qx { +namespace test { + +class QX_DLL1_EXPORT CPerson : public QObject +{ + + Q_OBJECT + QX_REGISTER_FRIEND_CLASS(qx::test::CPerson) + +public: + + enum sex { male, female, unknown }; + +protected: + + long m_lPersonId; + QString m_sFirstName; + QString m_sLastName; + double m_dDouble; + sex m_eSex; + +public: + + CPerson() : QObject(), m_lPersonId(0), m_sFirstName("toto"), m_dDouble(4.5678), m_eSex(unknown) { ; } + CPerson(long lId) : QObject(), m_lPersonId(lId), m_sFirstName("toto"), m_dDouble(4.5678), m_eSex(unknown) { ; } + virtual ~CPerson() { ; } + + long getPersonId() const { return m_lPersonId; } + QString getFirstName() const { return m_sFirstName; } + QString getLastName() const { return m_sLastName; } + double getDouble() const { return m_dDouble; } + sex getSex() const { return m_eSex; } + + void setPersonId(long l) { m_lPersonId = l; } + void setFirstName(const QString & s) { m_sFirstName = s; } + void setLastName(const QString & s) { m_sLastName = s; } + void setDouble(double d) { m_dDouble = d; } + void setSex(sex e) { m_eSex = e; } + + static int testStaticFct(const QString & s); + +private: + + void isValid(qx::QxInvalidValueX & invalidValues); + +}; + +} // namespace test +} // namespace qx + +QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QX_DLL1(qx::test::CPerson, QObject, 0, qx_test_CPerson) + +#endif // _QX_CLASS_PERSON_H_ diff --git a/test/qxDllSample/dll1/include/QxPersistable.h b/test/qxDllSample/dll1/include/QxPersistable.h new file mode 100644 index 0000000..736abdd --- /dev/null +++ b/test/qxDllSample/dll1/include/QxPersistable.h @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#ifndef _QX_PERSISTABLE_H_ +#define _QX_PERSISTABLE_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include "../include/precompiled.h" // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +namespace qx +{ + + class QxPersistable; + + namespace dao + { + namespace detail + { + + template <> + struct QxDao_Trigger; + + } // namespace detail + } // namespace dao + + /*! + * \brief qx::QxPersistable : super base class for persistent classes with many features and methods to override (be careful, don't forget to use QX_PERSISTABLE_HPP() and QX_PERSISTABLE_CPP() macros for each derived class) + */ + class QX_DLL1_EXPORT QxPersistable : public QObject, public qx::IxPersistable + { + + Q_OBJECT + QX_REGISTER_FRIEND_CLASS(qx::QxPersistable) + QX_PERSISTABLE_HPP(qx::QxPersistable) + + Q_PROPERTY(long qxId READ qxGetId WRITE qxSetId) + Q_PROPERTY(QDateTime qxDateCreation READ qxGetDateCreation WRITE qxSetDateCreation) + Q_PROPERTY(QDateTime qxDateModification READ qxGetDateModification WRITE qxSetDateModification) + + friend struct qx::dao::detail::QxDao_Trigger; + + protected: + long m_qxId; //!< Id of current instance stored into database + qx::QxDateTimeNeutral m_qxDateCreation; //!< Creation date-time automatically calculated before INSERT query + qx::QxDateTimeNeutral m_qxDateModification; //!< Modification date-time automatically calculated before INSERT and UPDATE queries + + public: + QxPersistable(); + virtual ~QxPersistable(); + + long qxGetId() const; + QDateTime qxGetDateCreation() const; + QDateTime qxGetDateModification() const; + + void qxSetId(long l); + void qxSetDateCreation(const QDateTime &dt); + void qxSetDateModification(const QDateTime &dt); + + protected: + // -- List of useful protected methods to override into derived class -- // + + virtual void qxIsValid(qx::QxInvalidValueX &invalidValues); + + virtual void qxOnBeforeInsert(qx::dao::detail::IxDao_Helper *dao); + virtual void qxOnBeforeUpdate(qx::dao::detail::IxDao_Helper *dao); + virtual void qxOnBeforeDelete(qx::dao::detail::IxDao_Helper *dao); + virtual void qxOnBeforeFetch(qx::dao::detail::IxDao_Helper *dao); + virtual void qxOnAfterInsert(qx::dao::detail::IxDao_Helper *dao); + virtual void qxOnAfterUpdate(qx::dao::detail::IxDao_Helper *dao); + virtual void qxOnAfterDelete(qx::dao::detail::IxDao_Helper *dao); + virtual void qxOnAfterFetch(qx::dao::detail::IxDao_Helper *dao); + + private: + void qxIsValidInternal(qx::QxInvalidValueX &invalidValues); + + Q_SIGNALS: + + void qxOnBeforeInsert(qx::QxPersistable *p); + void qxOnBeforeUpdate(qx::QxPersistable *p); + void qxOnBeforeDelete(qx::QxPersistable *p); + void qxOnBeforeFetch(qx::QxPersistable *p); + void qxOnAfterInsert(qx::QxPersistable *p); + void qxOnAfterUpdate(qx::QxPersistable *p); + void qxOnAfterDelete(qx::QxPersistable *p); + void qxOnAfterFetch(qx::QxPersistable *p); + + public: + // -- List of useful public methods available from 'qx::IxPersistable' interface (using QX_PERSISTABLE_HPP() and QX_PERSISTABLE_CPP() macros) -- // + + /* + virtual long qxCount(const qx::QxSqlQuery & query = qx::QxSqlQuery(), QSqlDatabase * pDatabase = NULL, const QStringList & relation = QStringList()); + virtual QSqlError qxFetchById(const QVariant & id = QVariant(), const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL); + virtual QSqlError qxFetchAll(qx::IxCollection & list, const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL); + virtual QSqlError qxFetchByQuery(const qx::QxSqlQuery & query, qx::IxCollection & list, const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL); + virtual QSqlError qxInsert(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL, bool bUseExecBatch = false); + virtual QSqlError qxUpdate(const qx::QxSqlQuery & query = qx::QxSqlQuery(), const QStringList & columns = QStringList(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL, bool bUseExecBatch = false); + virtual QSqlError qxSave(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL); + virtual QSqlError qxDeleteById(const QVariant & id = QVariant(), QSqlDatabase * pDatabase = NULL, bool bUseExecBatch = false); + virtual QSqlError qxDeleteAll(QSqlDatabase * pDatabase = NULL); + virtual QSqlError qxDeleteByQuery(const qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL); + virtual QSqlError qxDestroyById(const QVariant & id = QVariant(), QSqlDatabase * pDatabase = NULL, bool bUseExecBatch = false); + virtual QSqlError qxDestroyAll(QSqlDatabase * pDatabase = NULL); + virtual QSqlError qxDestroyByQuery(const qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL); + virtual qx_bool qxExist(const QVariant & id = QVariant(), QSqlDatabase * pDatabase = NULL); + virtual qx::QxInvalidValueX qxValidate(const QStringList & groups = QStringList()); + virtual qx::IxPersistableCollection_ptr qxNewPersistableCollection(bool bAsList = false) const; + virtual qx::IxClass * qxClass() const; + */ + }; + + typedef std::shared_ptr QxPersistable_ptr; + +} // namespace qx + +QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QX_DLL1(qx::QxPersistable, QObject, 0, qx_QxPersistable) + +namespace qx +{ + namespace dao + { + namespace detail + { + + template <> + struct QxDao_Trigger + { + + static inline void onBeforeInsert(qx::QxPersistable *t, qx::dao::detail::IxDao_Helper *dao) + { + if (t) + { + t->qxOnBeforeInsert(dao); + } + } + static inline void onBeforeUpdate(qx::QxPersistable *t, qx::dao::detail::IxDao_Helper *dao) + { + if (t) + { + t->qxOnBeforeUpdate(dao); + } + } + static inline void onBeforeDelete(qx::QxPersistable *t, qx::dao::detail::IxDao_Helper *dao) + { + if (t) + { + t->qxOnBeforeDelete(dao); + } + } + static inline void onBeforeFetch(qx::QxPersistable *t, qx::dao::detail::IxDao_Helper *dao) + { + if (t) + { + t->qxOnBeforeFetch(dao); + } + } + static inline void onAfterInsert(qx::QxPersistable *t, qx::dao::detail::IxDao_Helper *dao) + { + if (t) + { + t->qxOnAfterInsert(dao); + } + } + static inline void onAfterUpdate(qx::QxPersistable *t, qx::dao::detail::IxDao_Helper *dao) + { + if (t) + { + t->qxOnAfterUpdate(dao); + } + } + static inline void onAfterDelete(qx::QxPersistable *t, qx::dao::detail::IxDao_Helper *dao) + { + if (t) + { + t->qxOnAfterDelete(dao); + } + } + static inline void onAfterFetch(qx::QxPersistable *t, qx::dao::detail::IxDao_Helper *dao) + { + if (t) + { + t->qxOnAfterFetch(dao); + } + } + }; + + } // namespace detail + } // namespace dao +} // namespace qx + +#endif // _QX_PERSISTABLE_H_ diff --git a/test/qxDllSample/dll1/include/TestQtProperty.h b/test/qxDllSample/dll1/include/TestQtProperty.h new file mode 100644 index 0000000..61134e9 --- /dev/null +++ b/test/qxDllSample/dll1/include/TestQtProperty.h @@ -0,0 +1,53 @@ +#ifndef _QX_TEST_QT_META_PROPERTY_H_ +#define _QX_TEST_QT_META_PROPERTY_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include "../include/precompiled.h" // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +class QX_DLL1_EXPORT TestQtProperty : public QObject +{ + + Q_OBJECT + Q_PROPERTY(int id READ id WRITE setId) + Q_PROPERTY(long number READ number WRITE setNumber) + Q_PROPERTY(QString desc READ desc WRITE setDesc) + Q_PROPERTY(QDateTime birthDate READ birthDate WRITE setBirthDate) + Q_PROPERTY(QVariant photo READ photo WRITE setPhoto) + +protected: + + int m_id; + long m_number; + QString m_desc; + QDateTime m_birthDate; + QVariant m_photo; + +public: + + TestQtProperty() : QObject(), m_id(0), m_number(0) { ; } + virtual ~TestQtProperty() { ; } + + int id() const { return m_id; } + long number() const { return m_number; } + QString desc() const { return m_desc; } + QDateTime birthDate() const { return m_birthDate; } + QVariant photo() const { return m_photo; } + + void setId(int i) { m_id = i; } + void setNumber(long l) { m_number = l; } + void setDesc(const QString & s) { m_desc = s; } + void setBirthDate(const QDateTime & dt) { m_birthDate = dt; } + void setPhoto(const QVariant & v) { m_photo = v; } + +}; + +QX_REGISTER_HPP_QX_DLL1(TestQtProperty, QObject, 0) + +#endif // _QX_TEST_QT_META_PROPERTY_H_ diff --git a/test/qxDllSample/dll1/include/export.h b/test/qxDllSample/dll1/include/export.h new file mode 100644 index 0000000..f5ac083 --- /dev/null +++ b/test/qxDllSample/dll1/include/export.h @@ -0,0 +1,26 @@ +#ifndef _QX_DLL1_EXPORT_H_ +#define _QX_DLL1_EXPORT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#ifdef _QX_BUILDING_DLL1 +#define QX_DLL1_EXPORT QX_DLL_EXPORT_HELPER +#else // _QX_BUILDING_DLL1 +#define QX_DLL1_EXPORT QX_DLL_IMPORT_HELPER +#endif // _QX_BUILDING_DLL1 + +#ifdef _QX_BUILDING_DLL1 +#define QX_REGISTER_HPP_QX_DLL1 QX_REGISTER_HPP_EXPORT_DLL +#define QX_REGISTER_CPP_QX_DLL1 QX_REGISTER_CPP_EXPORT_DLL +#define QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QX_DLL1 QX_REGISTER_COMPLEX_CLASS_NAME_HPP_EXPORT_DLL +#define QX_REGISTER_COMPLEX_CLASS_NAME_CPP_QX_DLL1 QX_REGISTER_COMPLEX_CLASS_NAME_CPP_EXPORT_DLL +#else // _QX_BUILDING_DLL1 +#define QX_REGISTER_HPP_QX_DLL1 QX_REGISTER_HPP_IMPORT_DLL +#define QX_REGISTER_CPP_QX_DLL1 QX_REGISTER_CPP_IMPORT_DLL +#define QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QX_DLL1 QX_REGISTER_COMPLEX_CLASS_NAME_HPP_IMPORT_DLL +#define QX_REGISTER_COMPLEX_CLASS_NAME_CPP_QX_DLL1 QX_REGISTER_COMPLEX_CLASS_NAME_CPP_IMPORT_DLL +#endif // _QX_BUILDING_DLL1 + +#endif // _QX_DLL1_EXPORT_H_ diff --git a/test/qxDllSample/dll1/include/precompiled.h b/test/qxDllSample/dll1/include/precompiled.h new file mode 100644 index 0000000..0ac20db --- /dev/null +++ b/test/qxDllSample/dll1/include/precompiled.h @@ -0,0 +1,12 @@ +#ifndef _QX_DLL1_PRECOMPILED_HEADER_H_ +#define _QX_DLL1_PRECOMPILED_HEADER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#include "../include/export.h" + +#endif // _QX_DLL1_PRECOMPILED_HEADER_H_ diff --git a/test/qxDllSample/dll1/qt/moc/.gitignore b/test/qxDllSample/dll1/qt/moc/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxDllSample/dll1/qt/moc/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxDllSample/dll1/qt/rcc/src/.gitignore b/test/qxDllSample/dll1/qt/rcc/src/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxDllSample/dll1/qt/rcc/src/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxDllSample/dll1/qt/ui/include/.gitignore b/test/qxDllSample/dll1/qt/ui/include/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxDllSample/dll1/qt/ui/include/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxDllSample/dll1/qt/ui/src/.gitignore b/test/qxDllSample/dll1/qt/ui/src/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxDllSample/dll1/qt/ui/src/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxDllSample/dll1/qx/CPerson.qx.cpp b/test/qxDllSample/dll1/qx/CPerson.qx.cpp new file mode 100644 index 0000000..eda0a8d --- /dev/null +++ b/test/qxDllSample/dll1/qx/CPerson.qx.cpp @@ -0,0 +1,70 @@ +#include "../include/precompiled.h" + +#include "../include/CPerson.h" + +#include + +void myGlobalValidator_1(const QVariant & value, const qx::IxValidator * validator, qx::QxInvalidValueX & invalidValues); +void myGlobalValidator_2(const QString & value, const qx::IxValidator * validator, qx::QxInvalidValueX & invalidValues); + +QX_REGISTER_COMPLEX_CLASS_NAME_CPP_QX_DLL1(qx::test::CPerson, qx_test_CPerson) + +namespace qx { +template <> void register_class(QxClass & t) +{ + IxDataMember * pData = NULL; + IxFunction * pFct = NULL; + IxValidator * pValidator = NULL; + + t.setName("CPerson"); + + pData = t.id(& qx::test::CPerson::m_lPersonId, "idPerson", 0); + + pData = t.data(& qx::test::CPerson::m_sFirstName, "firstName", 0); + pData = t.data(& qx::test::CPerson::m_sLastName, "lastName", 0); + pData = t.data(& qx::test::CPerson::m_dDouble, "double", 0); + pData = t.data(& qx::test::CPerson::m_eSex, "sex", 0); + + pFct = t.fct_0(std::mem_fn(& qx::test::CPerson::getPersonId), "fct_getPersonId"); // using std::mem_fn() here is just a workaround for an issue with some versions of MSVC, it is not required with a full compliant C++11 compiler (http://stackoverflow.com/questions/23778883/vs2013-stdfunction-with-member-function) + pFct = t.fct_0(std::mem_fn(& qx::test::CPerson::getFirstName), "fct_getFirstName"); + pFct = t.fct_1(std::mem_fn(& qx::test::CPerson::setPersonId), "fct_setPersonId"); + + pFct = t.fctStatic_1(& qx::test::CPerson::testStaticFct, "fct_testStaticFct"); + + QxValidatorX * pAllValidator = t.getAllValidator(); + if (! pAllValidator) { qAssert(false); return; } + pValidator = pAllValidator->add_NotEmpty("firstName", "a person must have a firstname"); + pValidator = pAllValidator->add_NotEmpty("lastName"); + pValidator = pAllValidator->add_MinDecimal("double", 0.5, "'double' field must be greater than or equal to '0.5'"); + pValidator = pAllValidator->add_MaxDecimal("double", 103.19); + pValidator = pAllValidator->add_CustomValidator(std::mem_fn(& qx::test::CPerson::isValid)); // using std::mem_fn() here is just a workaround for an issue with some versions of MSVC, it is not required with a full compliant C++11 compiler (http://stackoverflow.com/questions/23778883/vs2013-stdfunction-with-member-function) + pValidator = pAllValidator->add_CustomValidator_QVariant(& myGlobalValidator_1, "firstName"); + pValidator = pAllValidator->add_CustomValidator_DataType(& myGlobalValidator_2, "lastName"); +}} + +// ******************************************************************************************************** +// ******************************************************************************************************** + +// Example of global functions 'myGlobalValidator_1' and 'myGlobalValidator_2' used by 'QxValidator' module +// Those functions will be called automatically by validator engine of QxOrm library : +// - when you try to insert or update using 'qx::dao::xxx' functions +// - when you call 'qx::validate()' function + +void myGlobalValidator_1(const QVariant & value, const qx::IxValidator * validator, qx::QxInvalidValueX & invalidValues) +{ + // Here you can test the value (converted to QVariant type) + // If an invalid value is detected, just add a message into 'invalidValues' collection + + Q_UNUSED(value); Q_UNUSED(validator); Q_UNUSED(invalidValues); +} + +void myGlobalValidator_2(const QString & value, const qx::IxValidator * validator, qx::QxInvalidValueX & invalidValues) +{ + // Here you can test the value (with its real type, in this example, the data-member is a 'QString' type) + // If an invalid value is detected, just add a message into 'invalidValues' collection + + Q_UNUSED(value); Q_UNUSED(validator); Q_UNUSED(invalidValues); +} + +// ******************************************************************************************************** +// ******************************************************************************************************** diff --git a/test/qxDllSample/dll1/release/.gitignore b/test/qxDllSample/dll1/release/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxDllSample/dll1/release/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxDllSample/dll1/src/CPerson.cpp b/test/qxDllSample/dll1/src/CPerson.cpp new file mode 100644 index 0000000..f74ceb4 --- /dev/null +++ b/test/qxDllSample/dll1/src/CPerson.cpp @@ -0,0 +1,31 @@ +#include "../include/precompiled.h" + +#include "../include/CPerson.h" + +#include + +namespace qx { +namespace test { + +void CPerson::isValid(qx::QxInvalidValueX & invalidValues) +{ + // This method is called automatically by 'QxValidator' module (validator engine of QxOrm library) : + // - when you try to insert or update using 'qx::dao::xxx' functions + // - when you call 'qx::validate()' function + + // For registration, see 'pAllValidator->add_CustomValidator(& qx::test::CPerson::isValid);' into 'qx::register_class()' function + + // Here, you can verify some values of your instance + // If a value is not valid, you must add an invalid value into the collection 'invalidValues' + + if ((m_sFirstName == "admin") || (m_sLastName == "admin")) + { invalidValues.insert("you cannot set 'admin' for the name of a person"); } +} + +int CPerson::testStaticFct(const QString & s) +{ + return s.toInt(); +} + +} // namespace test +} // namespace qx diff --git a/test/qxDllSample/dll1/src/QxPersistable.cpp b/test/qxDllSample/dll1/src/QxPersistable.cpp new file mode 100644 index 0000000..d4a5dd7 --- /dev/null +++ b/test/qxDllSample/dll1/src/QxPersistable.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** https://www.qxorm.com/ +** Copyright (C) 2013 XDL Team (ic-east.com) +** +** This file is part of the QxOrm library +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any +** damages arising from the use of this software +** +** Commercial Usage +** Licensees holding valid commercial QxOrm licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and XDL Team +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file 'license.gpl3.txt' included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met : http://www.gnu.org/copyleft/gpl.html +** +** If you are unsure which license is appropriate for your use, or +** if you have questions regarding the use of this file, please contact : +** ic-east.com +** +****************************************************************************/ + +#include "../include/precompiled.h" + +#include "../include/QxPersistable.h" + +#include + +QX_REGISTER_COMPLEX_CLASS_NAME_CPP_QX_DLL1(qx::QxPersistable, qx_QxPersistable) + +namespace qx +{ + + template <> + void register_class(QxClass &t) + { + t.setPropertyBag("QX_NOT_PERSISTABLE", "1"); + + t.setSoftDelete(qx::QxSoftDelete("qx_deleted_at")); + + t.id(&qx::QxPersistable::m_qxId, "qx_id"); + + t.data(&qx::QxPersistable::m_qxDateCreation, "qx_date_creation"); + t.data(&qx::QxPersistable::m_qxDateModification, "qx_date_modification"); + + QxValidatorX *pAllValidator = t.getAllValidator(); + pAllValidator->add_CustomValidator(std::mem_fn(&qx::QxPersistable::qxIsValidInternal)); // using std::mem_fn() here is just a workaround for an issue with some versions of MSVC, it is not required with a full compliant C++11 compiler (http://stackoverflow.com/questions/23778883/vs2013-stdfunction-with-member-function) + } + + QX_PERSISTABLE_CPP(QxPersistable) + + QxPersistable::QxPersistable() : QObject(), qx::IxPersistable(), m_qxId(0) { ; } + + QxPersistable::~QxPersistable() { ; } + + long QxPersistable::qxGetId() const { return m_qxId; } + + QDateTime QxPersistable::qxGetDateCreation() const { return m_qxDateCreation.toDateTime(); } + + QDateTime QxPersistable::qxGetDateModification() const { return m_qxDateModification.toDateTime(); } + + void QxPersistable::qxSetId(long l) { m_qxId = l; } + + void QxPersistable::qxSetDateCreation(const QDateTime &dt) { m_qxDateCreation.fromDateTime(dt); } + + void QxPersistable::qxSetDateModification(const QDateTime &dt) { m_qxDateModification.fromDateTime(dt); } + + void QxPersistable::qxIsValidInternal(qx::QxInvalidValueX &invalidValues) { this->qxIsValid(invalidValues); } + + void QxPersistable::qxIsValid(qx::QxInvalidValueX &invalidValues) { Q_UNUSED(invalidValues); } + + void QxPersistable::qxOnBeforeInsert(qx::dao::detail::IxDao_Helper *dao) + { + Q_UNUSED(dao); + m_qxDateCreation.fromDateTime(QDateTime::currentDateTime()); + m_qxDateModification = m_qxDateCreation; + Q_EMIT qxOnBeforeInsert(this); + } + + void QxPersistable::qxOnBeforeUpdate(qx::dao::detail::IxDao_Helper *dao) + { + Q_UNUSED(dao); + m_qxDateModification.fromDateTime(QDateTime::currentDateTime()); + Q_EMIT qxOnBeforeUpdate(this); + } + + void QxPersistable::qxOnBeforeDelete(qx::dao::detail::IxDao_Helper *dao) + { + Q_UNUSED(dao); + Q_EMIT qxOnBeforeDelete(this); + } + + void QxPersistable::qxOnBeforeFetch(qx::dao::detail::IxDao_Helper *dao) + { + Q_UNUSED(dao); + Q_EMIT qxOnBeforeFetch(this); + } + + void QxPersistable::qxOnAfterInsert(qx::dao::detail::IxDao_Helper *dao) + { + Q_UNUSED(dao); + Q_EMIT qxOnAfterInsert(this); + } + + void QxPersistable::qxOnAfterUpdate(qx::dao::detail::IxDao_Helper *dao) + { + Q_UNUSED(dao); + Q_EMIT qxOnAfterUpdate(this); + } + + void QxPersistable::qxOnAfterDelete(qx::dao::detail::IxDao_Helper *dao) + { + Q_UNUSED(dao); + Q_EMIT qxOnAfterDelete(this); + } + + void QxPersistable::qxOnAfterFetch(qx::dao::detail::IxDao_Helper *dao) + { + Q_UNUSED(dao); + Q_EMIT qxOnAfterFetch(this); + } + +} // namespace qx diff --git a/test/qxDllSample/dll1/src/TestQtProperty.cpp b/test/qxDllSample/dll1/src/TestQtProperty.cpp new file mode 100644 index 0000000..fc7d184 --- /dev/null +++ b/test/qxDllSample/dll1/src/TestQtProperty.cpp @@ -0,0 +1,24 @@ +#include "../include/precompiled.h" + +#include "../include/TestQtProperty.h" + +#include + +QX_REGISTER_CPP_QX_DLL1(TestQtProperty) +QX_REGISTER_ALL_QT_PROPERTIES(TestQtProperty, "id") + +/* + Instead of using 'QX_REGISTER_ALL_QT_PROPERTIES(...)' macro, it's also + possible to write classic 'void qx::register_class(...)' function, like this : + + namespace qx { + template <> void register_class(QxClass & t) + { qx::register_all_qt_properties(t, "my_id"); } + } // namespace qx + + So, you can mix Qt meta-properties and classic registration data-member. + All is stored using the same interface : 'qx::IxDataMember *'. + To more details about advantages and disadvantages of using Qt meta-property, + go to the FAQ of QxOrm library : + - How to register automatically Qt meta-properties to QxOrm context ? +*/ diff --git a/test/qxDllSample/dll1/src/main.cpp b/test/qxDllSample/dll1/src/main.cpp new file mode 100644 index 0000000..19c39b1 --- /dev/null +++ b/test/qxDllSample/dll1/src/main.cpp @@ -0,0 +1,20 @@ +#ifdef _MSC_VER + +#include + +extern "C" +int WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /* lpReservedt */) +{ + (void)hInstance; + switch (dwReason) + { + case DLL_PROCESS_ATTACH: ::OutputDebugStringA("dll1.DllMain() ---> DLL_PROCESS_ATTACH\n"); break; + case DLL_PROCESS_DETACH: ::OutputDebugStringA("dll1.DllMain() ---> DLL_PROCESS_DETACH\n"); break; + case DLL_THREAD_ATTACH: ::OutputDebugStringA("dll1.DllMain() ---> DLL_THREAD_ATTACH\n"); break; + case DLL_THREAD_DETACH: ::OutputDebugStringA("dll1.DllMain() ---> DLL_THREAD_DETACH\n"); break; + } + + return 1; +} + +#endif // _MSC_VER diff --git a/test/qxDllSample/dll2/CMakeLists.txt b/test/qxDllSample/dll2/CMakeLists.txt new file mode 100644 index 0000000..50f6ea4 --- /dev/null +++ b/test/qxDllSample/dll2/CMakeLists.txt @@ -0,0 +1,63 @@ +cmake_minimum_required(VERSION 3.1) + +project(dll2 LANGUAGES CXX) + +include(../../../QxOrm.cmake) + +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Sql Gui REQUIRED) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_DEBUG_POSTFIX "d") + +set(HEADERS + ./include/precompiled.h + ./include/export.h + ./include/CUser.h + ./include/CTestAll.h + ./include/Bar.h + ./include/Foo.h + ./include/BaseClassTrigger.h + ) + +set(SRCS + ./qx/CUser.qx.cpp + ./qx/CTestAll.qx.cpp + ./qx/Bar.qx.cpp + ./qx/Foo.qx.cpp + ./qx/BaseClassTrigger.qx.cpp + ./src/CUser.cpp + ./src/CTestAll.cpp + ./src/Bar.cpp + ./src/Foo.cpp + ./src/BaseClassTrigger.cpp + ./src/main.cpp + ) + +add_library(dll2 SHARED ${SRCS} ${HEADERS}) + +target_compile_definitions(dll2 PRIVATE -D_QX_BUILDING_DLL2) + +if(COMMAND target_precompile_headers) + target_precompile_headers(dll2 PRIVATE ./include/precompiled.h) +endif() # (COMMAND target_precompile_headers) + +target_link_libraries(dll2 ${QX_LIBRARIES} Qt${QT_VERSION_MAJOR}::Gui QxOrm dll1) + +set_target_properties(dll2 PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ) diff --git a/test/qxDllSample/dll2/debug/.gitignore b/test/qxDllSample/dll2/debug/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxDllSample/dll2/debug/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxDllSample/dll2/dll2.pro b/test/qxDllSample/dll2/dll2.pro new file mode 100644 index 0000000..d52e7db --- /dev/null +++ b/test/qxDllSample/dll2/dll2.pro @@ -0,0 +1,50 @@ +include(../../../QxOrm.pri) + +TEMPLATE = lib +CONFIG += dll +DEFINES += _QX_BUILDING_DLL2 +INCLUDEPATH += ../../../../QxOrm/include/ +DESTDIR = ../../../../QxOrm/test/_bin/ +QT += gui + +!contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) { +PRECOMPILED_HEADER = ./include/precompiled.h +} # !contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) + +CONFIG(debug, debug|release) { +TARGET = dll2d +} else { +TARGET = dll2 +} # CONFIG(debug, debug|release) + +LIBS += -L"../../../../QxOrm/lib" +LIBS += -L"../../../../QxOrm/test/_bin" + +CONFIG(debug, debug|release) { +LIBS += -l"QxOrmd" +LIBS += -l"dll1d" +} else { +LIBS += -l"QxOrm" +LIBS += -l"dll1" +} # CONFIG(debug, debug|release) + +HEADERS += ./include/precompiled.h +HEADERS += ./include/export.h +HEADERS += ./include/CUser.h +HEADERS += ./include/CTestAll.h +HEADERS += ./include/Bar.h +HEADERS += ./include/Foo.h +HEADERS += ./include/BaseClassTrigger.h + +SOURCES += ./qx/CUser.qx.cpp +SOURCES += ./qx/CTestAll.qx.cpp +SOURCES += ./qx/Bar.qx.cpp +SOURCES += ./qx/Foo.qx.cpp +SOURCES += ./qx/BaseClassTrigger.qx.cpp + +SOURCES += ./src/CUser.cpp +SOURCES += ./src/CTestAll.cpp +SOURCES += ./src/Bar.cpp +SOURCES += ./src/Foo.cpp +SOURCES += ./src/BaseClassTrigger.cpp +SOURCES += ./src/main.cpp diff --git a/test/qxDllSample/dll2/include/Bar.h b/test/qxDllSample/dll2/include/Bar.h new file mode 100644 index 0000000..36500e9 --- /dev/null +++ b/test/qxDllSample/dll2/include/Bar.h @@ -0,0 +1,48 @@ +#ifndef _QX_BAR_H_ +#define _QX_BAR_H_ + +#include "../include/BaseClassTrigger.h" + +class Foo; + +class QX_DLL2_EXPORT Bar : public BaseClassTrigger +{ + + QX_REGISTER_FRIEND_CLASS(Bar) + QX_PERSISTABLE_HPP(Bar) + +public: + + typedef std::shared_ptr Foo_ptr; + +protected: + + QString m_sCode; + QString m_sValue; + Foo_ptr m_pFoo; + +public: + + Bar() : BaseClassTrigger() { ; } + virtual ~Bar() { ; } + + virtual void makeAbstractClass() { ; } + + QString getCode() const { return m_sCode; } + QString getValue() const { return m_sValue; } + Foo_ptr getFoo() const { return m_pFoo; } + + void setCode(const QString & s) { m_sCode = s; } + void setValue(const QString & s) { m_sValue = s; } + void setFoo(Foo_ptr p) { m_pFoo = p; } + void setFoo(long l); + +}; + +QX_REGISTER_HPP_QX_DLL2(Bar, BaseClassTrigger, 0) + +typedef std::shared_ptr Bar_ptr; +typedef std::vector BarX; +typedef std::shared_ptr BarX_ptr; + +#endif // _QX_BAR_H_ diff --git a/test/qxDllSample/dll2/include/BaseClassTrigger.h b/test/qxDllSample/dll2/include/BaseClassTrigger.h new file mode 100644 index 0000000..4d9ae41 --- /dev/null +++ b/test/qxDllSample/dll2/include/BaseClassTrigger.h @@ -0,0 +1,67 @@ +#ifndef _QX_BASE_CLASS_TRIGGER_H_ +#define _QX_BASE_CLASS_TRIGGER_H_ + +class QX_DLL2_EXPORT BaseClassTrigger : public qx::IxPersistable +{ + + QX_REGISTER_FRIEND_CLASS(BaseClassTrigger) + +protected: + + long m_id; + QDateTime m_dateCreation; + QDateTime m_dateModification; + QString m_userCreation; + QString m_userModification; + +public: + + BaseClassTrigger() : m_id(0) { ; } + virtual ~BaseClassTrigger() { ; } + + virtual void makeAbstractClass() = 0; + + long getId() const { return m_id; } + QDateTime getDateCreation() const { return m_dateCreation; } + QDateTime getDateModification() const { return m_dateModification; } + QString getUserCreation() const { return m_userCreation; } + QString getUserModification() const { return m_userModification; } + + void setId(long l) { m_id = l; } + void setDateCreation(const QDateTime & dt) { m_dateCreation = dt; } + void setDateModification(const QDateTime & dt) { m_dateModification = dt; } + void setUserCreation(const QString & s) { m_userCreation = s; } + void setUserModification(const QString & s) { m_userModification = s; } + + void onBeforeInsert(qx::dao::detail::IxDao_Helper * dao); + void onBeforeUpdate(qx::dao::detail::IxDao_Helper * dao); + +}; + +QX_REGISTER_ABSTRACT_CLASS(BaseClassTrigger) +QX_REGISTER_HPP_QX_DLL2(BaseClassTrigger, qx::trait::no_base_class_defined, 0) + +namespace qx { +namespace dao { +namespace detail { + +template <> +struct QxDao_Trigger +{ + + static inline void onBeforeInsert(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao) { if (t) { t->onBeforeInsert(dao); } } + static inline void onBeforeUpdate(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao) { if (t) { t->onBeforeUpdate(dao); } } + static inline void onBeforeDelete(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao) { Q_UNUSED(t); Q_UNUSED(dao); } + static inline void onBeforeFetch(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao) { Q_UNUSED(t); Q_UNUSED(dao); } + static inline void onAfterInsert(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao) { Q_UNUSED(t); Q_UNUSED(dao); } + static inline void onAfterUpdate(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao) { Q_UNUSED(t); Q_UNUSED(dao); } + static inline void onAfterDelete(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao) { Q_UNUSED(t); Q_UNUSED(dao); } + static inline void onAfterFetch(BaseClassTrigger * t, qx::dao::detail::IxDao_Helper * dao) { Q_UNUSED(t); Q_UNUSED(dao); } + +}; + +} // namespace detail +} // namespace dao +} // namespace qx + +#endif // _QX_BASE_CLASS_TRIGGER_H_ diff --git a/test/qxDllSample/dll2/include/CTestAll.h b/test/qxDllSample/dll2/include/CTestAll.h new file mode 100644 index 0000000..b87b156 --- /dev/null +++ b/test/qxDllSample/dll2/include/CTestAll.h @@ -0,0 +1,105 @@ +#ifndef _QX_CLASS_TEST_ALL_H_ +#define _QX_CLASS_TEST_ALL_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#include +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +#include "../../dll1/include/CPerson.h" +#include "../../dll2/include/CUser.h" + +class QX_DLL2_EXPORT CTestAll +{ + + QX_REGISTER_FRIEND_CLASS(CTestAll) + +protected: + + long m_lId; + qx_bool m_oQxBool; + + std::string m_oStdString; + std::pair m_oStdPair; + std::vector< QSharedPointer > m_oStdVector; + std::list m_oStdList; + std::map > m_oStdMap; + std::shared_ptr m_oBoostSharedPtr; + +#ifdef _QX_ENABLE_BOOST + boost::tuple m_oBoostTuple; + boost::scoped_ptr m_oBoostScopedPtr; + boost::weak_ptr m_oBoostWeakPtr; + boost::unordered_map m_oBoostUnorderedMap; +#endif // _QX_ENABLE_BOOST + + QString m_oQString; + QObject m_oQObject; + QBrush m_oQBrush; + QByteArray m_oQByteArray; + QColor m_oQColor; + QDate m_oQDate; + QDateTime m_oQDateTime; + QTime m_oQTime; + QFont m_oQFont; + QImage m_oQImage; + QPicture m_oQPicture; + QPoint m_oQPoint; + QRect m_oQRect; + QSize m_oQSize; + QRegion m_oQRegion; + QUrl m_oQUrl; + QVariant m_oQVariant; + QStringList m_oQStringList; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + QMatrix m_oQMatrix; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + QRegExp m_oQRegExp; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + QHash m_oQHash; + QMultiHash > m_oQMultiHash; + QMap m_oQMap; + QMultiMap > m_oQMultiMap; + qx::QxCollection > m_oQxCollection; + + QList m_oQList; + QVector m_oQVector; + QSharedPointer > > m_oQSharedPointer; + QPair m_oQPair; + +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + QLinkedList m_oQLinkedList; +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + +public: + + CTestAll() { init(); } + virtual ~CTestAll() { terminate(); } + + static void test(); + +private: + + void init(); + void terminate(); + +}; + +QX_REGISTER_HPP_QX_DLL2(CTestAll, qx::trait::no_base_class_defined, 1) + +#endif // _QX_CLASS_TEST_ALL_H_ diff --git a/test/qxDllSample/dll2/include/CUser.h b/test/qxDllSample/dll2/include/CUser.h new file mode 100644 index 0000000..47abc64 --- /dev/null +++ b/test/qxDllSample/dll2/include/CUser.h @@ -0,0 +1,54 @@ +#ifndef _QX_CLASS_USER_H_ +#define _QX_CLASS_USER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#ifdef _QX_NO_PRECOMPILED_HEADER +#ifndef Q_MOC_RUN +#include "../include/precompiled.h" // Need to include precompiled header for the generated moc file +#endif // Q_MOC_RUN +#endif // _QX_NO_PRECOMPILED_HEADER + +#include "../../dll1/include/CPerson.h" + +class QX_DLL2_EXPORT CUser : public qx::test::CPerson +{ + + Q_OBJECT + QX_REGISTER_FRIEND_CLASS(CUser) + +protected: + + long m_lUserId; + QString m_sProfil; + qx_bool m_bIsAdmin; + QDateTime m_dtModif; + qx::test::CPerson * m_pBrother; + +public: + + CUser() : qx::test::CPerson(), m_lUserId(0), m_pBrother(NULL) { ; } + CUser(long lId) : qx::test::CPerson(), m_lUserId(lId), m_pBrother(NULL) { ; } + virtual ~CUser() { if (m_pBrother) { delete m_pBrother; m_pBrother = NULL; } } + + void test(); + + long getUserId() const { return m_lUserId; } + QString getProfil() const { return m_sProfil; } + qx_bool getIsAdmin() const { return m_bIsAdmin; } + QDateTime getDateModif() const { return m_dtModif; } + qx::test::CPerson * getBrother() const { return m_pBrother; } + + void setUserId(long l) { m_lUserId = l; } + void setProfil(const QString & s) { m_sProfil = s; } + void setIsAdmin(qx_bool b) { m_bIsAdmin = b; } + void setDateModif(const QDateTime & dt) { m_dtModif = dt; } + void setBrother(qx::test::CPerson * p) { if (m_pBrother) { delete m_pBrother; } m_pBrother = p; } + +}; + +QX_REGISTER_HPP_QX_DLL2(CUser, qx::test::CPerson, 2) + +#endif // _QX_CLASS_USER_H_ diff --git a/test/qxDllSample/dll2/include/Foo.h b/test/qxDllSample/dll2/include/Foo.h new file mode 100644 index 0000000..2a35caf --- /dev/null +++ b/test/qxDllSample/dll2/include/Foo.h @@ -0,0 +1,67 @@ +#ifndef _QX_FOO_H_ +#define _QX_FOO_H_ + +#include "../include/BaseClassTrigger.h" +#include "../include/Bar.h" + +class QX_DLL2_EXPORT Foo : public BaseClassTrigger +{ + + QX_REGISTER_FRIEND_CLASS(Foo) + QX_PERSISTABLE_HPP(Foo) + +protected: + + QString m_sName; + QVariant m_sDesc; + BarX_ptr m_pBarX; + + qx::QxDateNeutral m_oDateNeutral; + qx::QxTimeNeutral m_oTimeNeutral; + qx::QxDateTimeNeutral m_oDateTimeNeutral; + +#ifdef _QX_ENABLE_BOOST + boost::optional m_optInt; + boost::optional m_optString; +#endif // _QX_ENABLE_BOOST + +public: + + Foo() : BaseClassTrigger() { ; } + virtual ~Foo() { ; } + + virtual void makeAbstractClass() { ; } + + QString getName() const { return m_sName; } + QVariant getDesc() const { return m_sDesc; } + BarX * getBarX() const { return m_pBarX.get(); } + QDate getDate() const { return m_oDateNeutral.toDate(); } + QTime getTime() const { return m_oTimeNeutral.toTime(); } + QDateTime getDateTime() const { return m_oDateTimeNeutral.toDateTime(); } + +#ifdef _QX_ENABLE_BOOST + boost::optional getOptInt() const { return m_optInt; } + boost::optional getOptString() const { return m_optString; } +#endif // _QX_ENABLE_BOOST + + void setName(const QString & s) { m_sName = s; } + void setDesc(const QVariant & s) { m_sDesc = s; } + void setBarX(BarX_ptr p) { m_pBarX = p; } + void setDate(const QDate & d) { m_oDateNeutral.setDate(d); } + void setTime(const QTime & t) { m_oTimeNeutral.setTime(t); } + void setDateTime(const QDateTime & dt) { m_oDateTimeNeutral.setDateTime(dt); } + +#ifdef _QX_ENABLE_BOOST + void setOptInt(const boost::optional & opt) { m_optInt = opt; } + void setOptString(const boost::optional & opt) { m_optString = opt; } +#endif // _QX_ENABLE_BOOST + +}; + +QX_REGISTER_HPP_QX_DLL2(Foo, BaseClassTrigger, 0) + +typedef std::shared_ptr Foo_ptr; +typedef qx::QxCollection FooX; +typedef std::shared_ptr FooX_ptr; + +#endif // _QX_FOO_H_ diff --git a/test/qxDllSample/dll2/include/export.h b/test/qxDllSample/dll2/include/export.h new file mode 100644 index 0000000..e5bbb4c --- /dev/null +++ b/test/qxDllSample/dll2/include/export.h @@ -0,0 +1,28 @@ +#ifndef _QX_DLL2_EXPORT_H_ +#define _QX_DLL2_EXPORT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include "../../dll1/include/export.h" + +#ifdef _QX_BUILDING_DLL2 +#define QX_DLL2_EXPORT QX_DLL_EXPORT_HELPER +#else // _QX_BUILDING_DLL2 +#define QX_DLL2_EXPORT QX_DLL_IMPORT_HELPER +#endif // _QX_BUILDING_DLL2 + +#ifdef _QX_BUILDING_DLL2 +#define QX_REGISTER_HPP_QX_DLL2 QX_REGISTER_HPP_EXPORT_DLL +#define QX_REGISTER_CPP_QX_DLL2 QX_REGISTER_CPP_EXPORT_DLL +#define QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QX_DLL2 QX_REGISTER_COMPLEX_CLASS_NAME_HPP_EXPORT_DLL +#define QX_REGISTER_COMPLEX_CLASS_NAME_CPP_QX_DLL2 QX_REGISTER_COMPLEX_CLASS_NAME_CPP_EXPORT_DLL +#else // _QX_BUILDING_DLL2 +#define QX_REGISTER_HPP_QX_DLL2 QX_REGISTER_HPP_IMPORT_DLL +#define QX_REGISTER_CPP_QX_DLL2 QX_REGISTER_CPP_IMPORT_DLL +#define QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QX_DLL2 QX_REGISTER_COMPLEX_CLASS_NAME_HPP_IMPORT_DLL +#define QX_REGISTER_COMPLEX_CLASS_NAME_CPP_QX_DLL2 QX_REGISTER_COMPLEX_CLASS_NAME_CPP_IMPORT_DLL +#endif // _QX_BUILDING_DLL2 + +#endif // _QX_DLL2_EXPORT_H_ diff --git a/test/qxDllSample/dll2/include/precompiled.h b/test/qxDllSample/dll2/include/precompiled.h new file mode 100644 index 0000000..54b6852 --- /dev/null +++ b/test/qxDllSample/dll2/include/precompiled.h @@ -0,0 +1,12 @@ +#ifndef _QX_DLL2_PRECOMPILED_HEADER_H_ +#define _QX_DLL2_PRECOMPILED_HEADER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#include "../include/export.h" + +#endif // _QX_DLL2_PRECOMPILED_HEADER_H_ diff --git a/test/qxDllSample/dll2/qt/moc/.gitignore b/test/qxDllSample/dll2/qt/moc/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxDllSample/dll2/qt/moc/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxDllSample/dll2/qt/rcc/src/.gitignore b/test/qxDllSample/dll2/qt/rcc/src/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxDllSample/dll2/qt/rcc/src/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxDllSample/dll2/qt/ui/include/.gitignore b/test/qxDllSample/dll2/qt/ui/include/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxDllSample/dll2/qt/ui/include/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxDllSample/dll2/qt/ui/src/.gitignore b/test/qxDllSample/dll2/qt/ui/src/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxDllSample/dll2/qt/ui/src/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxDllSample/dll2/qx/Bar.qx.cpp b/test/qxDllSample/dll2/qx/Bar.qx.cpp new file mode 100644 index 0000000..315beca --- /dev/null +++ b/test/qxDllSample/dll2/qx/Bar.qx.cpp @@ -0,0 +1,23 @@ +#include "../include/precompiled.h" + +#include "../include/Bar.h" +#include "../include/Foo.h" + +#include + +QX_REGISTER_CPP_QX_DLL2(Bar) +QX_PERSISTABLE_CPP(Bar) + +namespace qx { +template <> void register_class(QxClass & t) +{ + IxDataMember * pData = NULL; + IxSqlRelation * pRelation = NULL; + + t.setSoftDelete(qx::QxSoftDelete("deleted_at")); + + pData = t.data(& Bar::m_sCode, "code"); + pData = t.data(& Bar::m_sValue, "value"); + + pRelation = t.relationManyToOne(& Bar::m_pFoo, "foo_id"); +}} diff --git a/test/qxDllSample/dll2/qx/BaseClassTrigger.qx.cpp b/test/qxDllSample/dll2/qx/BaseClassTrigger.qx.cpp new file mode 100644 index 0000000..857d809 --- /dev/null +++ b/test/qxDllSample/dll2/qx/BaseClassTrigger.qx.cpp @@ -0,0 +1,20 @@ +#include "../include/precompiled.h" + +#include "../include/BaseClassTrigger.h" + +#include + +QX_REGISTER_CPP_QX_DLL2(BaseClassTrigger) + +namespace qx { +template <> void register_class(QxClass & t) +{ + IxDataMember * pData = NULL; + + pData = t.id(& BaseClassTrigger::m_id, "id"); + + pData = t.data(& BaseClassTrigger::m_dateCreation, "date_creation"); + pData = t.data(& BaseClassTrigger::m_dateModification, "date_modification"); + pData = t.data(& BaseClassTrigger::m_userCreation, "user_creation"); + pData = t.data(& BaseClassTrigger::m_userModification, "user_modification"); +}} diff --git a/test/qxDllSample/dll2/qx/CTestAll.qx.cpp b/test/qxDllSample/dll2/qx/CTestAll.qx.cpp new file mode 100644 index 0000000..21a7de9 --- /dev/null +++ b/test/qxDllSample/dll2/qx/CTestAll.qx.cpp @@ -0,0 +1,75 @@ +#include "../include/precompiled.h" + +#include "../include/CTestAll.h" + +#include + +QX_REGISTER_CPP_QX_DLL2(CTestAll) + +namespace qx { +template <> void register_class(QxClass & t) +{ + IxDataMember * pData = NULL; + + pData = t.id(& CTestAll::m_lId, "m_lId"); + + pData = t.data(& CTestAll::m_oQxBool, "m_oQxBool"); + pData = t.data(& CTestAll::m_oStdString, "m_oStdString"); + pData = t.data(& CTestAll::m_oStdPair, "m_oStdPair"); + pData = t.data(& CTestAll::m_oStdVector, "m_oStdVector"); + pData = t.data(& CTestAll::m_oStdList, "m_oStdList"); + pData = t.data(& CTestAll::m_oStdMap, "m_oStdMap"); + pData = t.data(& CTestAll::m_oQString, "m_oQString"); + pData = t.data(& CTestAll::m_oQObject, "m_oQObject"); + pData = t.data(& CTestAll::m_oQByteArray, "m_oQByteArray"); + pData = t.data(& CTestAll::m_oQDate, "m_oQDate"); + pData = t.data(& CTestAll::m_oQDateTime, "m_oQDateTime"); + pData = t.data(& CTestAll::m_oQTime, "m_oQTime"); + +#ifdef _QX_ENABLE_BOOST + pData = t.data(& CTestAll::m_oBoostTuple, "m_oBoostTuple"); + pData = t.data(& CTestAll::m_oBoostScopedPtr, "m_oBoostScopedPtr", 1); + pData = t.data(& CTestAll::m_oBoostUnorderedMap, "m_oBoostUnorderedMap", 1); +#endif // _QX_ENABLE_BOOST + +#ifdef _QX_ENABLE_QT_GUI + pData = t.data(& CTestAll::m_oQBrush, "m_oQBrush"); + pData = t.data(& CTestAll::m_oQColor, "m_oQColor"); + pData = t.data(& CTestAll::m_oQFont, "m_oQFont", 0); + pData = t.data(& CTestAll::m_oQImage, "m_oQImage", 0); + pData = t.data(& CTestAll::m_oQPicture, "m_oQPicture", 0); + pData = t.data(& CTestAll::m_oQRegion, "m_oQRegion", 0); +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + pData = t.data(& CTestAll::m_oQMatrix, "m_oQMatrix", 1); +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#endif // _QX_ENABLE_QT_GUI + +#if (0) /* ((! defined(Q_OS_WIN)) || (! defined(__GNUC__))) // MinGW memory bug ? */ + pData = t.data(& CTestAll::m_oQPoint, "m_oQPoint", 0); + pData = t.data(& CTestAll::m_oQRect, "m_oQRect", 0); + pData = t.data(& CTestAll::m_oQUrl, "m_oQUrl", 0); + pData = t.data(& CTestAll::m_oQVariant, "m_oQVariant", 0); + pData = t.data(& CTestAll::m_oQStringList, "m_oQStringList", 0); + pData = t.data(& CTestAll::m_oQHash, "m_oQHash", 0); + pData = t.data(& CTestAll::m_oQMultiHash, "m_oQMultiHash", 0); + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + pData = t.data(& CTestAll::m_oQRegExp, "m_oQRegExp", 0); +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + pData = t.data(& CTestAll::m_oQMap, "m_oQMap", 1); + pData = t.data(& CTestAll::m_oQMultiMap, "m_oQMultiMap", 1); + pData = t.data(& CTestAll::m_oQxCollection, "m_oQxCollection", 1); + pData = t.data(& CTestAll::m_oQList, "m_oQList", 1); + pData = t.data(& CTestAll::m_oQVector, "m_oQVector", 1); + pData = t.data(& CTestAll::m_oQPair, "m_oQPair", 1); + pData = t.data(& CTestAll::m_oBoostSharedPtr, "m_oBoostSharedPtr", 1); + pData = t.data(& CTestAll::m_oQSharedPointer, "m_oQSharedPointer", 1); + pData = t.data(& CTestAll::m_oQSize, "m_oQSize", 1); + +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + pData = t.data(& CTestAll::m_oQLinkedList, "m_oQLinkedList", 1); +#endif // (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + +#endif // ((! defined(Q_OS_WIN)) || (! defined(__GNUC__))) +}} diff --git a/test/qxDllSample/dll2/qx/CUser.qx.cpp b/test/qxDllSample/dll2/qx/CUser.qx.cpp new file mode 100644 index 0000000..5f4a6c7 --- /dev/null +++ b/test/qxDllSample/dll2/qx/CUser.qx.cpp @@ -0,0 +1,26 @@ +#include "../include/precompiled.h" + +#include "../include/CUser.h" + +#include + +QX_REGISTER_CPP_QX_DLL2(CUser) + +namespace qx { +template <> void register_class(QxClass & t) +{ + IxDataMember * pData = NULL; + IxSqlRelation * pRelation = NULL; + IxValidator * pValidator = NULL; + + pData = t.data(& CUser::m_lUserId, "idUser", 0); + pData = t.data(& CUser::m_sProfil, "profil", 0); pData->setSqlAlias("profil"); + pData = t.data(& CUser::m_bIsAdmin, "isAdmin", 1); + pData = t.data(& CUser::m_dtModif, "dtModif", 1); + + pRelation = t.relationManyToOne(& CUser::m_pBrother, "brother", 2); + + QxValidatorX * pAllValidator = t.getAllValidator(); + if (! pAllValidator) { qAssert(false); return; } + pValidator = pAllValidator->add_RecursiveValidator("brother"); +}} diff --git a/test/qxDllSample/dll2/qx/Foo.qx.cpp b/test/qxDllSample/dll2/qx/Foo.qx.cpp new file mode 100644 index 0000000..2558f62 --- /dev/null +++ b/test/qxDllSample/dll2/qx/Foo.qx.cpp @@ -0,0 +1,28 @@ +#include "../include/precompiled.h" + +#include "../include/Foo.h" + +#include + +QX_REGISTER_CPP_QX_DLL2(Foo) +QX_PERSISTABLE_CPP(Foo) + +namespace qx { +template <> void register_class(QxClass & t) +{ + IxDataMember * pData = NULL; + IxSqlRelation * pRelation = NULL; + + pData = t.data(& Foo::m_sName, "name"); + pData = t.data(& Foo::m_sDesc, "desc"); + pData = t.data(& Foo::m_oDateNeutral, "date_neutral"); + pData = t.data(& Foo::m_oTimeNeutral, "time_neutral"); + pData = t.data(& Foo::m_oDateTimeNeutral, "dt_neutral"); + +#ifdef _QX_ENABLE_BOOST + pData = t.data(& Foo::m_optInt, "optional_int"); + pData = t.data(& Foo::m_optString, "optional_string"); +#endif // _QX_ENABLE_BOOST + + pRelation = t.relationOneToMany(& Foo::m_pBarX, "lstBar", "foo_id"); +}} diff --git a/test/qxDllSample/dll2/release/.gitignore b/test/qxDllSample/dll2/release/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxDllSample/dll2/release/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxDllSample/dll2/src/Bar.cpp b/test/qxDllSample/dll2/src/Bar.cpp new file mode 100644 index 0000000..e4a5aac --- /dev/null +++ b/test/qxDllSample/dll2/src/Bar.cpp @@ -0,0 +1,12 @@ +#include "../include/precompiled.h" + +#include "../include/Bar.h" +#include "../include/Foo.h" + +#include + +void Bar::setFoo(long l) +{ + m_pFoo.reset(new Foo()); + m_pFoo->setId(l); +} diff --git a/test/qxDllSample/dll2/src/BaseClassTrigger.cpp b/test/qxDllSample/dll2/src/BaseClassTrigger.cpp new file mode 100644 index 0000000..0f80f22 --- /dev/null +++ b/test/qxDllSample/dll2/src/BaseClassTrigger.cpp @@ -0,0 +1,21 @@ +#include "../include/precompiled.h" + +#include "../include/BaseClassTrigger.h" + +#include + +void BaseClassTrigger::onBeforeInsert(qx::dao::detail::IxDao_Helper * dao) +{ + Q_UNUSED(dao); + m_dateCreation = QDateTime::currentDateTime(); + m_dateModification = QDateTime::currentDateTime(); + m_userCreation = "current_user_1"; + m_userModification = "current_user_1"; +} + +void BaseClassTrigger::onBeforeUpdate(qx::dao::detail::IxDao_Helper * dao) +{ + Q_UNUSED(dao); + m_dateModification = QDateTime::currentDateTime(); + m_userModification = "current_user_2"; +} diff --git a/test/qxDllSample/dll2/src/CTestAll.cpp b/test/qxDllSample/dll2/src/CTestAll.cpp new file mode 100644 index 0000000..fc4e462 --- /dev/null +++ b/test/qxDllSample/dll2/src/CTestAll.cpp @@ -0,0 +1,89 @@ +#include "../include/precompiled.h" + +#include "../include/CTestAll.h" + +#include + +void CTestAll::init() +{ + m_lId = 56; + m_oQxBool = qx_bool(true, "qx_bool description"); + m_oStdString = "std::string text"; + + m_oQString = "QString text\nwith a new line"; + m_oQDate = QDate::currentDate(); + m_oQDateTime = QDateTime::currentDateTime(); + m_oQTime = QTime::currentTime(); + m_oQColor = QColor(255, 200, 200); + m_oQFont.setBold(true); + m_oQFont.setFamily("font family from qx test"); + m_oQPoint = QPoint(8, 11); + m_oQRect = QRect(QPoint(52, 3), QPoint(574, 1112)); + m_oQSize = QSize(1024, 768); + m_oQUrl = QUrl("www.qxorm.com"); + m_oQStringList << "str1" << "str2" << "str3" << "str4"; + m_oQByteArray = QByteArray("QByteArray t\0ext\nwith a new line\0and null multi\0ple null char\0", 55); + m_oQVariant = QVariant(0.437); + m_oQObject.setProperty("prop_1", 1); + m_oQObject.setProperty("prop_2", 2); + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + m_oQRegExp = QRegExp("qt reg exp"); +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + + QSharedPointer u1(new CUser()); + QSharedPointer u2(new CUser()); + + m_oStdPair = std::make_pair(QPoint(2, 6), QRect(99, 55, 44, 22)); + m_oStdList.push_back(QDateTime()); + m_oStdList.push_back(QDateTime::currentDateTime()); + m_oStdList.push_back(QDateTime()); + m_oStdMap.insert(std::make_pair("1", u1)); + m_oStdMap.insert(std::make_pair("2", u2)); + + m_oStdVector.push_back(QSharedPointer(new qx::test::CPerson())); + m_oStdVector.push_back(QSharedPointer(new CUser())); +} + +void CTestAll::terminate() +{ + Q_FOREACH(CUser * p2, m_oQVector) { if (p2) { delete p2; } } +} + +void CTestAll::test() +{ + std::shared_ptr o1; o1.reset(new CTestAll()); + std::shared_ptr o2; o2.reset(new CTestAll()); + + qx::QxCollection > coll; + coll.insert(0, o1); + coll.insert(1, o2); + +#if _QX_SERIALIZE_POLYMORPHIC + qx::serialization::polymorphic_xml::to_file(coll, "test_all.xml"); + qx::serialization::polymorphic_xml::from_file(coll, "test_all.xml"); +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY + qx::serialization::binary::to_file(coll, "test_all.bin"); + qx::serialization::binary::from_file(coll, "test_all.bin"); +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_XML + qx::serialization::xml::to_file(coll, "test_all.xml"); + qx::serialization::xml::from_file(coll, "test_all.xml"); +#endif // _QX_SERIALIZE_XML + +#if _QX_SERIALIZE_PORTABLE_BINARY + qx::serialization::portable_binary::to_file(coll, "test_all.bin2", 0); + qx::serialization::portable_binary::from_file(coll, "test_all.bin2", 0); +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#ifndef _QX_NO_JSON + qx::serialization::json::to_file(coll, "test_all.json"); + qx::serialization::json::from_file(coll, "test_all.json"); +#endif // _QX_NO_JSON + + qx::clone(coll); + qx::create("CTestAll"); +} diff --git a/test/qxDllSample/dll2/src/CUser.cpp b/test/qxDllSample/dll2/src/CUser.cpp new file mode 100644 index 0000000..4cd0916 --- /dev/null +++ b/test/qxDllSample/dll2/src/CUser.cpp @@ -0,0 +1,126 @@ +#include "../include/precompiled.h" + +#include "../include/CUser.h" + +#include + +void CUser::test() +{ + std::shared_ptr ptrTmp; + ptrTmp.reset(new CUser()); + qx::clone(ptrTmp); + +#if _QX_SERIALIZE_POLYMORPHIC + qx::serialization::polymorphic_xml::to_file(ptrTmp, "pointer_from_CUser.xml"); + qx::serialization::polymorphic_xml::from_file(ptrTmp, "pointer_from_CUser.xml"); +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_XML + qx::serialization::xml::to_file(ptrTmp, "pointer_from_CUser.xml"); + qx::serialization::xml::from_file(ptrTmp, "pointer_from_CUser.xml"); +#endif // _QX_SERIALIZE_XML + +#ifndef _QX_NO_JSON + qx::serialization::json::to_file(ptrTmp, "pointer_from_CUser.json"); + qx::serialization::json::from_file(ptrTmp, "pointer_from_CUser.json"); +#endif // _QX_NO_JSON + + qx::test::CPerson person; + qx::clone(person); + qx::create("qx::test::CPerson"); + +#if _QX_SERIALIZE_POLYMORPHIC + qx::serialization::polymorphic_binary::to_file(person, "person.bin"); + qx::serialization::polymorphic_binary::from_file(person, "person.bin"); + qx::serialization::polymorphic_xml::to_file(person, "person.xml"); + qx::serialization::polymorphic_xml::from_file(person, "person.xml"); +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY + qx::serialization::binary::to_file(person, "person.bin"); + qx::serialization::binary::from_file(person, "person.bin"); +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT + qx::serialization::text::to_file(person, "person.txt"); + qx::serialization::text::from_file(person, "person.txt"); +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML + qx::serialization::xml::to_file(person, "person.xml"); + qx::serialization::xml::from_file(person, "person.xml"); +#endif // _QX_SERIALIZE_XML + +#ifndef _QX_NO_JSON + qx::serialization::json::to_file(person, "person.json"); + qx::serialization::json::from_file(person, "person.json"); +#endif // _QX_NO_JSON + +#if _QX_SERIALIZE_PORTABLE_BINARY + qx::serialization::portable_binary::to_file(person, "person.bin2", 0); + qx::serialization::portable_binary::from_file(person, "person.bin2", 0); +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + qx::serialization::to_string(person); + qx::serialization::to_file_compressed(person, "person.zip"); + qx::serialization::from_file_compressed(person, "person.zip"); +#else // _QX_ENABLE_BOOST_SERIALIZATION + qx::serialization::qt::to_string(person); + qx::serialization::qt::to_file_compressed(person, "person.zip"); + qx::serialization::qt::from_file_compressed(person, "person.zip"); +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + //-------------------------------- + + CUser user; + qx::clone(user); + qx::create("CUser"); + +#if _QX_SERIALIZE_POLYMORPHIC + qx::serialization::polymorphic_binary::to_file(user, "user.bin"); + qx::serialization::polymorphic_binary::from_file(user, "user.bin"); + qx::serialization::polymorphic_xml::to_file(user, "user.xml"); + qx::serialization::polymorphic_xml::from_file(user, "user.xml"); +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY + qx::serialization::binary::to_file(user, "user.bin"); + qx::serialization::binary::from_file(user, "user.bin"); +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT + qx::serialization::text::to_file(user, "user.txt"); + qx::serialization::text::from_file(user, "user.txt"); +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML + qx::serialization::xml::to_file(user, "user.xml"); + qx::serialization::xml::from_file(user, "user.xml"); +#endif // _QX_SERIALIZE_XML + +#ifndef _QX_NO_JSON + qx::serialization::json::to_file(user, "user.json"); + qx::serialization::json::from_file(user, "user.json"); +#endif // _QX_NO_JSON + +#if _QX_SERIALIZE_PORTABLE_BINARY + qx::serialization::portable_binary::to_file(user, "user.bin2", 0); + qx::serialization::portable_binary::from_file(user, "user.bin2", 0); +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + qx::serialization::to_string(user); + qx::serialization::to_file_compressed(user, "user.zip"); + qx::serialization::from_file_compressed(user, "user.zip"); +#else // _QX_ENABLE_BOOST_SERIALIZATION + qx::serialization::qt::to_string(user); + qx::serialization::qt::to_file_compressed(user, "user.zip"); + qx::serialization::qt::from_file_compressed(user, "user.zip"); +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + //-------------------------------- + + qx::test::CPerson * pPerson = new qx::test::CPerson(); // You find a memory leak !!! + Q_UNUSED(pPerson); +} diff --git a/test/qxDllSample/dll2/src/Foo.cpp b/test/qxDllSample/dll2/src/Foo.cpp new file mode 100644 index 0000000..9274d91 --- /dev/null +++ b/test/qxDllSample/dll2/src/Foo.cpp @@ -0,0 +1,5 @@ +#include "../include/precompiled.h" + +#include "../include/Foo.h" + +#include diff --git a/test/qxDllSample/dll2/src/main.cpp b/test/qxDllSample/dll2/src/main.cpp new file mode 100644 index 0000000..74ddba9 --- /dev/null +++ b/test/qxDllSample/dll2/src/main.cpp @@ -0,0 +1,20 @@ +#ifdef _MSC_VER + +#include + +extern "C" +int WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /* lpReservedt */) +{ + (void)hInstance; + switch (dwReason) + { + case DLL_PROCESS_ATTACH: ::OutputDebugStringA("dll2.DllMain() ---> DLL_PROCESS_ATTACH\n"); break; + case DLL_PROCESS_DETACH: ::OutputDebugStringA("dll2.DllMain() ---> DLL_PROCESS_DETACH\n"); break; + case DLL_THREAD_ATTACH: ::OutputDebugStringA("dll2.DllMain() ---> DLL_THREAD_ATTACH\n"); break; + case DLL_THREAD_DETACH: ::OutputDebugStringA("dll2.DllMain() ---> DLL_THREAD_DETACH\n"); break; + } + + return 1; +} + +#endif // _MSC_VER diff --git a/test/qxDllSample/exe/CMakeLists.txt b/test/qxDllSample/exe/CMakeLists.txt new file mode 100644 index 0000000..78d57b5 --- /dev/null +++ b/test/qxDllSample/exe/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.1) + +project(exe LANGUAGES CXX) + +include(../../../QxOrm.cmake) + +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Sql Gui Widgets REQUIRED) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_DEBUG_POSTFIX "d") + +set(HEADERS + ./include/precompiled.h + ./include/export.h + ) + +set(SRCS + ./src/main.cpp + ) + +add_executable(exe ${SRCS} ${HEADERS}) + +target_compile_definitions(exe PRIVATE -D_QX_BUILDING_EXE) + +if(COMMAND target_precompile_headers) + target_precompile_headers(exe PRIVATE ./include/precompiled.h) +endif() # (COMMAND target_precompile_headers) + +target_link_libraries(exe ${QX_LIBRARIES} Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets QxOrm dll1 dll2) + +set_target_properties(exe PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../_bin" + ) + +set_target_properties(exe PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) diff --git a/test/qxDllSample/exe/debug/.gitignore b/test/qxDllSample/exe/debug/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxDllSample/exe/debug/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxDllSample/exe/exe.pro b/test/qxDllSample/exe/exe.pro new file mode 100644 index 0000000..584d577 --- /dev/null +++ b/test/qxDllSample/exe/exe.pro @@ -0,0 +1,39 @@ +include(../../../QxOrm.pri) + +TEMPLATE = app +DEFINES += _QX_BUILDING_EXE +INCLUDEPATH += ../../../../QxOrm/include/ +DESTDIR = ../../../../QxOrm/test/_bin/ + +QT += gui +greaterThan(QT_MAJOR_VERSION, 4) { QT += widgets } + +!contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) { +PRECOMPILED_HEADER = ./include/precompiled.h +} # !contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) + +macx:CONFIG-=app_bundle + +CONFIG(debug, debug|release) { +TARGET = exed +} else { +TARGET = exe +} # CONFIG(debug, debug|release) + +LIBS += -L"../../../../QxOrm/lib" +LIBS += -L"../../../../QxOrm/test/_bin" + +CONFIG(debug, debug|release) { +LIBS += -l"QxOrmd" +LIBS += -l"dll1d" +LIBS += -l"dll2d" +} else { +LIBS += -l"QxOrm" +LIBS += -l"dll1" +LIBS += -l"dll2" +} # CONFIG(debug, debug|release) + +HEADERS += ./include/precompiled.h +HEADERS += ./include/export.h + +SOURCES += ./src/main.cpp diff --git a/test/qxDllSample/exe/include/export.h b/test/qxDllSample/exe/include/export.h new file mode 100644 index 0000000..4a0af00 --- /dev/null +++ b/test/qxDllSample/exe/include/export.h @@ -0,0 +1,23 @@ +#ifndef _QX_EXE_EXPORT_H_ +#define _QX_EXE_EXPORT_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include "../../dll1/include/export.h" +#include "../../dll2/include/export.h" + +#ifdef _QX_BUILDING_EXE +#define QX_REGISTER_HPP_QX_EXE QX_REGISTER_HPP_EXPORT_DLL +#define QX_REGISTER_CPP_QX_EXE QX_REGISTER_CPP_EXPORT_DLL +#define QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QX_EXE QX_REGISTER_COMPLEX_CLASS_NAME_HPP_EXPORT_DLL +#define QX_REGISTER_COMPLEX_CLASS_NAME_CPP_QX_EXE QX_REGISTER_COMPLEX_CLASS_NAME_CPP_EXPORT_DLL +#else // _QX_BUILDING_EXE +#define QX_REGISTER_HPP_QX_EXE QX_REGISTER_HPP_IMPORT_DLL +#define QX_REGISTER_CPP_QX_EXE QX_REGISTER_CPP_IMPORT_DLL +#define QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QX_EXE QX_REGISTER_COMPLEX_CLASS_NAME_HPP_EXPORT_DLL +#define QX_REGISTER_COMPLEX_CLASS_NAME_CPP_QX_EXE QX_REGISTER_COMPLEX_CLASS_NAME_CPP_EXPORT_DLL +#endif // _QX_BUILDING_EXE + +#endif // _QX_EXE_EXPORT_H_ diff --git a/test/qxDllSample/exe/include/precompiled.h b/test/qxDllSample/exe/include/precompiled.h new file mode 100644 index 0000000..a8c1fb4 --- /dev/null +++ b/test/qxDllSample/exe/include/precompiled.h @@ -0,0 +1,14 @@ +#ifndef _QX_EXE_PRECOMPILED_HEADER_H_ +#define _QX_EXE_PRECOMPILED_HEADER_H_ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +#include "../include/export.h" + +#endif // _QX_EXE_PRECOMPILED_HEADER_H_ diff --git a/test/qxDllSample/exe/qt/moc/.gitignore b/test/qxDllSample/exe/qt/moc/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxDllSample/exe/qt/moc/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxDllSample/exe/qt/rcc/src/.gitignore b/test/qxDllSample/exe/qt/rcc/src/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxDllSample/exe/qt/rcc/src/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxDllSample/exe/qt/ui/include/.gitignore b/test/qxDllSample/exe/qt/ui/include/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxDllSample/exe/qt/ui/include/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxDllSample/exe/qt/ui/src/.gitignore b/test/qxDllSample/exe/qt/ui/src/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxDllSample/exe/qt/ui/src/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxDllSample/exe/qx/.gitignore b/test/qxDllSample/exe/qx/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxDllSample/exe/qx/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxDllSample/exe/release/.gitignore b/test/qxDllSample/exe/release/.gitignore new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/test/qxDllSample/exe/release/.gitignore @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/test/qxDllSample/exe/src/main.cpp b/test/qxDllSample/exe/src/main.cpp new file mode 100644 index 0000000..47238e8 --- /dev/null +++ b/test/qxDllSample/exe/src/main.cpp @@ -0,0 +1,555 @@ +#include + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + +#include "../include/precompiled.h" + +#include "../../dll1/include/CPerson.h" +#include "../../dll1/include/TestQtProperty.h" + +#include "../../dll2/include/CUser.h" +#include "../../dll2/include/CTestAll.h" +#include "../../dll2/include/Bar.h" +#include "../../dll2/include/Foo.h" + +#include + +void test_fct() { qDebug("[QxOrm] %s", "'test_fct()' called by 'qx::IxFunction()'"); } +struct test_class_fct { int class_fct() { qDebug("[QxOrm] %s", "'test_class_fct::class_fct()' called by 'qx::IxFunction()'"); return 123; }; }; + +int main(int argc, char * argv[]) +{ + QApplication app(argc, argv); + + //-------------------------------- + + std::shared_ptr ptrTmp; + ptrTmp.reset(new CUser()); + qx::clone(ptrTmp); + +#if _QX_SERIALIZE_POLYMORPHIC + qx::serialization::polymorphic_xml::to_file(ptrTmp, "pointer_from_main.xml"); + qx::serialization::polymorphic_xml::from_file(ptrTmp, "pointer_from_main.xml"); +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_XML + qx::serialization::xml::to_file(ptrTmp, "pointer_from_main.xml"); + qx::serialization::xml::from_file(ptrTmp, "pointer_from_main.xml"); +#endif // _QX_SERIALIZE_XML + +#ifndef _QX_NO_JSON + qx::serialization::json::to_file(ptrTmp, "pointer_from_main.json"); + qx::serialization::json::from_file(ptrTmp, "pointer_from_main.json"); +#endif // _QX_NO_JSON + + //-------------------------------- + + qx::test::CPerson person; + std::shared_ptr personClone = qx::clone(person); + qx::any a = qx::create("qx::test::CPerson"); + +#if _QX_SERIALIZE_POLYMORPHIC + qx::serialization::polymorphic_binary::to_file(person, "person.bin"); + qx::serialization::polymorphic_binary::from_file(person, "person.bin"); + qx::serialization::polymorphic_xml::to_file(person, "person.xml"); + qx::serialization::polymorphic_xml::from_file(person, "person.xml"); +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY + qx::serialization::binary::to_file(person, "person.bin"); + qx::serialization::binary::from_file(person, "person.bin"); +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT + qx::serialization::text::to_file(person, "person.txt"); + qx::serialization::text::from_file(person, "person.txt"); +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML + qx::serialization::xml::to_file(person, "person.xml"); + qx::serialization::xml::from_file(person, "person.xml"); +#endif // _QX_SERIALIZE_XML + +#ifndef _QX_NO_JSON + qx::serialization::json::to_file(person, "person.json"); + qx::serialization::json::from_file(person, "person.json"); +#endif // _QX_NO_JSON + +#if _QX_SERIALIZE_PORTABLE_BINARY + qx::serialization::portable_binary::to_file(person, "person.bin2", 0); + qx::serialization::portable_binary::from_file(person, "person.bin2", 0); +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + qx::serialization::to_string(person); + qx::serialization::to_file_compressed(person, "person.zip"); + qx::serialization::from_file_compressed(person, "person.zip"); +#else // _QX_ENABLE_BOOST_SERIALIZATION + qx::serialization::qt::to_string(person); + qx::serialization::qt::to_file_compressed(person, "person.zip"); + qx::serialization::qt::from_file_compressed(person, "person.zip"); +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + //-------------------------------- + + CUser user; + std::shared_ptr userClone = qx::clone(user); + qx::create("CUserXXX"); + + qx::cache::max_cost(2); + qx::cache::set("user", userClone); + qx::cache::set("object", ptrTmp); + qx::cache::set("person", personClone); + qAssert(qx::cache::count() == 2); + ptrTmp = qx::cache::get< std::shared_ptr >("object"); + qx_bool bCacheOk = qx::cache::get("user", userClone); + qAssert(! bCacheOk && (ptrTmp.get() != NULL)); + + std::shared_ptr personValidate = qx::clone(person); + if (! personValidate) { personValidate.reset(new qx::test::CPerson()); } + personValidate->setLastName("admin"); + personValidate->setDouble(305.86); + qx::QxInvalidValueX invalidValues = qx::validate(personValidate); + QString sInvalidValues = invalidValues.text(); + qDebug("[QxOrm] test 'QxValidator' module :\n%s", qPrintable(sInvalidValues)); + +#if _QX_SERIALIZE_POLYMORPHIC + qx::serialization::polymorphic_binary::to_file(user, "user.bin"); + qx::serialization::polymorphic_binary::from_file(user, "user.bin"); + qx::serialization::polymorphic_xml::to_file(user, "user.xml"); + qx::serialization::polymorphic_xml::from_file(user, "user.xml"); +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_BINARY + qx::serialization::binary::to_file(user, "user.bin"); + qx::serialization::binary::from_file(user, "user.bin"); +#endif // _QX_SERIALIZE_BINARY + +#if _QX_SERIALIZE_TEXT + qx::serialization::text::to_file(user, "user.txt"); + qx::serialization::text::from_file(user, "user.txt"); +#endif // _QX_SERIALIZE_TEXT + +#if _QX_SERIALIZE_XML + qx::serialization::xml::to_file(user, "user.xml"); + qx::serialization::xml::from_file(user, "user.xml"); +#endif // _QX_SERIALIZE_XML + +#ifndef _QX_NO_JSON + qx::serialization::json::to_file(user, "user.json"); + qx::serialization::json::from_file(user, "user.json"); +#endif // _QX_NO_JSON + +#if _QX_SERIALIZE_PORTABLE_BINARY + qx::serialization::portable_binary::to_file(user, "user.bin2", 0); + qx::serialization::portable_binary::from_file(user, "user.bin2", 0); +#endif // _QX_SERIALIZE_PORTABLE_BINARY + +#ifdef _QX_ENABLE_BOOST_SERIALIZATION + qx::serialization::to_string(user); + qx::serialization::to_file_compressed(user, "user.zip"); + qx::serialization::from_file_compressed(user, "user.zip"); +#else // _QX_ENABLE_BOOST_SERIALIZATION + qx::serialization::qt::to_string(user); + qx::serialization::qt::to_file_compressed(user, "user.zip"); + qx::serialization::qt::from_file_compressed(user, "user.zip"); +#endif // _QX_ENABLE_BOOST_SERIALIZATION + + //-------------------------------- + + qx::any resultInvoke; + CUser * pUser = new CUser(); // You find a memory leak !!! + pUser->test(); + qx_bool bInvokeOk = qx::QxClass::invoke("fct_getPersonId", pUser); qAssert(bInvokeOk); + bInvokeOk = qx::QxClassX::invoke("CUser", "fct_getPersonId", pUser); qAssert(bInvokeOk); + bInvokeOk = qx::QxClassX::invokeStatic("CUser", "fct_testStaticFct", "182", (& resultInvoke)); qAssert(bInvokeOk.getValue() && (qx::any_cast(resultInvoke) == 182)); + + // Other way to invoke a function with parameters encapsulated in a list of qx::any : std::vector + resultInvoke = qx::any(); + std::vector lstParams; + QString invokeParam1 = "238"; + lstParams.push_back(invokeParam1); + bInvokeOk = qx::QxClassX::invokeStatic("CUser", "fct_testStaticFct", lstParams, (& resultInvoke)); + qAssert(bInvokeOk.getValue() && (qx::any_cast(resultInvoke) == 238)); + + //-------------------------------- + + typedef QSharedPointer CUser_ptr; + qx::QxCollection coll; + CUser_ptr p1(new CUser()); + CUser_ptr p2(new CUser()); + coll.insert(0, p1); + coll.insert(1, p2); + + { + // Test qx::IxCollection interface using a smart-pointer (test also qx::any_cast_dynamic::get(...) function) + QSharedPointer pTestIxCollection = coll._get< QSharedPointer >(1); + Q_UNUSED(pTestIxCollection); + } + +#if _QX_SERIALIZE_POLYMORPHIC + qx::serialization::polymorphic_xml::to_file(coll, "collection.xml"); +#endif // _QX_SERIALIZE_POLYMORPHIC + +#if _QX_SERIALIZE_XML + qx::serialization::xml::to_file(coll, "collection.xml"); +#endif // _QX_SERIALIZE_XML + +#ifndef _QX_NO_JSON + qx::serialization::json::to_file(coll, "collection.json"); +#endif // _QX_NO_JSON + + //-------------------------------- + + qx::any any_ret; + qx::IxFunction_ptr pFct1 = qx::function::bind_fct_0(& test_fct); + qx_bool bInvoke = pFct1->invoke(); + + qx::IxFunction_ptr pFct2 = qx::function::bind_member_fct_0(std::mem_fn(& test_class_fct::class_fct)); // using std::mem_fn() here is just a workaround for an issue with some versions of MSVC, it is not required with a full compliant C++11 compiler (http://stackoverflow.com/questions/23778883/vs2013-stdfunction-with-member-function) + test_class_fct o_test_class_fct; + bInvoke = pFct2->invoke(& o_test_class_fct, "", (& any_ret)); + int iReturnByInvoke = qx::any_cast(any_ret); + Q_UNUSED(iReturnByInvoke); + + //-------------------------------- + + CTestAll::test(); + + //-------------------------------- + + QString sTestCvt = qx::cvt::to_string(18, "%03d"); + sTestCvt = qx::cvt::to_string(int(4)); + sTestCvt = qx::cvt::to_string(QDateTime::currentDateTime()); + sTestCvt = qx::cvt::to_string(pUser); + sTestCvt = qx::cvt::to_string(p1, "%08d"); + QObject dummy_01; Q_UNUSED(dummy_01); // sTestCvt = qx::cvt::to_string(dummy_01); + CTestAll dummy_02; sTestCvt = qx::cvt::to_string(dummy_02); + + qx_bool bCvtOk = qx::cvt::from_string("34", pUser); + sTestCvt = qx::cvt::to_string(pUser); + + QVariant sTestCvtVar = qx::cvt::to_variant(QTime::currentTime()); + QVariant dummy_03; bCvtOk = qx::cvt::from_variant(dummy_03, p1); + + QVector< std::shared_ptr > lstCvtTest; + std::shared_ptr pp1; pp1.reset(new CUser()); lstCvtTest.push_back(pp1); + std::shared_ptr pp2; lstCvtTest.push_back(pp2); + std::shared_ptr pp3; pp3.reset(new CUser()); lstCvtTest.push_back(pp3); + sTestCvt = qx::cvt::to_string(lstCvtTest); + lstCvtTest.remove(1); + bCvtOk = qx::cvt::from_string(sTestCvt, lstCvtTest); + qAssert(lstCvtTest.size() == 3); + + //-------------------------------- + + QFile::remove("./database.db"); + + qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE"); + qx::QxSqlDatabase::getSingleton()->setDatabaseName("database.db"); + qx::QxSqlDatabase::getSingleton()->setHostName("localhost"); + qx::QxSqlDatabase::getSingleton()->setUserName("root"); + qx::QxSqlDatabase::getSingleton()->setPassword(""); + qx::QxSqlDatabase::getSingleton()->setSqlPlaceHolderStyle(qx::QxSqlDatabase::ph_style_2_point_name); + qx::QxSqlDatabase::getSingleton()->setTraceSqlQuery(true); + qx::QxSqlDatabase::getSingleton()->setTraceSqlRecord(false); + + // Only for debug purpose : assert if invalid offset detected fetching a relation + qx::QxSqlDatabase::getSingleton()->setVerifyOffsetRelation(true); + + qx::test::CPerson * pBrother = new qx::test::CPerson(); + pBrother->setFirstName("brother firstname"); + pBrother->setLastName("brother lastname"); + pUser->setBrother(pBrother); + pUser->setLastName("user lastname"); + + QSqlError daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + daoError = qx::dao::insert(pBrother); + long lBrotherId = pBrother->getPersonId(); + daoError = qx::dao::insert(pUser); + long lDaoCount = qx::dao::count(); qAssert(lDaoCount == 1); + pUser->setBrother(NULL); + daoError = qx::dao::fetch_by_id(pUser); qAssert(pUser->getBrother() && (pUser->getBrother()->getPersonId() == lBrotherId)); + pUser->setBrother(NULL); + daoError = qx::dao::fetch_by_id_with_relation("brother", pUser); qAssert(pUser->getBrother() && (pUser->getBrother()->getFirstName() == "brother firstname")); + qx_bool bDaoExist = qx::dao::exist(pUser); qAssert(bDaoExist); + daoError = qx::dao::update(pUser); + daoError = qx::dao::delete_by_id(pUser); + lDaoCount = qx::dao::count(); qAssert(lDaoCount == 0); + daoError = qx::dao::save(pUser); + daoError = qx::dao::save(pUser); + lDaoCount = qx::dao::count(); qAssert(lDaoCount == 1); + daoError = qx::dao::delete_all(); + bDaoExist = qx::dao::exist(pUser); qAssert(! bDaoExist); + pUser->setBrother(NULL); + + typedef qx::QxCollection< long, std::shared_ptr > type_lstUser; + std::shared_ptr ppp1; ppp1.reset(new CUser(53)); + ppp1->setProfil("profil 10"); + ppp1->setLastName("ppp1 lastname"); + std::shared_ptr ppp2; ppp2.reset(new CUser(108)); + ppp2->setDateModif(QDateTime::currentDateTime()); + ppp2->setLastName("ppp1 lastname"); + type_lstUser lstUser; + lstUser.insert(ppp1->getUserId(), ppp1); + lstUser.insert(ppp2->getUserId(), ppp2); + + QString sQuery1 = "WHERE profil LIKE '%pro%'"; + QString sQuery2 = "LIMIT 1"; + qx::QxSqlQuery qxQuery1("WHERE profil LIKE :profil"); + qxQuery1.bind(":profil", "%pro%"); + + daoError = qx::dao::insert(lstUser); + lDaoCount = qx::dao::count(); qAssert(lDaoCount == 2); + lDaoCount = qx::dao::count(sQuery1); qAssert(lDaoCount == 1); + lDaoCount = qx::dao::count(qxQuery1); qAssert(lDaoCount == 1); + daoError = qx::dao::fetch_by_id(lstUser); + bDaoExist = qx::dao::exist(lstUser); qAssert(bDaoExist); + daoError = qx::dao::update(lstUser); + daoError = qx::dao::delete_by_id(lstUser); + lDaoCount = qx::dao::count(); qAssert(lDaoCount == 0); + daoError = qx::dao::save(lstUser); + + pBrother = new qx::test::CPerson(); + pBrother->setFirstName("brother firstname from collection"); + pBrother->setLastName("brother lastname from collection"); + lstUser.getByIndex(0)->setBrother(pBrother); + + daoError = qx::dao::insert(pBrother); + lBrotherId = pBrother->getPersonId(); + daoError = qx::dao::save(lstUser); + daoError = qx::dao::fetch_by_query(sQuery2, lstUser); qAssert(lstUser.size() == 1); + daoError = qx::dao::fetch_all_with_all_relation(lstUser); qAssert((lstUser.size() == 2) && lstUser.getByIndex(0)->getBrother() && (lstUser.getByIndex(0)->getBrother()->getLastName() == "brother lastname from collection")); + lDaoCount = qx::dao::count(); qAssert(lDaoCount == 2); + daoError = qx::dao::fetch_all(lstUser); qAssert(lstUser.size() == 2); + daoError = qx::dao::delete_by_query(sQuery1); + lDaoCount = qx::dao::count(); qAssert(lDaoCount == 1); + daoError = qx::dao::delete_all(); + bDaoExist = qx::dao::exist(lstUser); qAssert(! bDaoExist); + daoError = qx::dao::fetch_all(lstUser); qAssert(lstUser.size() == 0); + + //-------------------------------- + + daoError = qx::dao::create_table(); + daoError = qx::dao::create_table(); + + Foo_ptr pFoo; + pFoo.reset(new Foo()); pFoo->setName("name1"); pFoo->setDesc("desc1"); daoError = qx::dao::save(pFoo); qAssert(pFoo->getId() == 1); + pFoo.reset(new Foo()); pFoo->setName("name2"); pFoo->setDesc("desc2"); daoError = qx::dao::save(pFoo); qAssert(pFoo->getId() == 2); + pFoo.reset(new Foo()); pFoo->setName("name3"); pFoo->setDesc("desc3"); daoError = qx::dao::save(pFoo); qAssert(pFoo->getId() == 3); + pFoo.reset(new Foo()); pFoo->setName("name4"); pFoo->setDesc("desc4"); daoError = qx::dao::save(pFoo); qAssert(pFoo->getId() == 4); + pFoo.reset(new Foo()); pFoo->setName("name5"); pFoo->setDesc("desc5"); daoError = qx::dao::save(pFoo); qAssert(pFoo->getId() == 5); + pFoo.reset(new Foo()); pFoo->setName("name6"); pFoo->setDesc("desc6"); daoError = qx::dao::save(pFoo); qAssert(pFoo->getId() == 6); + pFoo.reset(new Foo()); pFoo->setName("name7"); pFoo->setDesc("desc7"); daoError = qx::dao::save(pFoo); qAssert(pFoo->getId() == 7); + + // Test qx::IxPersistable interface + daoError = pFoo->qxFetchById(); qAssert(! daoError.isValid()); + daoError = pFoo->qxUpdate(); qAssert(! daoError.isValid()); + lDaoCount = pFoo->qxCount(); qAssert(lDaoCount == 7); + invalidValues = pFoo->qxValidate(); qAssert(invalidValues.count() == 0); + +#ifndef _QX_NO_JSON + QString fooAsJson = pFoo->toJson(); qDebug("[QxOrm] qx::IxPersistable::toJson() test 1 :\n%s", qPrintable(fooAsJson)); + qx::IxPersistable * pFooPersistable = static_cast(pFoo.get()); + fooAsJson = qx::serialization::json::to_string(* pFooPersistable); + qDebug("[QxOrm] qx::IxPersistable::toJson() test 2 :\n%s", qPrintable(fooAsJson)); + + qx::IxPersistableCollection_ptr pFooPersistableCollection = qx::IxPersistable::qxFetchAll("Foo"); + qAssert(pFooPersistableCollection && (pFooPersistableCollection->__count() != 0)); + fooAsJson = pFooPersistableCollection->toJson(); + qDebug("[QxOrm] qx::IxPersistableCollection::toJson() test 3 :\n%s", qPrintable(fooAsJson)); + daoError = pFooPersistableCollection->qxSave(); qAssert(! daoError.isValid()); + + QStringList emptyColumnsRelations; + pFooPersistableCollection = qx::IxPersistable::qxFetchAll("Foo", emptyColumnsRelations, emptyColumnsRelations, NULL, true); + qAssert(pFooPersistableCollection && (pFooPersistableCollection->__count() != 0)); + fooAsJson = pFooPersistableCollection->toJson(); + qDebug("[QxOrm] qx::IxPersistableCollection::toJson() test 4 :\n%s", qPrintable(fooAsJson)); + daoError = pFooPersistableCollection->qxSave(); qAssert(! daoError.isValid()); +#endif // _QX_NO_JSON + + Bar_ptr pBar; + pBar.reset(new Bar()); pBar->setCode("code1"); pBar->setValue("value1"); pBar->setFoo(3); daoError = qx::dao::save(pBar); qAssert(pBar->getId() == 1); + pBar.reset(new Bar()); pBar->setCode("code2"); pBar->setValue("value2"); pBar->setFoo(3); daoError = qx::dao::save(pBar); qAssert(pBar->getId() == 2); + pBar.reset(new Bar()); pBar->setCode("code3"); pBar->setValue("value3"); pBar->setFoo(2); daoError = qx::dao::save(pBar); qAssert(pBar->getId() == 3); + pBar.reset(new Bar()); pBar->setCode("code4"); pBar->setValue("value4"); pBar->setFoo(3); daoError = qx::dao::save(pBar); qAssert(pBar->getId() == 4); + pBar.reset(new Bar()); pBar->setCode("code5"); pBar->setValue("value5"); pBar->setFoo(2); daoError = qx::dao::save(pBar); qAssert(pBar->getId() == 5); + pBar.reset(new Bar()); pBar->setCode("code6"); pBar->setValue("value6"); pBar->setFoo(6); daoError = qx::dao::save(pBar); qAssert(pBar->getId() == 6); + pBar.reset(new Bar()); pBar->setCode("code7"); pBar->setValue("value7"); pBar->setFoo(3); daoError = qx::dao::save(pBar); qAssert(pBar->getId() == 7); + + // Test inserting a complex QVariant in database + pFoo.reset(new Foo()); pFoo->setId(3); daoError = pFoo->qxFetchById(); qAssert(! daoError.isValid()); + QMap testComplexVariant; testComplexVariant.insert("key_1", QVariant(QString("val_1"))); + QList testComplexVariantItem; testComplexVariantItem.append(QVariant(QString("val_2_inside_QVariantList"))); + testComplexVariantItem.append(QVariant(QString("val_3_inside_QVariantList"))); + testComplexVariant.insert("key_2", QVariant(testComplexVariantItem)); + pFoo->setDesc(testComplexVariant); + daoError = pFoo->qxUpdate(); qAssert(! daoError.isValid()); + + pFoo.reset(new Foo()); pFoo->setId(3); + daoError = qx::dao::fetch_by_id_with_relation("lstBar", pFoo); qAssert(pFoo->getBarX() && (pFoo->getBarX()->size() == 4)); qx::dump((* pFoo), true); + pFoo.reset(new Foo()); pFoo->setId(4); + daoError = qx::dao::fetch_by_id_with_relation("lstBar", pFoo); qAssert(! pFoo->getBarX() || (pFoo->getBarX()->size() == 0)); + pFoo.reset(new Foo()); pFoo->setId(2); + daoError = qx::dao::fetch_by_id_with_relation("lstBar", pFoo); qAssert(pFoo->getBarX() && (pFoo->getBarX()->size() == 2)); + + FooX_ptr pFooX; pFooX.reset(new FooX()); + daoError = qx::dao::fetch_all_with_all_relation(pFooX); qAssert(pFooX->size() == 7); + pFoo = pFooX->getByKey(3); qAssert(pFoo->getBarX() && (pFoo->getBarX()->size() == 4)); + pFoo = pFooX->getByKey(4); qAssert(! pFoo->getBarX() || (pFoo->getBarX()->size() == 0)); + pFoo = pFooX->getByKey(2); qAssert(pFoo->getBarX() && (pFoo->getBarX()->size() == 2)); + + { + // Test eager fetching an instance (with relationships) which is not a pointer or smart-pointer + QList pFooX1; daoError = qx::dao::fetch_all_with_all_relation(pFooX1); qAssert(pFooX1.size() == 7); + QHash pFooX2; daoError = qx::dao::fetch_all_with_all_relation(pFooX2); qAssert(pFooX2.size() == 7); + qx::QxCollection pFooX3; daoError = qx::dao::fetch_all_with_all_relation(pFooX3); qAssert(pFooX3.size() == 7); + +#ifdef _QX_ENABLE_BOOST +#if (BOOST_VERSION >= 105000) + boost::unordered_map pFooX4; daoError = qx::dao::fetch_all_with_all_relation(pFooX4); qAssert(pFooX4.size() == 7); +#endif // (BOOST_VERSION >= 105000) +#endif // _QX_ENABLE_BOOST + + std::vector pFooX5; daoError = qx::dao::fetch_all_with_all_relation(pFooX5); qAssert(pFooX5.size() == 7); + QSet pFooX6; daoError = qx::dao::fetch_all_with_all_relation(pFooX6); qAssert(pFooX6.size() == 7); Q_FOREACH(Foo * pFooTemp, pFooX6) { delete pFooTemp; } + std::set pFooX7; daoError = qx::dao::fetch_all_with_all_relation(pFooX7); qAssert(pFooX7.size() == 7); + +#ifdef _QX_ENABLE_BOOST + boost::unordered_set pFooX8; daoError = qx::dao::fetch_all_with_all_relation(pFooX8); qAssert(pFooX8.size() == 7); +#endif // _QX_ENABLE_BOOST + } + + pBar.reset(new Bar()); pBar->setId(7); + daoError = qx::dao::fetch_by_id_with_relation("foo_id", pBar); qAssert(pBar->getFoo() && (pBar->getFoo()->getName() == "name3")); + pFoo = pBar->getFoo(); if (pFoo) { pFoo->setDate(QDate::currentDate()); pFoo->setTime(QTime::currentTime()); pFoo->setDateTime(QDateTime::currentDateTime()); } + qx::dump(pBar, false); + qx::dump(pBar, true); + + // Test soft delete behavior + pBar.reset(new Bar()); + pBar->setId(5); + daoError = qx::dao::delete_by_id(pBar); qAssert(! daoError.isValid()); // This Bar item is now soft-deleted in database + bDaoExist = qx::dao::exist(pBar); qAssert(! bDaoExist); + daoError = qx::dao::delete_all(); qAssert(! daoError.isValid()); // All Bar items are now soft-deleted in database + long lBarCount = qx::dao::count(); qAssert(lBarCount == 0); Q_UNUSED(lBarCount); + + { + // Fetch soft-deleted items from database using session and ignoreSoftDelete() behavior + qx::QxSession session; + session.ignoreSoftDelete(); // The second parameter allows to select classes to ignore soft delete behavior (by default, if empty, then all classes are ignored) + BarX lstOfSoftDeletedBars; + daoError = session.fetchAll(lstOfSoftDeletedBars); + qAssert((! daoError.isValid()) && (lstOfSoftDeletedBars.size() > 0)); + lBarCount = session.count(); qAssert(lBarCount > 0); Q_UNUSED(lBarCount); + + // Try to fetch soft-deleted items from database through relationships + pFooX.reset(new FooX()); + daoError = session.fetchAll(pFooX, QStringList(), QStringList() << "lstBar"); + qAssert((! daoError.isValid()) && (pFooX->size() == 7)); + pFoo = pFooX->getByKey(3); qAssert(pFoo->getBarX() && (pFoo->getBarX()->size() == 4)); + pFoo = pFooX->getByKey(4); qAssert(! pFoo->getBarX() || (pFoo->getBarX()->size() == 0)); + pFoo = pFooX->getByKey(2); qAssert(pFoo->getBarX() && (pFoo->getBarX()->size() == 2)); + + // Remove physically items in database even if a soft delete behavior has been defined (using destroy functions) + daoError = session.destroyAll(); qAssert(! daoError.isValid()); + daoError = session.fetchAll(lstOfSoftDeletedBars); + qAssert((! daoError.isValid()) && (lstOfSoftDeletedBars.size() == 0)); + lBarCount = session.count(); qAssert(lBarCount == 0); Q_UNUSED(lBarCount); + } + + { + pFoo.reset(new Foo()); pFoo->setName("name10"); pFoo->setDesc("desc10"); + pFoo->setDate(QDate::currentDate()); pFoo->setTime(QTime::currentTime()); pFoo->setDateTime(QDateTime::currentDateTime()); +#ifdef _QX_ENABLE_BOOST + boost::optional optionalString("test boost optional"); pFoo->setOptString(optionalString); +#endif // _QX_ENABLE_BOOST + + // Test session to manage automatically database transactions (using C++ RAII) + qx::QxSession session; + session += qx::dao::insert(pFoo, session.database()); + session += qx::dao::update(pFoo, session.database()); + session += qx::dao::fetch_by_id(pFoo, session.database()); + session += qx::dao::delete_by_id(pFoo, session.database()); + qAssert(session.isValid()); + } + + //-------------------------------- + + // Dump all registered classes into QxOrm context (introspection engine) + qx::QxClassX::dumpAllClasses(); + + pBar.reset(new Bar()); + qx::IxDataMember * pDataMember = qx::QxClassX::getDataMember("Bar", "code"); qAssert(pDataMember != NULL); + bool bIntrospectionOk = pDataMember->setValue(pBar.get(), QString("test introspection engine")); qAssert(bIntrospectionOk); + QString sIntrospectionTest = pDataMember->getValue(pBar.get(), (& bIntrospectionOk)); + qAssert(bIntrospectionOk && (sIntrospectionTest == "test introspection engine")); + qAssert(pBar->getCode() == "test introspection engine"); + + //-------------------------------- + + daoError = qx::dao::create_table(); + TestQtProperty testQtMetaProperty; + QString sQtPropertyDesc = "test Qt introspection engine : meta-property using Q_PROPERTY() macro"; + testQtMetaProperty.setDesc(sQtPropertyDesc); + testQtMetaProperty.setBirthDate(QDateTime::currentDateTime()); + daoError = qx::dao::insert(testQtMetaProperty); qAssert(! daoError.isValid()); + daoError = qx::dao::fetch_by_id(testQtMetaProperty); qAssert(! daoError.isValid()); + qx::IxDataMember * pQtMetaProperty = qx::QxClassX::getDataMember("TestQtProperty", "desc"); Q_UNUSED(pQtMetaProperty); + qAssert(pQtMetaProperty && pQtMetaProperty->getValue(& testQtMetaProperty) == sQtPropertyDesc); + qx::dump(testQtMetaProperty, false); + qx::dump(testQtMetaProperty, true); + + //-------------------------------- + + QDateTime queryDT1, queryDT2; + qx_query queryToTest; + queryToTest.where("sex").isEqualTo("female") + .and_("age").isGreaterThan(38) + .or_("last_name").isNotEqualTo("Dupont") + .or_("first_name").like("Alfred") + .and_OpenParenthesis("id").isLessThanOrEqualTo(999) + .and_("birth_date").isBetween(queryDT1, queryDT2) + .closeParenthesis() + .or_("id").in(50, 999, 11, 23, 78945) + .and_("is_deleted").isNotNull() + .orderAsc("last_name", "first_name", "sex") + .limit(50, 150); + + QString sQueryToTest = queryToTest.query(); + qDebug("[QxOrm] test SQL query using C++ syntax : '%s'", qPrintable(sQueryToTest)); + + //-------------------------------- + + qx::QxRepository repositoryFoo; + pFooX.reset(new FooX()); + daoError = repositoryFoo.fetchAll(pFooX); + qAssert(! daoError.isValid()); + Foo_ptr pFooTmp; + pFooTmp.reset(repositoryFoo.fetchById(3)); + if (pFooTmp) + { + pFooTmp->setDesc("desc_modified"); + daoError = repositoryFoo.save(pFooTmp); + qAssert(! daoError.isValid()); + } + + //-------------------------------- + + // Just to check compilation of model/view template class + qx::IxModel * pModel = new qx::QxModel(); + Q_UNUSED(pModel); + + //-------------------------------- + + qAssert(qx::QxClassX::implementIxPersistable("qx::QxPersistable")); + qAssert(qx::QxClassX::implementIxPersistable("Bar")); + + qx::cache::clear(); + + return 0; +} diff --git a/tools/_build_debug.bat b/tools/_build_debug.bat new file mode 100644 index 0000000..0b70b45 --- /dev/null +++ b/tools/_build_debug.bat @@ -0,0 +1,2 @@ +qmake %QXORM_QMAKE_PARAMS% +nmake debug diff --git a/tools/_build_release.bat b/tools/_build_release.bat new file mode 100644 index 0000000..eb095a3 --- /dev/null +++ b/tools/_build_release.bat @@ -0,0 +1,2 @@ +qmake %QXORM_QMAKE_PARAMS% +nmake release diff --git a/tools/_clean_debug.bat b/tools/_clean_debug.bat new file mode 100644 index 0000000..8344b5d --- /dev/null +++ b/tools/_clean_debug.bat @@ -0,0 +1,6 @@ +cd "./debug/" +del /F /S /Q "./*.*" +cd "../lib/" +del /F /S /Q "./QxOrmd.*" +cd "../" +del /F /S /Q "./vc90.pdb" diff --git a/tools/_clean_release.bat b/tools/_clean_release.bat new file mode 100644 index 0000000..ed4adb4 --- /dev/null +++ b/tools/_clean_release.bat @@ -0,0 +1,5 @@ +cd "./release/" +del /F /S /Q "./*.*" +cd "../lib/" +del /F /S /Q "./QxOrm.*" +cd "../" diff --git a/tools/clean_all.bat b/tools/clean_all.bat new file mode 100644 index 0000000..da287c2 --- /dev/null +++ b/tools/clean_all.bat @@ -0,0 +1,571 @@ +cd "../" +call "./tools/_clean_debug.bat" +call "./tools/_clean_release.bat" +rmdir /S /Q "./obj/" +del /F /S /Q /A "*.suo" +del /F /S /Q "./QxOrm.sdf" +del /F /S /Q "./QxOrm.ncb" +del /F /S /Q "./QxOrm.vcproj.A*" +del /F /S /Q "./QxOrm.vcproj.B*" +del /F /S /Q "./QxOrm.vcproj.C*" +del /F /S /Q "./QxOrm.vcproj.D*" +del /F /S /Q "./QxOrm.vcproj.E*" +del /F /S /Q "./QxOrm.vcproj.F*" +del /F /S /Q "./QxOrm.vcproj.G*" +del /F /S /Q "./QxOrm.vcproj.H*" +del /F /S /Q "./QxOrm.vcproj.I*" +del /F /S /Q "./QxOrm.vcproj.J*" +del /F /S /Q "./QxOrm.vcproj.K*" +del /F /S /Q "./QxOrm.vcproj.L*" +del /F /S /Q "./QxOrm.vcproj.M*" +del /F /S /Q "./QxOrm.vcproj.N*" +del /F /S /Q "./QxOrm.vcproj.O*" +del /F /S /Q "./QxOrm.vcproj.P*" +del /F /S /Q "./QxOrm.vcproj.Q*" +del /F /S /Q "./QxOrm.vcproj.R*" +del /F /S /Q "./QxOrm.vcproj.S*" +del /F /S /Q "./QxOrm.vcproj.T*" +del /F /S /Q "./QxOrm.vcproj.U*" +del /F /S /Q "./QxOrm.vcproj.V*" +del /F /S /Q "./QxOrm.vcproj.W*" +del /F /S /Q "./QxOrm.vcproj.X*" +del /F /S /Q "./QxOrm.vcproj.Y*" +del /F /S /Q "./QxOrm.vcproj.Z*" +del /F /S /Q "./Makefile*" +del /F /S /Q "./vc90.pdb" +del /F /S /Q "./vc110.pdb" +cd "./qt/moc/" +del /F /S /Q "./*.cpp" +cd "../../" +cd "./test/qxDllSample/" +rmdir /S /Q "./obj/" +del /F /S /Q "./test.sdf" +del /F /S /Q "./test.ncb" +cd "../../test/qxBlog/" +rmdir /S /Q "./obj/" +del /F /S /Q "./qxBlog.sdf" +del /F /S /Q "./qxBlog.ncb" +del /F /S /Q "./qxBlog.vcproj.A*" +del /F /S /Q "./qxBlog.vcproj.B*" +del /F /S /Q "./qxBlog.vcproj.C*" +del /F /S /Q "./qxBlog.vcproj.D*" +del /F /S /Q "./qxBlog.vcproj.E*" +del /F /S /Q "./qxBlog.vcproj.F*" +del /F /S /Q "./qxBlog.vcproj.G*" +del /F /S /Q "./qxBlog.vcproj.H*" +del /F /S /Q "./qxBlog.vcproj.I*" +del /F /S /Q "./qxBlog.vcproj.J*" +del /F /S /Q "./qxBlog.vcproj.K*" +del /F /S /Q "./qxBlog.vcproj.L*" +del /F /S /Q "./qxBlog.vcproj.M*" +del /F /S /Q "./qxBlog.vcproj.N*" +del /F /S /Q "./qxBlog.vcproj.O*" +del /F /S /Q "./qxBlog.vcproj.P*" +del /F /S /Q "./qxBlog.vcproj.Q*" +del /F /S /Q "./qxBlog.vcproj.R*" +del /F /S /Q "./qxBlog.vcproj.S*" +del /F /S /Q "./qxBlog.vcproj.T*" +del /F /S /Q "./qxBlog.vcproj.U*" +del /F /S /Q "./qxBlog.vcproj.V*" +del /F /S /Q "./qxBlog.vcproj.W*" +del /F /S /Q "./qxBlog.vcproj.X*" +del /F /S /Q "./qxBlog.vcproj.Y*" +del /F /S /Q "./qxBlog.vcproj.Z*" +del /F /S /Q "./Makefile*" +del /F /S /Q "./vc90.pdb" +del /F /S /Q "./vc110.pdb" +del /F /S /Q "./*.sqlite" +cd "./qt/moc/" +del /F /S /Q "./*.cpp" +cd "../../" +cd "../../test/qxDllSample/dll1/" +rmdir /S /Q "./obj/" +del /F /S /Q "./dll1.sdf" +del /F /S /Q "./dll1.ncb" +del /F /S /Q "./dll1.vcproj.A*" +del /F /S /Q "./dll1.vcproj.B*" +del /F /S /Q "./dll1.vcproj.C*" +del /F /S /Q "./dll1.vcproj.D*" +del /F /S /Q "./dll1.vcproj.E*" +del /F /S /Q "./dll1.vcproj.F*" +del /F /S /Q "./dll1.vcproj.G*" +del /F /S /Q "./dll1.vcproj.H*" +del /F /S /Q "./dll1.vcproj.I*" +del /F /S /Q "./dll1.vcproj.J*" +del /F /S /Q "./dll1.vcproj.K*" +del /F /S /Q "./dll1.vcproj.L*" +del /F /S /Q "./dll1.vcproj.M*" +del /F /S /Q "./dll1.vcproj.N*" +del /F /S /Q "./dll1.vcproj.O*" +del /F /S /Q "./dll1.vcproj.P*" +del /F /S /Q "./dll1.vcproj.Q*" +del /F /S /Q "./dll1.vcproj.R*" +del /F /S /Q "./dll1.vcproj.S*" +del /F /S /Q "./dll1.vcproj.T*" +del /F /S /Q "./dll1.vcproj.U*" +del /F /S /Q "./dll1.vcproj.V*" +del /F /S /Q "./dll1.vcproj.W*" +del /F /S /Q "./dll1.vcproj.X*" +del /F /S /Q "./dll1.vcproj.Y*" +del /F /S /Q "./dll1.vcproj.Z*" +del /F /S /Q "./Makefile*" +del /F /S /Q "./vc90.pdb" +del /F /S /Q "./vc110.pdb" +cd "./qt/moc/" +del /F /S /Q "./*.cpp" +cd "../../../" +cd "../../test/qxDllSample/dll2/" +rmdir /S /Q "./obj/" +del /F /S /Q "./dll2.sdf" +del /F /S /Q "./dll2.ncb" +del /F /S /Q "./dll2.vcproj.A*" +del /F /S /Q "./dll2.vcproj.B*" +del /F /S /Q "./dll2.vcproj.C*" +del /F /S /Q "./dll2.vcproj.D*" +del /F /S /Q "./dll2.vcproj.E*" +del /F /S /Q "./dll2.vcproj.F*" +del /F /S /Q "./dll2.vcproj.G*" +del /F /S /Q "./dll2.vcproj.H*" +del /F /S /Q "./dll2.vcproj.I*" +del /F /S /Q "./dll2.vcproj.J*" +del /F /S /Q "./dll2.vcproj.K*" +del /F /S /Q "./dll2.vcproj.L*" +del /F /S /Q "./dll2.vcproj.M*" +del /F /S /Q "./dll2.vcproj.N*" +del /F /S /Q "./dll2.vcproj.O*" +del /F /S /Q "./dll2.vcproj.P*" +del /F /S /Q "./dll2.vcproj.Q*" +del /F /S /Q "./dll2.vcproj.R*" +del /F /S /Q "./dll2.vcproj.S*" +del /F /S /Q "./dll2.vcproj.T*" +del /F /S /Q "./dll2.vcproj.U*" +del /F /S /Q "./dll2.vcproj.V*" +del /F /S /Q "./dll2.vcproj.W*" +del /F /S /Q "./dll2.vcproj.X*" +del /F /S /Q "./dll2.vcproj.Y*" +del /F /S /Q "./dll2.vcproj.Z*" +del /F /S /Q "./Makefile*" +del /F /S /Q "./vc90.pdb" +del /F /S /Q "./vc110.pdb" +cd "./qt/moc/" +del /F /S /Q "./*.cpp" +cd "../../../" +cd "../../test/qxDllSample/exe/" +rmdir /S /Q "./obj/" +del /F /S /Q "./exe.sdf" +del /F /S /Q "./exe.ncb" +del /F /S /Q "./exe.vcproj.A*" +del /F /S /Q "./exe.vcproj.B*" +del /F /S /Q "./exe.vcproj.C*" +del /F /S /Q "./exe.vcproj.D*" +del /F /S /Q "./exe.vcproj.E*" +del /F /S /Q "./exe.vcproj.F*" +del /F /S /Q "./exe.vcproj.G*" +del /F /S /Q "./exe.vcproj.H*" +del /F /S /Q "./exe.vcproj.I*" +del /F /S /Q "./exe.vcproj.J*" +del /F /S /Q "./exe.vcproj.K*" +del /F /S /Q "./exe.vcproj.L*" +del /F /S /Q "./exe.vcproj.M*" +del /F /S /Q "./exe.vcproj.N*" +del /F /S /Q "./exe.vcproj.O*" +del /F /S /Q "./exe.vcproj.P*" +del /F /S /Q "./exe.vcproj.Q*" +del /F /S /Q "./exe.vcproj.R*" +del /F /S /Q "./exe.vcproj.S*" +del /F /S /Q "./exe.vcproj.T*" +del /F /S /Q "./exe.vcproj.U*" +del /F /S /Q "./exe.vcproj.V*" +del /F /S /Q "./exe.vcproj.W*" +del /F /S /Q "./exe.vcproj.X*" +del /F /S /Q "./exe.vcproj.Y*" +del /F /S /Q "./exe.vcproj.Z*" +del /F /S /Q "./Makefile*" +del /F /S /Q "./vc90.pdb" +del /F /S /Q "./vc110.pdb" +del /F /S /Q "./*.db" +del /F /S /Q "./*.xml" +del /F /S /Q "./*.bin" +del /F /S /Q "./*.zip" +cd "./qt/moc/" +del /F /S /Q "./*.cpp" +cd "../../../" +cd "../../test/qxBlogCompositeKey/" +rmdir /S /Q "./obj/" +del /F /S /Q "./qxBlog.sdf" +del /F /S /Q "./qxBlog.ncb" +del /F /S /Q "./qxBlog.vcproj.A*" +del /F /S /Q "./qxBlog.vcproj.B*" +del /F /S /Q "./qxBlog.vcproj.C*" +del /F /S /Q "./qxBlog.vcproj.D*" +del /F /S /Q "./qxBlog.vcproj.E*" +del /F /S /Q "./qxBlog.vcproj.F*" +del /F /S /Q "./qxBlog.vcproj.G*" +del /F /S /Q "./qxBlog.vcproj.H*" +del /F /S /Q "./qxBlog.vcproj.I*" +del /F /S /Q "./qxBlog.vcproj.J*" +del /F /S /Q "./qxBlog.vcproj.K*" +del /F /S /Q "./qxBlog.vcproj.L*" +del /F /S /Q "./qxBlog.vcproj.M*" +del /F /S /Q "./qxBlog.vcproj.N*" +del /F /S /Q "./qxBlog.vcproj.O*" +del /F /S /Q "./qxBlog.vcproj.P*" +del /F /S /Q "./qxBlog.vcproj.Q*" +del /F /S /Q "./qxBlog.vcproj.R*" +del /F /S /Q "./qxBlog.vcproj.S*" +del /F /S /Q "./qxBlog.vcproj.T*" +del /F /S /Q "./qxBlog.vcproj.U*" +del /F /S /Q "./qxBlog.vcproj.V*" +del /F /S /Q "./qxBlog.vcproj.W*" +del /F /S /Q "./qxBlog.vcproj.X*" +del /F /S /Q "./qxBlog.vcproj.Y*" +del /F /S /Q "./qxBlog.vcproj.Z*" +del /F /S /Q "./Makefile*" +del /F /S /Q "./vc90.pdb" +del /F /S /Q "./vc110.pdb" +del /F /S /Q "./*.sqlite" +cd "./qt/moc/" +del /F /S /Q "./*.cpp" +cd "../../" +cd "../../test/qxBlogModelView/" +rmdir /S /Q "./obj/" +del /F /S /Q "./qxBlog.sdf" +del /F /S /Q "./qxBlog.ncb" +del /F /S /Q "./qxBlog.vcproj.A*" +del /F /S /Q "./qxBlog.vcproj.B*" +del /F /S /Q "./qxBlog.vcproj.C*" +del /F /S /Q "./qxBlog.vcproj.D*" +del /F /S /Q "./qxBlog.vcproj.E*" +del /F /S /Q "./qxBlog.vcproj.F*" +del /F /S /Q "./qxBlog.vcproj.G*" +del /F /S /Q "./qxBlog.vcproj.H*" +del /F /S /Q "./qxBlog.vcproj.I*" +del /F /S /Q "./qxBlog.vcproj.J*" +del /F /S /Q "./qxBlog.vcproj.K*" +del /F /S /Q "./qxBlog.vcproj.L*" +del /F /S /Q "./qxBlog.vcproj.M*" +del /F /S /Q "./qxBlog.vcproj.N*" +del /F /S /Q "./qxBlog.vcproj.O*" +del /F /S /Q "./qxBlog.vcproj.P*" +del /F /S /Q "./qxBlog.vcproj.Q*" +del /F /S /Q "./qxBlog.vcproj.R*" +del /F /S /Q "./qxBlog.vcproj.S*" +del /F /S /Q "./qxBlog.vcproj.T*" +del /F /S /Q "./qxBlog.vcproj.U*" +del /F /S /Q "./qxBlog.vcproj.V*" +del /F /S /Q "./qxBlog.vcproj.W*" +del /F /S /Q "./qxBlog.vcproj.X*" +del /F /S /Q "./qxBlog.vcproj.Y*" +del /F /S /Q "./qxBlog.vcproj.Z*" +del /F /S /Q "./Makefile*" +del /F /S /Q "./vc90.pdb" +del /F /S /Q "./vc110.pdb" +del /F /S /Q "./*.sqlite" +cd "./qt/moc/" +del /F /S /Q "./*.cpp" +cd "../../" +cd "../../test/qxBlogMongoDB/" +rmdir /S /Q "./obj/" +del /F /S /Q "./qxBlog.sdf" +del /F /S /Q "./qxBlog.ncb" +del /F /S /Q "./qxBlog.vcproj.A*" +del /F /S /Q "./qxBlog.vcproj.B*" +del /F /S /Q "./qxBlog.vcproj.C*" +del /F /S /Q "./qxBlog.vcproj.D*" +del /F /S /Q "./qxBlog.vcproj.E*" +del /F /S /Q "./qxBlog.vcproj.F*" +del /F /S /Q "./qxBlog.vcproj.G*" +del /F /S /Q "./qxBlog.vcproj.H*" +del /F /S /Q "./qxBlog.vcproj.I*" +del /F /S /Q "./qxBlog.vcproj.J*" +del /F /S /Q "./qxBlog.vcproj.K*" +del /F /S /Q "./qxBlog.vcproj.L*" +del /F /S /Q "./qxBlog.vcproj.M*" +del /F /S /Q "./qxBlog.vcproj.N*" +del /F /S /Q "./qxBlog.vcproj.O*" +del /F /S /Q "./qxBlog.vcproj.P*" +del /F /S /Q "./qxBlog.vcproj.Q*" +del /F /S /Q "./qxBlog.vcproj.R*" +del /F /S /Q "./qxBlog.vcproj.S*" +del /F /S /Q "./qxBlog.vcproj.T*" +del /F /S /Q "./qxBlog.vcproj.U*" +del /F /S /Q "./qxBlog.vcproj.V*" +del /F /S /Q "./qxBlog.vcproj.W*" +del /F /S /Q "./qxBlog.vcproj.X*" +del /F /S /Q "./qxBlog.vcproj.Y*" +del /F /S /Q "./qxBlog.vcproj.Z*" +del /F /S /Q "./Makefile*" +del /F /S /Q "./vc90.pdb" +del /F /S /Q "./vc110.pdb" +del /F /S /Q "./*.sqlite" +cd "./qt/moc/" +del /F /S /Q "./*.cpp" +cd "../../" +cd "../../test/qxBlogRestApi/" +rmdir /S /Q "./obj/" +del /F /S /Q "./qxBlog.sdf" +del /F /S /Q "./qxBlog.ncb" +del /F /S /Q "./qxBlog.vcproj.A*" +del /F /S /Q "./qxBlog.vcproj.B*" +del /F /S /Q "./qxBlog.vcproj.C*" +del /F /S /Q "./qxBlog.vcproj.D*" +del /F /S /Q "./qxBlog.vcproj.E*" +del /F /S /Q "./qxBlog.vcproj.F*" +del /F /S /Q "./qxBlog.vcproj.G*" +del /F /S /Q "./qxBlog.vcproj.H*" +del /F /S /Q "./qxBlog.vcproj.I*" +del /F /S /Q "./qxBlog.vcproj.J*" +del /F /S /Q "./qxBlog.vcproj.K*" +del /F /S /Q "./qxBlog.vcproj.L*" +del /F /S /Q "./qxBlog.vcproj.M*" +del /F /S /Q "./qxBlog.vcproj.N*" +del /F /S /Q "./qxBlog.vcproj.O*" +del /F /S /Q "./qxBlog.vcproj.P*" +del /F /S /Q "./qxBlog.vcproj.Q*" +del /F /S /Q "./qxBlog.vcproj.R*" +del /F /S /Q "./qxBlog.vcproj.S*" +del /F /S /Q "./qxBlog.vcproj.T*" +del /F /S /Q "./qxBlog.vcproj.U*" +del /F /S /Q "./qxBlog.vcproj.V*" +del /F /S /Q "./qxBlog.vcproj.W*" +del /F /S /Q "./qxBlog.vcproj.X*" +del /F /S /Q "./qxBlog.vcproj.Y*" +del /F /S /Q "./qxBlog.vcproj.Z*" +del /F /S /Q "./Makefile*" +del /F /S /Q "./vc90.pdb" +del /F /S /Q "./vc110.pdb" +del /F /S /Q "./*.sqlite" +cd "./qt/moc/" +del /F /S /Q "./*.cpp" +cd "../../" +cd "../../test/qxBlogPImpl/" +rmdir /S /Q "./obj/" +del /F /S /Q "./qxBlog.sdf" +del /F /S /Q "./qxBlog.ncb" +del /F /S /Q "./qxBlog.vcproj.A*" +del /F /S /Q "./qxBlog.vcproj.B*" +del /F /S /Q "./qxBlog.vcproj.C*" +del /F /S /Q "./qxBlog.vcproj.D*" +del /F /S /Q "./qxBlog.vcproj.E*" +del /F /S /Q "./qxBlog.vcproj.F*" +del /F /S /Q "./qxBlog.vcproj.G*" +del /F /S /Q "./qxBlog.vcproj.H*" +del /F /S /Q "./qxBlog.vcproj.I*" +del /F /S /Q "./qxBlog.vcproj.J*" +del /F /S /Q "./qxBlog.vcproj.K*" +del /F /S /Q "./qxBlog.vcproj.L*" +del /F /S /Q "./qxBlog.vcproj.M*" +del /F /S /Q "./qxBlog.vcproj.N*" +del /F /S /Q "./qxBlog.vcproj.O*" +del /F /S /Q "./qxBlog.vcproj.P*" +del /F /S /Q "./qxBlog.vcproj.Q*" +del /F /S /Q "./qxBlog.vcproj.R*" +del /F /S /Q "./qxBlog.vcproj.S*" +del /F /S /Q "./qxBlog.vcproj.T*" +del /F /S /Q "./qxBlog.vcproj.U*" +del /F /S /Q "./qxBlog.vcproj.V*" +del /F /S /Q "./qxBlog.vcproj.W*" +del /F /S /Q "./qxBlog.vcproj.X*" +del /F /S /Q "./qxBlog.vcproj.Y*" +del /F /S /Q "./qxBlog.vcproj.Z*" +del /F /S /Q "./Makefile*" +del /F /S /Q "./vc90.pdb" +del /F /S /Q "./vc110.pdb" +del /F /S /Q "./*.sqlite" +cd "./qt/moc/" +del /F /S /Q "./*.cpp" +cd "../../" +cd "../../test/qxBlogCpp11/" +rmdir /S /Q "./obj/" +del /F /S /Q "./qxBlog.sdf" +del /F /S /Q "./qxBlog.ncb" +del /F /S /Q "./qxBlog.vcproj.A*" +del /F /S /Q "./qxBlog.vcproj.B*" +del /F /S /Q "./qxBlog.vcproj.C*" +del /F /S /Q "./qxBlog.vcproj.D*" +del /F /S /Q "./qxBlog.vcproj.E*" +del /F /S /Q "./qxBlog.vcproj.F*" +del /F /S /Q "./qxBlog.vcproj.G*" +del /F /S /Q "./qxBlog.vcproj.H*" +del /F /S /Q "./qxBlog.vcproj.I*" +del /F /S /Q "./qxBlog.vcproj.J*" +del /F /S /Q "./qxBlog.vcproj.K*" +del /F /S /Q "./qxBlog.vcproj.L*" +del /F /S /Q "./qxBlog.vcproj.M*" +del /F /S /Q "./qxBlog.vcproj.N*" +del /F /S /Q "./qxBlog.vcproj.O*" +del /F /S /Q "./qxBlog.vcproj.P*" +del /F /S /Q "./qxBlog.vcproj.Q*" +del /F /S /Q "./qxBlog.vcproj.R*" +del /F /S /Q "./qxBlog.vcproj.S*" +del /F /S /Q "./qxBlog.vcproj.T*" +del /F /S /Q "./qxBlog.vcproj.U*" +del /F /S /Q "./qxBlog.vcproj.V*" +del /F /S /Q "./qxBlog.vcproj.W*" +del /F /S /Q "./qxBlog.vcproj.X*" +del /F /S /Q "./qxBlog.vcproj.Y*" +del /F /S /Q "./qxBlog.vcproj.Z*" +del /F /S /Q "./Makefile*" +del /F /S /Q "./vc90.pdb" +del /F /S /Q "./vc110.pdb" +del /F /S /Q "./*.sqlite" +cd "./qt/moc/" +del /F /S /Q "./*.cpp" +cd "../../" +cd "../../test/qxClientServer/qxClient/" +rmdir /S /Q "./obj/" +del /F /S /Q "./qxClient.sdf" +del /F /S /Q "./qxClient.ncb" +del /F /S /Q "./qxClient.vcproj.A*" +del /F /S /Q "./qxClient.vcproj.B*" +del /F /S /Q "./qxClient.vcproj.C*" +del /F /S /Q "./qxClient.vcproj.D*" +del /F /S /Q "./qxClient.vcproj.E*" +del /F /S /Q "./qxClient.vcproj.F*" +del /F /S /Q "./qxClient.vcproj.G*" +del /F /S /Q "./qxClient.vcproj.H*" +del /F /S /Q "./qxClient.vcproj.I*" +del /F /S /Q "./qxClient.vcproj.J*" +del /F /S /Q "./qxClient.vcproj.K*" +del /F /S /Q "./qxClient.vcproj.L*" +del /F /S /Q "./qxClient.vcproj.M*" +del /F /S /Q "./qxClient.vcproj.N*" +del /F /S /Q "./qxClient.vcproj.O*" +del /F /S /Q "./qxClient.vcproj.P*" +del /F /S /Q "./qxClient.vcproj.Q*" +del /F /S /Q "./qxClient.vcproj.R*" +del /F /S /Q "./qxClient.vcproj.S*" +del /F /S /Q "./qxClient.vcproj.T*" +del /F /S /Q "./qxClient.vcproj.U*" +del /F /S /Q "./qxClient.vcproj.V*" +del /F /S /Q "./qxClient.vcproj.W*" +del /F /S /Q "./qxClient.vcproj.X*" +del /F /S /Q "./qxClient.vcproj.Y*" +del /F /S /Q "./qxClient.vcproj.Z*" +del /F /S /Q "./Makefile*" +del /F /S /Q "./vc90.pdb" +del /F /S /Q "./vc110.pdb" +del /F /S /Q "./*.db" +cd "./qt/moc/" +del /F /S /Q "./*.cpp" +cd "../../../" +cd "../../test/qxClientServer/qxServer/" +rmdir /S /Q "./obj/" +del /F /S /Q "./qxServer.sdf" +del /F /S /Q "./qxServer.ncb" +del /F /S /Q "./qxServer.vcproj.A*" +del /F /S /Q "./qxServer.vcproj.B*" +del /F /S /Q "./qxServer.vcproj.C*" +del /F /S /Q "./qxServer.vcproj.D*" +del /F /S /Q "./qxServer.vcproj.E*" +del /F /S /Q "./qxServer.vcproj.F*" +del /F /S /Q "./qxServer.vcproj.G*" +del /F /S /Q "./qxServer.vcproj.H*" +del /F /S /Q "./qxServer.vcproj.I*" +del /F /S /Q "./qxServer.vcproj.J*" +del /F /S /Q "./qxServer.vcproj.K*" +del /F /S /Q "./qxServer.vcproj.L*" +del /F /S /Q "./qxServer.vcproj.M*" +del /F /S /Q "./qxServer.vcproj.N*" +del /F /S /Q "./qxServer.vcproj.O*" +del /F /S /Q "./qxServer.vcproj.P*" +del /F /S /Q "./qxServer.vcproj.Q*" +del /F /S /Q "./qxServer.vcproj.R*" +del /F /S /Q "./qxServer.vcproj.S*" +del /F /S /Q "./qxServer.vcproj.T*" +del /F /S /Q "./qxServer.vcproj.U*" +del /F /S /Q "./qxServer.vcproj.V*" +del /F /S /Q "./qxServer.vcproj.W*" +del /F /S /Q "./qxServer.vcproj.X*" +del /F /S /Q "./qxServer.vcproj.Y*" +del /F /S /Q "./qxServer.vcproj.Z*" +del /F /S /Q "./Makefile*" +del /F /S /Q "./vc90.pdb" +del /F /S /Q "./vc110.pdb" +del /F /S /Q "./*.db" +cd "./qt/moc/" +del /F /S /Q "./*.cpp" +cd "../../../" +cd "../../test/qxClientServer/qxService/" +rmdir /S /Q "./obj/" +del /F /S /Q "./qxService.sdf" +del /F /S /Q "./qxService.ncb" +del /F /S /Q "./qxService.vcproj.A*" +del /F /S /Q "./qxService.vcproj.B*" +del /F /S /Q "./qxService.vcproj.C*" +del /F /S /Q "./qxService.vcproj.D*" +del /F /S /Q "./qxService.vcproj.E*" +del /F /S /Q "./qxService.vcproj.F*" +del /F /S /Q "./qxService.vcproj.G*" +del /F /S /Q "./qxService.vcproj.H*" +del /F /S /Q "./qxService.vcproj.I*" +del /F /S /Q "./qxService.vcproj.J*" +del /F /S /Q "./qxService.vcproj.K*" +del /F /S /Q "./qxService.vcproj.L*" +del /F /S /Q "./qxService.vcproj.M*" +del /F /S /Q "./qxService.vcproj.N*" +del /F /S /Q "./qxService.vcproj.O*" +del /F /S /Q "./qxService.vcproj.P*" +del /F /S /Q "./qxService.vcproj.Q*" +del /F /S /Q "./qxService.vcproj.R*" +del /F /S /Q "./qxService.vcproj.S*" +del /F /S /Q "./qxService.vcproj.T*" +del /F /S /Q "./qxService.vcproj.U*" +del /F /S /Q "./qxService.vcproj.V*" +del /F /S /Q "./qxService.vcproj.W*" +del /F /S /Q "./qxService.vcproj.X*" +del /F /S /Q "./qxService.vcproj.Y*" +del /F /S /Q "./qxService.vcproj.Z*" +del /F /S /Q "./Makefile*" +del /F /S /Q "./vc90.pdb" +del /F /S /Q "./vc110.pdb" +del /F /S /Q "./*.db" +cd "./qt/moc/" +del /F /S /Q "./*.cpp" +cd "../../../" +cd "../../test/_bin/" +del /F /S /Q "./*.*" +cd "../../test/qxDllSample/dll1/debug/" +del /F /S /Q "./*.*" +cd "../../../../test/qxDllSample/dll1/release/" +del /F /S /Q "./*.*" +cd "../../../../test/qxDllSample/dll2/debug/" +del /F /S /Q "./*.*" +cd "../../../../test/qxDllSample/dll2/release/" +del /F /S /Q "./*.*" +cd "../../../../test/qxDllSample/exe/debug/" +del /F /S /Q "./*.*" +cd "../../../../test/qxDllSample/exe/release/" +del /F /S /Q "./*.*" +cd "../../../../test/qxBlog/debug/" +del /F /S /Q "./*.*" +cd "../../../test/qxBlog/release/" +del /F /S /Q "./*.*" +cd "../../../test/qxBlogCompositeKey/debug/" +del /F /S /Q "./*.*" +cd "../../../test/qxBlogCompositeKey/release/" +del /F /S /Q "./*.*" +cd "../../../test/qxBlogModelView/debug/" +del /F /S /Q "./*.*" +cd "../../../test/qxBlogModelView/release/" +del /F /S /Q "./*.*" +cd "../../../test/qxBlogCpp11/debug/" +del /F /S /Q "./*.*" +cd "../../../test/qxBlogCpp11/release/" +del /F /S /Q "./*.*" +cd "../../../test/qxClientServer/qxClient/debug/" +del /F /S /Q "./*.*" +cd "../../../../test/qxClientServer/qxClient/release/" +del /F /S /Q "./*.*" +cd "../../../../test/qxClientServer/qxServer/debug/" +del /F /S /Q "./*.*" +cd "../../../../test/qxClientServer/qxServer/release/" +del /F /S /Q "./*.*" +cd "../../../../test/qxClientServer/qxService/debug/" +del /F /S /Q "./*.*" +cd "../../../../test/qxClientServer/qxService/release/" +del /F /S /Q "./*.*" +cd "../../../../" +cd "./tools/" +call "./gitignore_recursive.bat" +pause diff --git a/tools/gcc_build_all_debug_full.sh b/tools/gcc_build_all_debug_full.sh new file mode 100644 index 0000000..ae57fd3 --- /dev/null +++ b/tools/gcc_build_all_debug_full.sh @@ -0,0 +1,160 @@ +#!/bin/bash + +if [[ $1 == "release" ]]; +then + CONFIG=release +else + CONFIG=debug + SUFFIX=d +fi + +if [[ $2 == "full" ]] || [ -z "$2" ]; +then + QMAKEPARAMS="\"DEFINES += _QX_ENABLE_BOOST_SERIALIZATION\" \"DEFINES += _QX_ENABLE_QT_GUI\" \"DEFINES += _QX_ENABLE_QT_NETWORK\"" +fi + +clear +pwd + +echo "-- BOOST ENVIRONMENT VARIABLES --" +export BOOST_INCLUDE=/usr/include +export BOOST_LIB=/usr/lib +export BOOST_LIB_SERIALIZATION_DEBUG=boost_serialization-mt-d +export BOOST_LIB_SERIALIZATION_RELEASE=boost_serialization-mt +export BOOST_LIB_WIDE_SERIALIZATION_DEBUG=boost_wserialization-mt-d +export BOOST_LIB_WIDE_SERIALIZATION_RELEASE=boost_wserialization-mt + +echo "-- MAKE OPTIONS : USE 8 CORE CPU TO REDUCE BUILD TIMES --" +MAKEOPT=-j8 + +GCCVERSION="$(gcc -dumpversion)" +if [ "$GCCVERSION" = "4.4.1" ]; +then + echo "-- ADD GCC C++11 FEATURES --" + QMAKEPARAMS="$QMAKEPARAMS \"QMAKE_CXXFLAGS += -std=c++0x\"" +fi + +echo "-- BUILD QXORM LIBRARY --" +cd "../" +eval qmake $QMAKEPARAMS +make $CONFIG $MAKEOPT +if [ ! -f ./lib/libQxOrm$SUFFIX.so ]; +then + exit 1 +fi +cd "./lib/" +cp libQxOrm$SUFFIX.* "../test/_bin/" +cd "../" + +echo "-- BUILD TEST DLL1 --" +cd "./test/qxDllSample/dll1/" +eval qmake $QMAKEPARAMS +make $CONFIG $MAKEOPT +if [ ! -f ../../_bin/libdll1$SUFFIX.so ]; +then + exit 1 +fi + +echo "-- BUILD TEST DLL2 --" +cd "../dll2/" +eval qmake $QMAKEPARAMS +make $CONFIG $MAKEOPT +if [ ! -f ../../_bin/libdll2$SUFFIX.so ]; +then + exit 1 +fi + +echo "-- BUILD TEST EXE --" +cd "../exe/" +eval qmake $QMAKEPARAMS +make $CONFIG $MAKEOPT +if [ ! -f ../../_bin/exe$SUFFIX ]; +then + exit 1 +fi + +echo "-- BUILD TEST QXBLOG --" +cd "../../qxBlog/" +eval qmake $QMAKEPARAMS +make $CONFIG $MAKEOPT +if [ ! -f ../_bin/qxBlog$SUFFIX ]; +then + exit 1 +fi + +echo "-- BUILD TEST QXBLOG COMPOSITE KEY --" +cd "../qxBlogCompositeKey/" +eval qmake $QMAKEPARAMS +make $CONFIG $MAKEOPT +if [ ! -f ../_bin/qxBlogCompositeKey$SUFFIX ]; +then + exit 1 +fi + +echo "-- BUILD TEST QXBLOG MODEL VIEW --" +cd "../qxBlogModelView/" +eval qmake $QMAKEPARAMS +make $CONFIG $MAKEOPT +if [ ! -f ../_bin/qxBlogModelView$SUFFIX ]; +then + exit 1 +fi + +echo "-- BUILD TEST QXBLOG PIMPL IDIOM --" +cd "../qxBlogPImpl/" +eval qmake $QMAKEPARAMS +make $CONFIG $MAKEOPT +if [ ! -f ../_bin/qxBlogPImpl$SUFFIX ]; +then + exit 1 +fi + +echo "-- BUILD TEST QXBLOG REST API AND HTTP SERVER --" +cd "../qxBlogRestApi/" +eval qmake $QMAKEPARAMS +make $CONFIG $MAKEOPT +if [ ! -f ../_bin/qxBlogRestApi$SUFFIX ]; +then + exit 1 +fi + +echo "-- CHECK BATCH TYPE : FULL OR MINIMAL --" +if [[ $2 == "minimal" ]]; +then + exit 0 +fi + +echo "-- BUILD TEST QXCLIENTSERVER QXSERVICE --" +cd "../qxClientServer/qxService" +eval qmake $QMAKEPARAMS qxServiceServer.pro +make $CONFIG $MAKEOPT +if [ ! -f ../../_bin/libqxServiceServer$SUFFIX.so ]; +then + exit 1 +fi +eval qmake $QMAKEPARAMS qxServiceClient.pro +make $CONFIG $MAKEOPT +if [ ! -f ../../_bin/libqxServiceClient$SUFFIX.so ]; +then + exit 1 +fi + +echo "-- BUILD TEST QXCLIENTSERVER QXSERVER --" +cd "../qxServer/" +eval qmake $QMAKEPARAMS +make $CONFIG $MAKEOPT +if [ ! -f ../../_bin/qxServer$SUFFIX ]; +then + exit 1 +fi + +echo "-- BUILD TEST QXCLIENTSERVER QXCLIENT --" +cd "../qxClient/" +eval qmake $QMAKEPARAMS +make $CONFIG $MAKEOPT +if [ ! -f ../../_bin/qxClient$SUFFIX ]; +then + exit 1 +fi + +exit 0 diff --git a/tools/gcc_build_all_debug_minimal.sh b/tools/gcc_build_all_debug_minimal.sh new file mode 100644 index 0000000..8aa39bb --- /dev/null +++ b/tools/gcc_build_all_debug_minimal.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +chmod u+rwx ./gcc_build_all_debug_full.sh +./gcc_build_all_debug_full.sh debug minimal diff --git a/tools/gcc_build_all_release_full.sh b/tools/gcc_build_all_release_full.sh new file mode 100644 index 0000000..75a6fa8 --- /dev/null +++ b/tools/gcc_build_all_release_full.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +chmod u+rwx ./gcc_build_all_debug_full.sh +./gcc_build_all_debug_full.sh release full diff --git a/tools/gcc_build_all_release_minimal.sh b/tools/gcc_build_all_release_minimal.sh new file mode 100644 index 0000000..3b1ba63 --- /dev/null +++ b/tools/gcc_build_all_release_minimal.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +chmod u+rwx ./gcc_build_all_debug_full.sh +./gcc_build_all_debug_full.sh release minimal diff --git a/tools/gitignore_recursive.bat b/tools/gitignore_recursive.bat new file mode 100644 index 0000000..8befc99 --- /dev/null +++ b/tools/gitignore_recursive.bat @@ -0,0 +1 @@ +cscript.exe gitignore_recursive.vbs diff --git a/tools/gitignore_recursive.file b/tools/gitignore_recursive.file new file mode 100644 index 0000000..efadbd8 --- /dev/null +++ b/tools/gitignore_recursive.file @@ -0,0 +1,7 @@ +# git does not allow empty directories. +# Yet, we need to add this empty directory on git. +# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it. +# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this. +* +# And then add an exception for this specifc file (so that we can commit it). +!.gitignore diff --git a/tools/gitignore_recursive.vbs b/tools/gitignore_recursive.vbs new file mode 100644 index 0000000..13bc7a7 --- /dev/null +++ b/tools/gitignore_recursive.vbs @@ -0,0 +1,22 @@ +gitRepoFolder = "../" +gitIgnoreFile = "./gitignore_recursive.file" + +Set fso = CreateObject("Scripting.FileSystemObject") +Call checkFolder(fso.GetFolder(gitRepoFolder), 999) + +Sub checkFolder(folder, depth) + If (depth > 0) Then + For Each subFolder In folder.SubFolders + Call checkFolder(subFolder, (depth - 1)) + Next + End If + + folderPath = Replace(folder.Path, "\", "/") + If (Right(folderPath, 1) <> "/") Then folderPath = (folderPath + "/") + If (InStr(folderPath, ".git") > 0) Then Exit Sub + + If (((folder.Files.Count <= 0) And (folder.SubFolders.Count <= 0)) Or (Right(folderPath, 17) = "doc/doxygen/html/")) Then + Call Wscript.Echo("add '.gitignore' file to folder : " + folderPath) + Call fso.CopyFile(gitIgnoreFile, (folderPath + ".gitignore")) + End If +End Sub diff --git a/tools/mingw_build_all_debug_qt4_full.bat b/tools/mingw_build_all_debug_qt4_full.bat new file mode 100644 index 0000000..ed3b68e --- /dev/null +++ b/tools/mingw_build_all_debug_qt4_full.bat @@ -0,0 +1,106 @@ +REM -- BATCH PARAMETERS -- +set BATCH_TYPE=full +set MINGW_CONFIG=debug +set LIB_SUFFIX=d +if "%1"=="release" set MINGW_CONFIG=%1 +if "%1"=="release" set LIB_SUFFIX= +if "%2"=="minimal" set BATCH_TYPE=%2 +if "%BATCH_TYPE%"=="full" set QXORM_QMAKE_PARAMS="DEFINES += _QX_ENABLE_BOOST_SERIALIZATION" "DEFINES += _QX_ENABLE_QT_GUI" "DEFINES += _QX_ENABLE_QT_NETWORK" +set QXORM_QMAKE_PARAMS=%QXORM_QMAKE_PARAMS% "QMAKE_CXXFLAGS += -std=c++0x" + +REM -- QT ENVIRONMENT VARIABLES -- +set QT_DIR=%QT4_MINGW% +set PATH=%QT_DIR%\bin;%PATH% + +REM -- BOOST ENVIRONMENT VARIABLES -- +set BOOST_INCLUDE=%BOOST_DIR%\include +set BOOST_LIB=%BOOST_DIR%\lib_shared +set BOOST_LIB_SERIALIZATION_DEBUG=libboost_serialization-mgw45-mt-d-1_57 +set BOOST_LIB_SERIALIZATION_RELEASE=libboost_serialization-mgw45-mt-1_57 +set BOOST_LIB_WIDE_SERIALIZATION_DEBUG=libboost_wserialization-mgw45-mt-d-1_57 +set BOOST_LIB_WIDE_SERIALIZATION_RELEASE=libboost_wserialization-mgw45-mt-1_57 + +REM -- MAKE OPTIONS : USE 8 CORE CPU TO REDUCE BUILD TIMES -- +set MAKE_COMMAND=make -j8 + +REM -- BUILD QXORM LIBRARY -- +cd "../" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "./lib/QxOrm%LIB_SUFFIX%.dll" GOTO END + +REM -- BUILD TEST DLL1 -- +cd "./test/qxDllSample/dll1/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../../_bin/dll1%LIB_SUFFIX%.dll" GOTO END + +REM -- BUILD TEST DLL2 -- +cd "../dll2/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../../_bin/dll2%LIB_SUFFIX%.dll" GOTO END + +REM -- BUILD TEST EXE -- +cd "../exe/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../../_bin/exe%LIB_SUFFIX%.exe" GOTO END + +REM -- BUILD TEST QXBLOG -- +cd "../../qxBlog/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../_bin/qxBlog%LIB_SUFFIX%.exe" GOTO END + +REM -- BUILD TEST QXBLOG COMPOSITE KEY -- +cd "../qxBlogCompositeKey/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../_bin/qxBlogCompositeKey%LIB_SUFFIX%.exe" GOTO END + +REM -- BUILD TEST QXBLOG MODEL VIEW -- +cd "../qxBlogModelView/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../_bin/qxBlogModelView%LIB_SUFFIX%.exe" GOTO END + +REM -- BUILD TEST QXBLOG PIMPL IDIOM -- +cd "../qxBlogPImpl/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../_bin/qxBlogPImpl%LIB_SUFFIX%.exe" GOTO END + +REM -- BUILD TEST QXBLOG REST API AND HTTP SERVER -- +cd "../qxBlogRestApi/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../_bin/qxBlogRestApi%LIB_SUFFIX%.exe" GOTO END + +REM -- CHECK BATCH TYPE : FULL OR MINIMAL -- +IF "%BATCH_TYPE%"=="minimal" GOTO END + +REM -- BUILD TEST QXCLIENTSERVER QXSERVICE -- +cd "../qxClientServer/qxService" +qmake %QXORM_QMAKE_PARAMS% qxServiceServer.pro +make %MINGW_CONFIG% +IF NOT EXIST "../../_bin/qxServiceServer%LIB_SUFFIX%.dll" GOTO END +qmake %QXORM_QMAKE_PARAMS% qxServiceClient.pro +make %MINGW_CONFIG% +IF NOT EXIST "../../_bin/qxServiceClient%LIB_SUFFIX%.dll" GOTO END + +REM -- BUILD TEST QXCLIENTSERVER QXSERVER -- +cd "../qxServer/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../../_bin/qxServer%LIB_SUFFIX%.exe" GOTO END + +REM -- BUILD TEST QXCLIENTSERVER QXCLIENT -- +cd "../qxClient/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../../_bin/qxClient%LIB_SUFFIX%.exe" GOTO END + +REM -- BATCH FINISHED -- +:END +pause diff --git a/tools/mingw_build_all_debug_qt4_minimal.bat b/tools/mingw_build_all_debug_qt4_minimal.bat new file mode 100644 index 0000000..aa44f87 --- /dev/null +++ b/tools/mingw_build_all_debug_qt4_minimal.bat @@ -0,0 +1 @@ +CALL mingw_build_all_debug_qt4_full.bat debug minimal diff --git a/tools/mingw_build_all_debug_qt5_full.bat b/tools/mingw_build_all_debug_qt5_full.bat new file mode 100644 index 0000000..8ae6cdd --- /dev/null +++ b/tools/mingw_build_all_debug_qt5_full.bat @@ -0,0 +1,113 @@ +REM -- BATCH PARAMETERS -- +set BATCH_TYPE=full +set MINGW_CONFIG=debug +set LIB_SUFFIX=d +set QXORM_QMAKE_PARAMS="DEFINES += _QX_NO_PRECOMPILED_HEADER" +if "%1"=="release" set MINGW_CONFIG=%1 +if "%1"=="release" set LIB_SUFFIX= +if "%2"=="minimal" set BATCH_TYPE=%2 +if "%BATCH_TYPE%"=="full" set QXORM_QMAKE_PARAMS=%QXORM_QMAKE_PARAMS% "DEFINES += _QX_ENABLE_BOOST_SERIALIZATION" "DEFINES += _QX_ENABLE_QT_GUI" "DEFINES += _QX_ENABLE_QT_NETWORK" + +REM -- QT ENVIRONMENT VARIABLES -- +set MINGW_DIR=%MINGW_QT5_DIR% +set QT_DIR=%QT5_MINGW% +set PATH=%MINGW_DIR%\bin;%QT_DIR%\bin;%PATH% + +REM -- BOOST ENVIRONMENT VARIABLES -- +set BOOST_INCLUDE=%BOOST_DIR%\include +set BOOST_LIB=%BOOST_DIR%\lib_shared +set BOOST_LIB_SERIALIZATION_DEBUG=libboost_serialization-mgw49-mt-d-1_57 +set BOOST_LIB_SERIALIZATION_RELEASE=libboost_serialization-mgw49-mt-1_57 +set BOOST_LIB_WIDE_SERIALIZATION_DEBUG=libboost_wserialization-mgw49-mt-d-1_57 +set BOOST_LIB_WIDE_SERIALIZATION_RELEASE=libboost_wserialization-mgw49-mt-1_57 + +REM -- MAKE OPTIONS : USE 8 CORE CPU TO REDUCE BUILD TIMES -- +set MAKE_COMMAND=make -j8 + +REM -- BUILD QXORM LIBRARY -- +cd "../" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "./lib/QxOrm%LIB_SUFFIX%.dll" GOTO END + +REM -- BUILD TEST DLL1 -- +cd "./test/qxDllSample/dll1/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../../_bin/dll1%LIB_SUFFIX%.dll" GOTO END + +REM -- BUILD TEST DLL2 -- +cd "../dll2/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../../_bin/dll2%LIB_SUFFIX%.dll" GOTO END + +REM -- BUILD TEST EXE -- +cd "../exe/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../../_bin/exe%LIB_SUFFIX%.exe" GOTO END + +REM -- BUILD TEST QXBLOG -- +cd "../../qxBlog/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../_bin/qxBlog%LIB_SUFFIX%.exe" GOTO END + +REM -- BUILD TEST QXBLOG COMPOSITE KEY -- +cd "../qxBlogCompositeKey/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../_bin/qxBlogCompositeKey%LIB_SUFFIX%.exe" GOTO END + +REM -- BUILD TEST QXBLOG MODEL VIEW -- +cd "../qxBlogModelView/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../_bin/qxBlogModelView%LIB_SUFFIX%.exe" GOTO END + +REM -- BUILD TEST QXBLOG PIMPL IDIOM -- +cd "../qxBlogPImpl/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../_bin/qxBlogPImpl%LIB_SUFFIX%.exe" GOTO END + +REM -- BUILD TEST QXBLOG REST API AND HTTP SERVER -- +cd "../qxBlogRestApi/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../_bin/qxBlogRestApi%LIB_SUFFIX%.exe" GOTO END + +REM -- CHECK BATCH TYPE : FULL OR MINIMAL -- +IF "%BATCH_TYPE%"=="minimal" GOTO END + +REM -- BUILD TEST QXBLOG C++11 -- +cd "../qxBlogCpp11/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../_bin/qxBlogCpp11%LIB_SUFFIX%.exe" GOTO END + +REM -- BUILD TEST QXCLIENTSERVER QXSERVICE -- +cd "../qxClientServer/qxService" +qmake %QXORM_QMAKE_PARAMS% qxServiceServer.pro +make %MINGW_CONFIG% +IF NOT EXIST "../../_bin/qxServiceServer%LIB_SUFFIX%.dll" GOTO END +qmake %QXORM_QMAKE_PARAMS% qxServiceClient.pro +make %MINGW_CONFIG% +IF NOT EXIST "../../_bin/qxServiceClient%LIB_SUFFIX%.dll" GOTO END + +REM -- BUILD TEST QXCLIENTSERVER QXSERVER -- +cd "../qxServer/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../../_bin/qxServer%LIB_SUFFIX%.exe" GOTO END + +REM -- BUILD TEST QXCLIENTSERVER QXCLIENT -- +cd "../qxClient/" +qmake %QXORM_QMAKE_PARAMS% +make %MINGW_CONFIG% +IF NOT EXIST "../../_bin/qxClient%LIB_SUFFIX%.exe" GOTO END + +REM -- BATCH FINISHED -- +:END +pause diff --git a/tools/mingw_build_all_debug_qt5_minimal.bat b/tools/mingw_build_all_debug_qt5_minimal.bat new file mode 100644 index 0000000..373d2e3 --- /dev/null +++ b/tools/mingw_build_all_debug_qt5_minimal.bat @@ -0,0 +1 @@ +CALL mingw_build_all_debug_qt5_full.bat debug minimal diff --git a/tools/mingw_build_all_release_qt4_full.bat b/tools/mingw_build_all_release_qt4_full.bat new file mode 100644 index 0000000..5a80bca --- /dev/null +++ b/tools/mingw_build_all_release_qt4_full.bat @@ -0,0 +1 @@ +CALL mingw_build_all_debug_qt4_full.bat release full diff --git a/tools/mingw_build_all_release_qt4_minimal.bat b/tools/mingw_build_all_release_qt4_minimal.bat new file mode 100644 index 0000000..daadd5d --- /dev/null +++ b/tools/mingw_build_all_release_qt4_minimal.bat @@ -0,0 +1 @@ +CALL mingw_build_all_debug_qt4_full.bat release minimal diff --git a/tools/mingw_build_all_release_qt5_full.bat b/tools/mingw_build_all_release_qt5_full.bat new file mode 100644 index 0000000..a913bfd --- /dev/null +++ b/tools/mingw_build_all_release_qt5_full.bat @@ -0,0 +1 @@ +CALL mingw_build_all_debug_qt5_full.bat release full diff --git a/tools/mingw_build_all_release_qt5_minimal.bat b/tools/mingw_build_all_release_qt5_minimal.bat new file mode 100644 index 0000000..83d610b --- /dev/null +++ b/tools/mingw_build_all_release_qt5_minimal.bat @@ -0,0 +1 @@ +CALL mingw_build_all_debug_qt5_full.bat release minimal diff --git a/tools/msvc2012_build_all_debug_32b_full.bat b/tools/msvc2012_build_all_debug_32b_full.bat new file mode 100644 index 0000000..a8eea00 --- /dev/null +++ b/tools/msvc2012_build_all_debug_32b_full.bat @@ -0,0 +1,76 @@ +REM -- BATCH PARAMETERS -- +set BATCH_TYPE=full +set MSVC_CONFIG=Debug +set MSVC_PLATFORM=Win32 +set LIB_SUFFIX=d +if "%1"=="release" set MSVC_CONFIG=Release +if "%1"=="release" set LIB_SUFFIX= +if "%2"=="x64" set MSVC_PLATFORM=x64 +if "%3"=="minimal" set BATCH_TYPE=%3 +if "%BATCH_TYPE%"=="full" set QXORM_QMAKE_PARAMS="DEFINES += _QX_ENABLE_BOOST_SERIALIZATION" "DEFINES += _QX_ENABLE_QT_GUI" "DEFINES += _QX_ENABLE_QT_NETWORK" "DEFINES += _QX_ENABLE_MONGODB" + +REM -- QT ENVIRONMENT VARIABLES -- +set QT_DIR=%QT5_MSVC2012_32B% +if "%2"=="x64" set QT_DIR=%QT5_MSVC2012_64B% +set PATH=%QT_DIR%\bin;%PATH% + +REM -- BOOST ENVIRONMENT VARIABLES -- +set BOOST_INCLUDE=%BOOST_DIR%\include +set BOOST_LIB=%BOOST_DIR%\lib_shared +if "%2"=="x64" set BOOST_LIB=%BOOST_DIR%\lib_shared_64b +set BOOST_LIB_SERIALIZATION_DEBUG=boost_serialization-vc110-mt-gd-1_57 +set BOOST_LIB_SERIALIZATION_RELEASE=boost_serialization-vc110-mt-1_57 +set BOOST_LIB_WIDE_SERIALIZATION_DEBUG=boost_wserialization-vc110-mt-gd-1_57 +set BOOST_LIB_WIDE_SERIALIZATION_RELEASE=boost_wserialization-vc110-mt-1_57 + +REM -- LOAD MSVC ENVIRONMENT VARIABLES -- +IF EXIST "%PROGRAMFILES(X86)%" SET PROGFILES32=%PROGRAMFILES(X86)% +IF NOT EXIST "%PROGRAMFILES(X86)%" SET PROGFILES32=%PROGRAMFILES% +CALL "%PROGFILES32%\Microsoft Visual Studio 11.0\VC\vcvarsall.bat" x86 +SET CL=/MP +CD "..\" + +REM -- BUILD QXORM LIBRARY -- +msbuild ".\QxOrm.sln" /p:Configuration="%MSVC_CONFIG%" /p:Platform="%MSVC_PLATFORM%" /t:build /nologo /v:detailed +IF NOT EXIST ".\lib\QxOrm%LIB_SUFFIX%.dll" GOTO END + +REM -- BUILD TEST DLL SAMPLE -- +msbuild ".\test\qxDllSample\test.sln" /p:Configuration="%MSVC_CONFIG%" /p:Platform="%MSVC_PLATFORM%" /t:build /nologo /v:detailed +IF NOT EXIST ".\test\_bin\exe%LIB_SUFFIX%.exe" GOTO END + +REM -- BUILD TEST QXBLOG -- +msbuild ".\test\qxBlog\qxBlog.sln" /p:Configuration="%MSVC_CONFIG%" /p:Platform="%MSVC_PLATFORM%" /t:build /nologo /v:detailed +IF NOT EXIST ".\test\_bin\qxBlog%LIB_SUFFIX%.exe" GOTO END + +REM -- BUILD TEST QXBLOG COMPOSITE KEY -- +msbuild ".\test\qxBlogCompositeKey\qxBlog.sln" /p:Configuration="%MSVC_CONFIG%" /p:Platform="%MSVC_PLATFORM%" /t:build /nologo /v:detailed +IF NOT EXIST ".\test\_bin\qxBlogCompositeKey%LIB_SUFFIX%.exe" GOTO END + +REM -- BUILD TEST QXBLOG MODEL VIEW -- +msbuild ".\test\qxBlogModelView\qxBlog.sln" /p:Configuration="%MSVC_CONFIG%" /p:Platform="%MSVC_PLATFORM%" /t:build /nologo /v:detailed +IF NOT EXIST ".\test\_bin\qxBlogModelView%LIB_SUFFIX%.exe" GOTO END + +REM -- BUILD TEST QXBLOG PIMPL IDIOM -- +msbuild ".\test\qxBlogPImpl\qxBlog.sln" /p:Configuration="%MSVC_CONFIG%" /p:Platform="%MSVC_PLATFORM%" /t:build /nologo /v:detailed +IF NOT EXIST ".\test\_bin\qxBlogPImpl%LIB_SUFFIX%.exe" GOTO END + +REM -- BUILD TEST QXBLOG REST API AND HTTP SERVER -- +msbuild ".\test\qxBlogRestApi\qxBlog.sln" /p:Configuration="%MSVC_CONFIG%" /p:Platform="%MSVC_PLATFORM%" /t:build /nologo /v:detailed +IF NOT EXIST ".\test\_bin\qxBlogRestApi%LIB_SUFFIX%.exe" GOTO END + +REM -- CHECK BATCH TYPE : FULL OR MINIMAL -- +IF "%BATCH_TYPE%"=="minimal" GOTO END + +REM -- BUILD TEST QXBLOG C++11 -- +msbuild ".\test\qxBlogCpp11\qxBlog.sln" /p:Configuration="%MSVC_CONFIG%" /p:Platform="%MSVC_PLATFORM%" /t:build /nologo /v:detailed +IF NOT EXIST ".\test\_bin\qxBlogCpp11%LIB_SUFFIX%.exe" GOTO END + +REM -- BUILD TEST QXCLIENTSERVER -- +msbuild ".\test\qxClientServer\qxClient\qxClient.sln" /p:Configuration="%MSVC_CONFIG%" /p:Platform="%MSVC_PLATFORM%" /t:build /nologo /v:detailed +IF NOT EXIST ".\test\_bin\qxClient%LIB_SUFFIX%.exe" GOTO END +msbuild ".\test\qxClientServer\qxServer\qxServer.sln" /p:Configuration="%MSVC_CONFIG%" /p:Platform="%MSVC_PLATFORM%" /t:build /nologo /v:detailed +IF NOT EXIST ".\test\_bin\qxServer%LIB_SUFFIX%.exe" GOTO END + +REM -- BATCH FINISHED -- +:END +PAUSE diff --git a/tools/msvc2012_build_all_debug_32b_minimal.bat b/tools/msvc2012_build_all_debug_32b_minimal.bat new file mode 100644 index 0000000..c5212dd --- /dev/null +++ b/tools/msvc2012_build_all_debug_32b_minimal.bat @@ -0,0 +1 @@ +CALL msvc2012_build_all_debug_32b_full.bat debug x86 minimal diff --git a/tools/msvc2012_build_all_debug_64b_full.bat b/tools/msvc2012_build_all_debug_64b_full.bat new file mode 100644 index 0000000..485037c --- /dev/null +++ b/tools/msvc2012_build_all_debug_64b_full.bat @@ -0,0 +1 @@ +CALL msvc2012_build_all_debug_32b_full.bat debug x64 full diff --git a/tools/msvc2012_build_all_debug_64b_minimal.bat b/tools/msvc2012_build_all_debug_64b_minimal.bat new file mode 100644 index 0000000..a891890 --- /dev/null +++ b/tools/msvc2012_build_all_debug_64b_minimal.bat @@ -0,0 +1 @@ +CALL msvc2012_build_all_debug_32b_full.bat debug x64 minimal diff --git a/tools/msvc2012_build_all_release_32b_full.bat b/tools/msvc2012_build_all_release_32b_full.bat new file mode 100644 index 0000000..afb42da --- /dev/null +++ b/tools/msvc2012_build_all_release_32b_full.bat @@ -0,0 +1 @@ +CALL msvc2012_build_all_debug_32b_full.bat release x86 full diff --git a/tools/msvc2012_build_all_release_32b_minimal.bat b/tools/msvc2012_build_all_release_32b_minimal.bat new file mode 100644 index 0000000..29ad044 --- /dev/null +++ b/tools/msvc2012_build_all_release_32b_minimal.bat @@ -0,0 +1 @@ +CALL msvc2012_build_all_debug_32b_full.bat release x86 minimal diff --git a/tools/msvc2012_build_all_release_64b_full.bat b/tools/msvc2012_build_all_release_64b_full.bat new file mode 100644 index 0000000..67e92c1 --- /dev/null +++ b/tools/msvc2012_build_all_release_64b_full.bat @@ -0,0 +1 @@ +CALL msvc2012_build_all_debug_32b_full.bat release x64 full diff --git a/tools/msvc2012_build_all_release_64b_minimal.bat b/tools/msvc2012_build_all_release_64b_minimal.bat new file mode 100644 index 0000000..e4ddd64 --- /dev/null +++ b/tools/msvc2012_build_all_release_64b_minimal.bat @@ -0,0 +1 @@ +CALL msvc2012_build_all_debug_32b_full.bat release x64 minimal diff --git a/tools/msvc2015_build_all_debug_64b_full.bat b/tools/msvc2015_build_all_debug_64b_full.bat new file mode 100644 index 0000000..ef0133e --- /dev/null +++ b/tools/msvc2015_build_all_debug_64b_full.bat @@ -0,0 +1,23 @@ +REM -- BATCH DEFAULT PARAMETERS -- +set QT_DIR=%QT5_8_MSVC2015_64B% +set PATH=%QT_DIR%\bin;%PATH% +set MSVC_CONFIG=Debug +set CL=/MP +set BATCH_TYPE=full +set CMAKE_OPTIONS=-D_QX_UNITY_BUILD=1 + +REM -- CHECK BATCH COMMAND LINE OPTIONS -- +if "%1"=="release" set MSVC_CONFIG=Release +if "%2"=="minimal" set BATCH_TYPE=%2 +if "%BATCH_TYPE%"=="full" set CMAKE_OPTIONS=%CMAKE_OPTIONS% -D_QX_ENABLE_BOOST=1 -D_QX_ENABLE_QT_GUI=1 -D_QX_ENABLE_QT_NETWORK=1 + +REM -- BATCH EXECUTION DIRECTORY -- +cd "..\test\" +set QX_TEST_DIR="%cd%" +cd ".\build\" + +REM -- CALL CMAKE TO BUILD QXORM LIBRARY AND ALL SAMPLES PROJECTS -- +cmake -G"Visual Studio 14 2015 Win64" %QX_TEST_DIR% %CMAKE_OPTIONS% +cmake --build . --config %MSVC_CONFIG% + +pause diff --git a/tools/msvc2015_build_all_debug_64b_minimal.bat b/tools/msvc2015_build_all_debug_64b_minimal.bat new file mode 100644 index 0000000..8688bbe --- /dev/null +++ b/tools/msvc2015_build_all_debug_64b_minimal.bat @@ -0,0 +1 @@ +CALL msvc2015_build_all_debug_64b_full.bat debug minimal diff --git a/tools/msvc2015_build_all_release_64b_full.bat b/tools/msvc2015_build_all_release_64b_full.bat new file mode 100644 index 0000000..f694f04 --- /dev/null +++ b/tools/msvc2015_build_all_release_64b_full.bat @@ -0,0 +1 @@ +CALL msvc2015_build_all_debug_64b_full.bat release full diff --git a/tools/msvc2015_build_all_release_64b_minimal.bat b/tools/msvc2015_build_all_release_64b_minimal.bat new file mode 100644 index 0000000..67e8c56 --- /dev/null +++ b/tools/msvc2015_build_all_release_64b_minimal.bat @@ -0,0 +1 @@ +CALL msvc2015_build_all_debug_64b_full.bat release minimal diff --git a/tools/msvc2017_build_all_debug_64b_full.bat b/tools/msvc2017_build_all_debug_64b_full.bat new file mode 100644 index 0000000..575fde1 --- /dev/null +++ b/tools/msvc2017_build_all_debug_64b_full.bat @@ -0,0 +1,26 @@ +REM -- BATCH DEFAULT PARAMETERS -- +set QT_DIR=%QT5_9_MSVC2015_64B% +set BOOST_ROOT=%BOOST_ROOT_1_66% +set MONGOC_LIB=%MONGOC_LIB_MSVC2017% +set BSON_LIB=%MONGOC_LIB% +set PATH=%QT_DIR%\bin;%BOOST_ROOT%\lib_shared;%MONGOC_LIB%;%PATH% +set MSVC_CONFIG=Debug +set CL=/MP +set BATCH_TYPE=full +set CMAKE_OPTIONS=-D_QX_UNITY_BUILD=1 + +REM -- CHECK BATCH COMMAND LINE OPTIONS -- +if "%1"=="release" set MSVC_CONFIG=Release +if "%2"=="minimal" set BATCH_TYPE=%2 +if "%BATCH_TYPE%"=="full" set CMAKE_OPTIONS=%CMAKE_OPTIONS% -D_QX_ENABLE_BOOST=1 -D_QX_ENABLE_QT_GUI=1 -D_QX_ENABLE_QT_NETWORK=1 -D_QX_ENABLE_MONGODB=1 + +REM -- BATCH EXECUTION DIRECTORY -- +cd "..\test\" +set QX_TEST_DIR="%cd%" +cd ".\build\" + +REM -- CALL CMAKE TO BUILD QXORM LIBRARY AND ALL SAMPLES PROJECTS -- +cmake -G"Visual Studio 15 2017 Win64" %QX_TEST_DIR% %CMAKE_OPTIONS% +cmake --build . --config %MSVC_CONFIG% + +pause diff --git a/tools/msvc2017_build_all_debug_64b_minimal.bat b/tools/msvc2017_build_all_debug_64b_minimal.bat new file mode 100644 index 0000000..d6ff3bc --- /dev/null +++ b/tools/msvc2017_build_all_debug_64b_minimal.bat @@ -0,0 +1 @@ +CALL msvc2017_build_all_debug_64b_full.bat debug minimal diff --git a/tools/msvc2017_build_all_release_64b_full.bat b/tools/msvc2017_build_all_release_64b_full.bat new file mode 100644 index 0000000..a651b3c --- /dev/null +++ b/tools/msvc2017_build_all_release_64b_full.bat @@ -0,0 +1 @@ +CALL msvc2017_build_all_debug_64b_full.bat release full diff --git a/tools/msvc2017_build_all_release_64b_minimal.bat b/tools/msvc2017_build_all_release_64b_minimal.bat new file mode 100644 index 0000000..1281e5b --- /dev/null +++ b/tools/msvc2017_build_all_release_64b_minimal.bat @@ -0,0 +1 @@ +CALL msvc2017_build_all_debug_64b_full.bat release minimal diff --git a/tools/msvc2019_build_all_debug_64b_full.bat b/tools/msvc2019_build_all_debug_64b_full.bat new file mode 100644 index 0000000..e93d8db --- /dev/null +++ b/tools/msvc2019_build_all_debug_64b_full.bat @@ -0,0 +1,26 @@ +REM -- BATCH DEFAULT PARAMETERS -- +set QT_DIR=%QT6_0_MSVC2019_64B% +set BOOST_ROOT=%BOOST_ROOT_1_66% +set MONGOC_LIB=%MONGOC_LIB_MSVC2017% +set BSON_LIB=%MONGOC_LIB% +set PATH=%QT_DIR%\bin;%BOOST_ROOT%\lib_shared;%MONGOC_LIB%;%PATH% +set MSVC_CONFIG=Debug +set CL=/MP +set BATCH_TYPE=full +set CMAKE_OPTIONS= + +REM -- CHECK BATCH COMMAND LINE OPTIONS -- +if "%1"=="release" set MSVC_CONFIG=Release +if "%2"=="minimal" set BATCH_TYPE=%2 +if "%BATCH_TYPE%"=="full" set CMAKE_OPTIONS=%CMAKE_OPTIONS% -D_QX_ENABLE_BOOST=1 -D_QX_ENABLE_QT_GUI=1 -D_QX_ENABLE_QT_NETWORK=1 -D_QX_ENABLE_MONGODB=1 -D_QX_UNITY_BUILD=1 + +REM -- BATCH EXECUTION DIRECTORY -- +cd "..\test\" +set QX_TEST_DIR="%cd%" +cd ".\build\" + +REM -- CALL CMAKE TO BUILD QXORM LIBRARY AND ALL SAMPLES PROJECTS -- +cmake -G"Visual Studio 16 2019" -A x64 %QX_TEST_DIR% %CMAKE_OPTIONS% +cmake --build . --config %MSVC_CONFIG% + +pause diff --git a/tools/msvc2019_build_all_debug_64b_minimal.bat b/tools/msvc2019_build_all_debug_64b_minimal.bat new file mode 100644 index 0000000..c339b8d --- /dev/null +++ b/tools/msvc2019_build_all_debug_64b_minimal.bat @@ -0,0 +1 @@ +CALL msvc2019_build_all_debug_64b_full.bat debug minimal diff --git a/tools/msvc2019_build_all_release_64b_full.bat b/tools/msvc2019_build_all_release_64b_full.bat new file mode 100644 index 0000000..5af958a --- /dev/null +++ b/tools/msvc2019_build_all_release_64b_full.bat @@ -0,0 +1 @@ +CALL msvc2019_build_all_debug_64b_full.bat release full diff --git a/tools/msvc2019_build_all_release_64b_minimal.bat b/tools/msvc2019_build_all_release_64b_minimal.bat new file mode 100644 index 0000000..29742ef --- /dev/null +++ b/tools/msvc2019_build_all_release_64b_minimal.bat @@ -0,0 +1 @@ +CALL msvc2019_build_all_debug_64b_full.bat release minimal diff --git a/tools/osx_build_all_debug_full.sh b/tools/osx_build_all_debug_full.sh new file mode 100644 index 0000000..4540117 --- /dev/null +++ b/tools/osx_build_all_debug_full.sh @@ -0,0 +1,179 @@ +#!/bin/bash + +if [[ $1 == "release" ]]; +then + CONFIG=release + LIBBOOSTSERIALIZATION=libboost_serialization-mt.dylib +else + CONFIG=debug + SUFFIX=d + LIBBOOSTSERIALIZATION=libboost_serialization-mt-d.dylib +fi + +if [[ $2 == "full" ]] || [ -z "$2" ]; +then + QMAKEPARAMS="\"DEFINES += _QX_ENABLE_BOOST_SERIALIZATION\" \"DEFINES += _QX_ENABLE_QT_GUI\" \"DEFINES += _QX_ENABLE_QT_NETWORK\"" +fi + +if [ ! -f ../lib/$LIBBOOSTSERIALIZATION ] +then + echo "You must copy '$LIBBOOSTSERIALIZATION' in 'QxOrm/lib/' before launching this script." + exit 0 +fi + +function testFile { +if [ ! $1 $2 ] +then + echo "$2 Error." + exit 1 +else + echo "$2 OK." +fi +} + +clear +pwd +cd .. + +echo "-- BOOST ENVIRONMENT VARIABLES --" +export BOOST_INCLUDE=/usr/include +export BOOST_LIB=/usr/lib +export BOOST_LIB_SERIALIZATION_DEBUG=boost_serialization-mt-d +export BOOST_LIB_SERIALIZATION_RELEASE=boost_serialization-mt +export BOOST_LIB_WIDE_SERIALIZATION_DEBUG=boost_wserialization-mt-d +export BOOST_LIB_WIDE_SERIALIZATION_RELEASE=boost_wserialization-mt + +echo "-- MAKE OPTIONS : USE 8 CORE CPU TO REDUCE BUILD TIMES --" +MAKEOPT=-j8 + +echo "-- BUILD QXORM LIBRARY --" +eval qmake $QMAKEPARAMS QxOrm.pro -r -spec macx-g++ +make -w $CONFIG $MAKEOPT +testFile -f ./lib/libQxOrm$SUFFIX.* +cp -R -v ./lib/libQxOrm$SUFFIX.* ./test/_bin/ +cp -R -v ./lib/$LIBBOOSTSERIALIZATION ./test/_bin/ +install_name_tool -id @executable_path/libQxOrm$SUFFIX.dylib ./test/_bin/libQxOrm$SUFFIX.dylib +install_name_tool -id @executable_path/$LIBBOOSTSERIALIZATION ./test/_bin/$LIBBOOSTSERIALIZATION +install_name_tool -change $LIBBOOSTSERIALIZATION @executable_path/$LIBBOOSTSERIALIZATION ./test/_bin/libQxOrm$SUFFIX.dylib + +cd ./test/ + +echo "-- BUILD DLLSAMPLE --" +cd ./qxDllSample/dll1/ +eval qmake $QMAKEPARAMS dll1.pro -r -spec macx-g++ +make -w $CONFIG $MAKEOPT +testFile -f ../../_bin/libdll1$SUFFIX.* +cd ../../ +install_name_tool -id @executable_path/libdll1$SUFFIX.dylib ./_bin/libdll1$SUFFIX.dylib +install_name_tool -change $LIBBOOSTSERIALIZATION @executable_path/$LIBBOOSTSERIALIZATION ./_bin/libdll1$SUFFIX.dylib +cd ./qxDllSample/dll2/ +eval qmake $QMAKEPARAMS dll2.pro -r -spec macx-g++ +make -w $CONFIG $MAKEOPT +testFile -f ../../_bin/libdll2$SUFFIX.* +cd ../../ +install_name_tool -id @executable_path/libdll2$SUFFIX.dylib ./_bin/libdll2$SUFFIX.dylib +install_name_tool -change $LIBBOOSTSERIALIZATION @executable_path/$LIBBOOSTSERIALIZATION ./_bin/libdll2$SUFFIX.dylib +#install_name_tool -change libdll1$SUFFIX.1.dylib @executable_path/libdll1$SUFFIX.dylib ./_bin/libdll2$SUFFIX.dylib +cd ./qxDllSample/exe/ +eval qmake $QMAKEPARAMS exe.pro -r -spec macx-g++ +make -w $CONFIG $MAKEOPT +testFile -f ../../_bin/exe$SUFFIX +cd ../../ +install_name_tool -change $LIBBOOSTSERIALIZATION @executable_path/$LIBBOOSTSERIALIZATION ./_bin/exe$SUFFIX + +echo "-- BUILD TEST QXBLOG --" +cd ./qxBlog/ +eval qmake $QMAKEPARAMS qxBlog.pro -r -spec macx-g++ +make -w $CONFIG $MAKEOPT +testFile -f ../_bin/qxBlog$SUFFIX +cd ../ +install_name_tool -change $LIBBOOSTSERIALIZATION @executable_path/$LIBBOOSTSERIALIZATION ./_bin/qxBlog$SUFFIX + +echo "-- BUILD TEST QXBLOG COMPOSITE KEY --" +cd ./qxBlogCompositeKey/ +eval qmake $QMAKEPARAMS qxBlog.pro -r -spec macx-g++ +make -w $CONFIG $MAKEOPT +testFile -f ../_bin/qxBlogCompositeKey$SUFFIX +cd ../ +install_name_tool -change $LIBBOOSTSERIALIZATION @executable_path/$LIBBOOSTSERIALIZATION ./_bin/qxBlogCompositeKey$SUFFIX + +echo "-- BUILD TEST QXBLOG MODEL VIEW --" +cd ./qxBlogModelView/ +eval qmake $QMAKEPARAMS qxBlog.pro -r -spec macx-g++ +make -w $CONFIG $MAKEOPT +testFile -f ../_bin/qxBlogModelView$SUFFIX +cd ../ +install_name_tool -change $LIBBOOSTSERIALIZATION @executable_path/$LIBBOOSTSERIALIZATION ./_bin/qxBlogModelView$SUFFIX + +echo "-- BUILD TEST QXBLOG PIMPL IDIOM --" +cd ./qxBlogPImpl/ +eval qmake $QMAKEPARAMS qxBlog.pro -r -spec macx-g++ +make -w $CONFIG $MAKEOPT +testFile -f ../_bin/qxBlogPImpl$SUFFIX +cd ../ +install_name_tool -change $LIBBOOSTSERIALIZATION @executable_path/$LIBBOOSTSERIALIZATION ./_bin/qxBlogPImpl$SUFFIX + +echo "-- BUILD TEST QXBLOG REST API AND HTTP SERVER --" +cd ./qxBlogRestApi/ +eval qmake $QMAKEPARAMS qxBlog.pro -r -spec macx-g++ +make -w $CONFIG $MAKEOPT +testFile -f ../_bin/qxBlogRestApi$SUFFIX +cd ../ +install_name_tool -change $LIBBOOSTSERIALIZATION @executable_path/$LIBBOOSTSERIALIZATION ./_bin/qxBlogRestApi$SUFFIX + +echo "-- CHECK BATCH TYPE : FULL OR MINIMAL --" +if [[ $2 == "minimal" ]]; +then + exit 0 +fi + +echo "-- BUILD TEST QXCLIENTSERVER QXSERVICE --" +cd ./qxClientServer/qxService +eval qmake $QMAKEPARAMS qxServiceServer.pro -r -spec macx-g++ +make -w $CONFIG $MAKEOPT +testFile -f ../../_bin/libqxServiceServer$SUFFIX.* +install_name_tool -id @executable_path/../Frameworks/libqxServiceServer$SUFFIX.dylib ../../_bin/libqxServiceServer$SUFFIX.dylib +install_name_tool -change $LIBBOOSTSERIALIZATION @executable_path/../Frameworks/$LIBBOOSTSERIALIZATION ../../_bin/libqxServiceServer$SUFFIX.dylib +install_name_tool -change @executable_path/libQxOrm$SUFFIX.dylib @executable_path/../Frameworks/libQxOrm$SUFFIX.dylib ../../_bin/libqxServiceServer$SUFFIX.dylib +eval qmake $QMAKEPARAMS qxServiceClient.pro -r -spec macx-g++ +make -w $CONFIG $MAKEOPT +testFile -f ../../_bin/libqxServiceClient$SUFFIX.* +install_name_tool -id @executable_path/../Frameworks/libqxServiceClient$SUFFIX.dylib ../../_bin/libqxServiceClient$SUFFIX.dylib +install_name_tool -change $LIBBOOSTSERIALIZATION @executable_path/../Frameworks/$LIBBOOSTSERIALIZATION ../../_bin/libqxServiceClient$SUFFIX.dylib +install_name_tool -change @executable_path/libQxOrm$SUFFIX.dylib @executable_path/../Frameworks/libQxOrm$SUFFIX.dylib ../../_bin/libqxServiceClient$SUFFIX.dylib +cd ../../ + +echo "-- BUILD TEST QXCLIENTSERVER QXSERVER --" +cd ./qxClientServer/qxServer/ +eval qmake $QMAKEPARAMS qxServer.pro -r -spec macx-g++ +make -w $CONFIG $MAKEOPT +testFile -d ../../_bin/qxServer$SUFFIX.app +cd ../../ +mkdir -p ./_bin/qxServer$SUFFIX.app/Contents/Frameworks/ +cp -R -v ./_bin/libQxOrm$SUFFIX.* ./_bin/qxServer$SUFFIX.app/Contents/Frameworks/ +install_name_tool -id @executable_path/../Frameworks/libQxOrm$SUFFIX.dylib ./_bin/qxServer$SUFFIX.app/Contents/Frameworks/libQxOrm$SUFFIX.dylib +install_name_tool -change @executable_path/$LIBBOOSTSERIALIZATION @executable_path/../Frameworks/$LIBBOOSTSERIALIZATION ./_bin/qxServer$SUFFIX.app/Contents/Frameworks/libQxOrm$SUFFIX.dylib +cp -R -v ./_bin/$LIBBOOSTSERIALIZATION ./_bin/qxServer$SUFFIX.app/Contents/Frameworks/ +install_name_tool -id @executable_path/../Frameworks/$LIBBOOSTSERIALIZATION ./_bin/qxServer$SUFFIX.app/Contents/Frameworks/$LIBBOOSTSERIALIZATION +mv -v ./_bin/libqxServiceServer$SUFFIX.* ./_bin/qxServer$SUFFIX.app/Contents/Frameworks/ +install_name_tool -change $LIBBOOSTSERIALIZATION @executable_path/../Frameworks/$LIBBOOSTSERIALIZATION ./_bin/qxServer$SUFFIX.app/Contents/MacOS/qxServer$SUFFIX +install_name_tool -change @executable_path/libQxOrm$SUFFIX.dylib @executable_path/../Frameworks/libQxOrm$SUFFIX.dylib ./_bin/qxServer$SUFFIX.app/Contents/MacOS/qxServer$SUFFIX + +echo "-- BUILD TEST QXCLIENTSERVER QXCLIENT --" +cd ./qxClientServer/qxClient/ +eval qmake $QMAKEPARAMS qxClient.pro -r -spec macx-g++ +make -w $CONFIG $MAKEOPT +testFile -d ../../_bin/qxClient$SUFFIX.app +cd ../../ +mkdir -p ./_bin/qxClient$SUFFIX.app/Contents/Frameworks/ +cp -R -v ./_bin/libQxOrm$SUFFIX.* ./_bin/qxClient$SUFFIX.app/Contents/Frameworks/ +install_name_tool -id @executable_path/../Frameworks/libQxOrm$SUFFIX.dylib ./_bin/qxClient$SUFFIX.app/Contents/Frameworks/libQxOrm$SUFFIX.dylib +install_name_tool -change @executable_path/$LIBBOOSTSERIALIZATION @executable_path/../Frameworks/$LIBBOOSTSERIALIZATION ./_bin/qxClient$SUFFIX.app/Contents/Frameworks/libQxOrm$SUFFIX.dylib +cp -R -v ./_bin/$LIBBOOSTSERIALIZATION ./_bin/qxClient$SUFFIX.app/Contents/Frameworks/ +install_name_tool -id @executable_path/../Frameworks/$LIBBOOSTSERIALIZATION ./_bin/qxClient$SUFFIX.app/Contents/Frameworks/$LIBBOOSTSERIALIZATION +mv -v ./_bin/libqxServiceClient$SUFFIX.* ./_bin/qxClient$SUFFIX.app/Contents/Frameworks/ +install_name_tool -change $LIBBOOSTSERIALIZATION @executable_path/../Frameworks/$LIBBOOSTSERIALIZATION ./_bin/qxClient$SUFFIX.app/Contents/MacOS/qxClient$SUFFIX +install_name_tool -change @executable_path/libQxOrm$SUFFIX.dylib @executable_path/../Frameworks/libQxOrm$SUFFIX.dylib ./_bin/qxClient$SUFFIX.app/Contents/MacOS/qxClient$SUFFIX +cd ../ + +echo "Success !" diff --git a/tools/osx_build_all_debug_minimal.sh b/tools/osx_build_all_debug_minimal.sh new file mode 100644 index 0000000..0dd1cc0 --- /dev/null +++ b/tools/osx_build_all_debug_minimal.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +./osx_build_all_debug_full.sh debug minimal diff --git a/tools/osx_build_all_release_full.sh b/tools/osx_build_all_release_full.sh new file mode 100644 index 0000000..b4fdb61 --- /dev/null +++ b/tools/osx_build_all_release_full.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +./osx_build_all_debug_full.sh release full diff --git a/tools/osx_build_all_release_minimal.sh b/tools/osx_build_all_release_minimal.sh new file mode 100644 index 0000000..d335cd6 --- /dev/null +++ b/tools/osx_build_all_release_minimal.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +./osx_build_all_debug_full.sh release minimal diff --git a/tools/qxorm.ebuild b/tools/qxorm.ebuild new file mode 100644 index 0000000..fe36310 --- /dev/null +++ b/tools/qxorm.ebuild @@ -0,0 +1,67 @@ +# Copyright 1999-2010 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v3 +# $Header: /var/cvsroot/gentoo-x86/dev-cpp/qxorm/qxorm-1.5.0-r1.ebuild,v 1.5.0-r1 2011/04/04 $ + +EAPI="2" + +inherit qt4-r2 + +DESCRIPTION="C++ Object Relational Mapping library" +HOMEPAGE="https://www.qxorm.com/" +SRC_URI="https://www.qxorm.com/version/QxOrm_${PV}.zip" +S=${WORKDIR}/QxOrm + +LICENSE="GPL-3" +SLOT="0" +KEYWORDS="amd64 x86" + +IUSE="gui" + +RDEPEND="x11-libs/qt-sql:4 + gui? ( x11-libs/qt-gui:4 )" + +DEPEND="${RDEPEND}" + +PACKNAME="QxOrm" + +PATCHES=( + "${FILESDIR}/${P}-20110413.patch" +) + +src_configure() { + local conf_add + conf_add="${conf_add} $(use_enable gui)" + + eqmake4 "${PACKNAME}.pro" \ + CONFIG+="${conf_add}" \ + || die "eqmake4 failed." +} + +src_compile() { + default + emake all || die "make all failed" +} + +src_install() { + emake install DESTDIR="${D}"|| die "make install failed" + # deploy headers + dodir /usr/include/${PACKNAME} || die "create include directory failed" + insinto /usr/include/${PACKNAME} + doins -r ${S}/include/* || die "headers copy failed" + doins -r ${S}/inl/* || die "inline headers copy failed" + find "${D}"/usr/include -type f -name "*.h" -exec sed -i 's,../inl/,,g' {} \; + find "${D}"/usr/include -type f -name "*.inl" -exec sed -i 's,../inl/,,g' {} \; + + # deploy library + insinto /usr/lib + #doins ${S}/lib/libQxOrm.a || die "copy library failed" + #doins ${S}/lib/libQxOrm.la || die "copy library failed" + exeinto /usr/lib + doexe ${S}/lib/libQxOrm.so.1.0.0 || die "copy library failed" + + # rename library + LIBDIR=`ls "${D}"/usr/|grep lib` + mv "${D}"/usr/${LIBDIR}/lib${PACKNAME}.so.1.0.0 "${D}"/usr/${LIBDIR}/lib${PACKNAME}-1.1.so.${PV} || die "rename library failed" + ln -sf lib${PACKNAME}-1.1.so.${PV} "${D}"/usr/${LIBDIR}/lib${PACKNAME}-1.1.so || die "create symbolic link failed" + ln -sf lib${PACKNAME}-1.1.so.${PV} "${D}"/usr/${LIBDIR}/lib${PACKNAME}.so || die "create symbolic link failed" +} diff --git a/tools/qxorm.spec b/tools/qxorm.spec new file mode 100644 index 0000000..69511c6 --- /dev/null +++ b/tools/qxorm.spec @@ -0,0 +1,84 @@ +%define version 1.5.0 + +Name: QxOrm +Version: %{version} +Release: 1%{?dist} +License: GPLv3+ +Group: System Environment/Libraries +Summary: C++ Object Relational Mapping (ORM) library +URL: https://www.qxorm.com/ + +Source0: QxOrm_%{version}.zip + +Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root +BuildRequires: boost-devel,qt-devel + +%description +QxOrm is a C++ library designed to provide Object Relational Mapping (ORM) feature to C++ users. +QxOrm is developed by XDL Team, a software development engineer since 2003. +QxOrm provides many functionalities starting from a simple C++ setting function by class : + +* persistence : communication with a lot of databases (with 1-1, 1-n, n-1 and n-n relationships) +* serialization : binary and xml format +* reflection (or introspection) : access to classes definitions, retrieve properties and call classes methods + +%package devel +Summary: Development tools for QxOrm +Group: Development/Libraries +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description devel +The QxOrm-devel package contains header files and documentation necessary +for developing programs using the QxOrm + +%package doc +Summary: Documentation for QxOrm +Group: Development/Libraries +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description doc +The QxOrm-devel package contains the Doxygen files for +QxOrm. + +%prep +%setup -q -n QxOrm + +%build + +# Otherwise qmake installs everything into $PREFIX/lib +sed QxOrm.usrlib.pro "s#/lib#/%{_lib}#" +qmake-qt4 PREFIX=$RPM_BUILD_ROOT%{_usr} QxOrm.usrlib.pro +make release %{?_smp_mflags} + +%install +rm -rf $RPM_BUILD_ROOT +make release-install + +# We move the 'inl' into the include directory +# otherwise the name would be '/usr/inl' +mv $RPM_BUILD_ROOT%{_usr}/inl $RPM_BUILD_ROOT%{_includedir} + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + +%files +%defattr(-,root,root) +%doc license.gpl3.txt +%{_libdir}/libQxOrm.so* + +%files devel +%defattr(-,root,root) +%{_includedir}/* + +%files doc +%defattr(-,root,root) +%doc doc + +%clean +rm -rf $RPM_BUILD_ROOT + +%changelog +* Wed Apr 3 2013 Erik Wasser 1.2.5-1 +- First version +

    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

  2. #5`eWSr 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 Ks8m45`+otdlTp|J literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_export_cpp_services_server_app.png b/doc/qxentityeditor/resource/qxee_export_cpp_services_server_app.png new file mode 100644 index 0000000000000000000000000000000000000000..5d620d4af8ae4856ad8bb9e325a61311f1f6451f GIT binary patch literal 15835 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 F_ydm4r-1+f literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_export_plugins.png b/doc/qxentityeditor/resource/qxee_export_plugins.png new file mode 100644 index 0000000000000000000000000000000000000000..359f4c6bfa2d4c80fe6d4ccf8a75bd167814fa7d GIT binary patch literal 26023 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 literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_export_source_control_settings.png b/doc/qxentityeditor/resource/qxee_export_source_control_settings.png new file mode 100644 index 0000000000000000000000000000000000000000..11f218d9572c7d3a27129a5d6d136a47f7dcf024 GIT binary patch literal 17269 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 r+x;R>h_or$1RBiB2s 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͙L*gJFnG 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 literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_export_xml_json.png b/doc/qxentityeditor/resource/qxee_export_xml_json.png new file mode 100644 index 0000000000000000000000000000000000000000..ab5e427aaa1bbf4ed77785b9dedefe91609cd1c6 GIT binary patch literal 7235 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 u|LDO&w&y8=``9VEfs-Q6964+M7|T!S;X%V1~n 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 literal 0 HcmV?d00001 diff --git a/doc/qxentityeditor/resource/qxee_import_mysql.png b/doc/qxentityeditor/resource/qxee_import_mysql.png new file mode 100644 index 0000000000000000000000000000000000000000..a21acba9ab40b6d211897f9c2d07adcde86a78e6 GIT binary patch literal 31954 zcma%jbwE^6x9Fmk+4 z|K|?HrD7Dr+}^^GS<-r}SP1tM*~4`zMCZ|E(!vqcbAT&7cJFeHk4t@9#YPKwSda`I z)^R& z^z56hJu!6Pp2uOOF>E_DFTMpteuLcossVmYtUVq70$KlXb% zUsMA+_JBvzf2=fI-EgEmmqL{kxLj*1;LfMeBrZA{1x-B^GB@|eZ6`f_Pto1Q)70(v z@XdHp<5J$y`ZfB5uxk*wzDADZ-e576NV(+{={wjNQuSt`)aLSJGJN6E1}1ViAL~!C zXm~Th(Rf;zn|%u5`?4h@H4sAuv;?i5A1w3DIjBYsPfqfSidyrk1@I%C8;_{Um!9Lb zGnNzUrGl6(*C!#tk+VZUEMM!+*C0;daRo;6UtJ}HF zB*s7=CEr})(75lL!A{M9u9s95MND|ry7P-0(MQ$o9uztS0dGsY3Zyh&iYK2BwdV$g z(;3ms+V&kL7vD?CiJx$ca}n7=LZ$)JO{FbY!?p8)29oCb&E#%ni^&>hoPOd z=XRU&g!7#AI0c!{^IyXR8;>k(V$C77tt8l!u9}mVO?+mJsHU~q7F&i2%2XcU*&Fpx8(=O@AUPv-(5yuQ6(uf}I-I|zxLUYwzyGgNq>{=eZeI8EO zb-a@`F4TB9KB2!LB@Aq#ngRz$M~}1m1=82k1)9J3-bFKr;Co{pPx;4|uXE_eG?eI$ zh9y5K(5bZ>YtQZS$0t{4Jo}FDFxpu~3=nv%Zw^z-lya4L7~^&ZCuif-r~g|B-iN_L(06pJ#j;r~1=BQ&T5#Eo1iqS5f}TtgifKVt zNi_?!*oTiud;0W)`}h_Nqu$OQkP1B4X)}9)FT@oo!hpyk+dJPOFPsSRA!J-9=8^yA zc~CpiH-8-^SycUWo|U-z@b~Af@#6Z^ zfU&Xg8Be=yF?3rUPMo#teo->~#dY}8Vnp7BnD7muP4fA=GYp+w?S6^e1zYPw%FWw< zD3j;1v`L`SZEa1x*Qm)M>;OHv@HZR99+>P zU85AQc3$va{=kjm8sW+w@NihqF48Y;=-yHFdh*@#je-nsGGziGhp5WQteHqM*BxMX zx{U#E#6+Cp4fv4Wg=A6am(|GpqL@-scDV#G*TbuA^NB(o*Y?;W^6`QgznkOfEtO$= z6ThF=zZ`9;j|6}o*desy*(`*Uh!RF<$}n`H8}7lW}ihK_LW*vciTN!Y~b3iQuND8_rBo^&uh+|t31`HHHAHSjui=d zh}9=c$6Y`5q@$I5$Kd4^#|4qoA|aC!kOhg}f)$wrJ~)-A*s!J9C!0l#lP03%jVcYQ zVQ?yGO&+M*f-D4zV)aJMmI+^|#r2-`jeBh_=3?Xvt}`(}p0Qgf^|yM^_R~*jVUQ=6cj!%Df zJdvl#%e8)zB={_-;Bx=lBvFRZ!xh~6Gh6vW{B=pgU zdTFt2L~vq)H`BbEyfL?i)$(!Tb(k~-Vp zvfNdRSG^(R@tU$^6gL>_5Q9xA4UCIA8aRuo8l*<9&_@9ixw5mA@HVBoOoHGk`PR>D z`u(PDVG}>W(iuxpfWJazQ1*7-`k)u92yKV&NGh@-TnK2InN(_?obTmg>- z;S8|2_>bC~D_k^#n?DOwgbrf4I^+CWCvxE>>^Tf!A4m^y7YA$e@lPSd3h0jq7Bme{ zyY(ESJbcF)=FL%pjC_5&i~X;C&5{$sha(fs_|CKyZB)n$nP6rKSq)a**HB{n{>27~ zi=_g4`Z~q4O12(VcEci-#%prIL02I{RsvTVJrN17qze)Prp6=p7EYVhNyhl;d>k$$ zSorODZ^Jn*82ADmiJEw2h9rEzj@$BnjA&?#`#`DIShuf#SElhDtblPf5UCzp z{a__0g!TLS^+Bdr`mKA8zRYbIx8|Ysy!Fd}SIK3caPy%47>Z{7qRLaKRwP3m5jYJb z$+9_XVn4?lySH7KKw(P#rmQ21VJmAaZON6}0gP|8;Qt2|!Y+Hvbg^Zu$+)_1&_=hp ziEJw~ih@R78j^z>l8^k{M0f`DeTudF1n53G;b6YP52ZdkZX17KDvuZC(*9zm^-^Qy~fs4$_405C9g+FTrhE6zG^a5;7B<9DIgg5k&VVZlXmSI)_P@(-hB%Wj-L zor+yy$g0I+I=m#xCRSw&+LA2MqAo>*W~bIkdbrVzJGs?l*1aJYv|qb(qK%^$bM#_I z)VOEB{U>*i-~*}=tp0{l;&w)-vG{im!3w^FE1AaWl5>05j&gOQM)qd3i-B3JiQ?c1@f- zstYBj^?XVtTMi}mx%5?}=MqRsu0mii6JK;c>QX6z?(CgWU~hrql>eoU8R~+y zzhsqGe&B7_fT%uE(i~Zaz^T~Yqt}YCnuOkUTyBs-|C+^}C!mM6n^J!R=}h1MOOX9< zF8{v(4+!lKNZ;~a90Mql^IB`Z))n>lhG4n`#%Z^e)j150l1+(lPHrDn80t1j$NP{;b>_wAbaCP}y zM=gFaN%3@$Bk|8Rt(GHk;%2Qh+tLCSXI<)PStnqif2jgx1NA|k8Fh_ z0|GyI-%TAR0lhRCe#FIgoah)&7w3$TmIdy|Q?^{D@ zV&KBb4}mCX(Gu%uS@D7JtPbkpkB0fihkVR;>bzDnJ=Wjd!eJs>1~sd$f(iCVom**q zhs=IF&f)gRZ(8AOtGS>1 zH^L$jpOunhsqYi!Qdn${VA~n%B8omZ|K#;|483YM`dprQM!ucw#GCt$23HsFW|6XX zw+#!2Ej8J*F)3pBZKxjiK)VfGgPgzGtL|RhQl^7q7tCr`v@>f*;lA^WQGHiZk&cE5 zv4R)WOQ?PEM7Qt8HT7`o@SOR{wBoNdrDgF>-E4WVLqTI-^oT}a16KEePPOSsN-@#9 z`Kwj)D(6A-Voe4(PQlkvMy~0ds**|FZt;i(++M^%L^vH}F;@%03mu|tX{W%{Lk4tun zl(~$gg#)M0cu*RW*4-$NI2)9pOxikUb%$(d}KgKP_UK}{JSG9tiE(g zAZnceDI`5L8@!oisH^L59urRsYmM*8wa2YWo5x8AP)fvWy# zrhcaT1f+8zTux;HvdlyjI=18CTCGyNXPiZ)#6egVyMhsG(~g|4F9LH7`f1)^P$aLa zTy6aM6g~;5vg|ND(>jGDQQqacdN2!wsXxH+N zV7=?t8_}P`V!tgXBFTVxg}pT;9w@nbxKsRy)8xg#ae<1t;L%r^g!9#Sv5Y(YEs2u! zd*Nj=C2ccmf5aP;E)?4sl1mcA8eUD!W3BCt!R0}^KBM7qeI*-Wyu**uVllZm;>zRG z^<7iUq#hn@WGmU;pIMIcXdXhQRzv9^Nd6h9fQ9+-jCEOib@t0G9rIug&~61<09Q65A|-|iVzLN9vWvh zGkPFq)uIQJ@ksDZ!Rud%D5zI!O8MSS^B{;U+Zd-6C$`U72~BvqB>C88*eH+J%u08p zH*1}fenQ8Q5DU>pM1+cU=p}jW`Jw?!D+f_k)u~f$`7)=p#{hA$T7t4xXuKr0^6=-_ zxW+;~4YPhmB9`xLmICzIM-}lM#(NrF?9mc5c;l2sLVqX~O6n7&tEgwDCQM25_g=b_ z8P7@(2`QOT*O*RV zuEk$DeAf8Vbr8)-$UekDv~_l99M&U-oxQKYmZ8wFQVYqPP-9B4A1`M4(t-%nlR8`xA z*gL2f8#PBZ?v66a7wT^E=@I^rkM1J(6^;N@j7_+*jbd>%b8~&du7JpS%@X(FMmcE` z#LA9#Ja&JD^YT3cJ>bCv`7zl}RpfVIPlz8{qkVXvX_0u5V|AEG%FAhOA*Wa>0;UJt zN+LDs3(x4|vN3Ck;H?10Z)_2wXZgo*Rm6;**gq1CY~wVq{UIv1>v18@EGzn%7+nr= z1tso8a(^$M1Y5cg)@^2IRmDeOu!i2)PQ$*v&rNjxvv=l40&b;`vQ`8RqDvnwf{%Dk zaNgO8os{|YU485QCYp1GC9tXzHBw7Iks&+^w|8=u|1!=XlAcb?K1BIREdbSNy_h?6 zIN;iLWVH`s$3H!Tm}h!?Gi)ZFYH4^BBOQRleIgJNaQ8tqmPu4%4>^VqoEZ*X6%6In z|D~Pa?PhAf?WWmQwvJ?v8-;;X6qW_R5?2X#Jv^8y<&_n$-lwAP-cfCyAAmI&_+R2Z zxxjBO`S37>oa2PY%{%%1ig24G>MeCsLd2o;Mte)_YODyaAzLQulYRF9(u8RaGl4^B zhJEVnyX78+*{=@`jb+m(&b%^*htsgTSf<12tYkx#s@Eh{X`a8pFj(0L{@$*NPJEg2 zkyW5!^t-i-&%P(c-`0_tH8jyg|i2c%C^V#iHB%{ozwu3GLG}(|DhN9i&Mx z^|(m6G>rm;x-`Q&!9XZ(mc)r}i}!y0Py$_@-Z&&x#JG5S?!o$CS*<~|uvOk(CV>sT z_|BTU_=op%U)92yqZ?jp4TpknF2BW3trFUWto99LRx)jMEv#2TvIP+wqP%$s;%0=V z);+RR(zP3mkUW@28!Lv^Ph+ypS3PKf|f#2;=EXw9}PmrQDdCGKmh< zljvJB9yjJzg0Fk7aWE&;TguimZmHmYRgQCW6Q5DH{ z*gC5O9U?KCC>m|bM3sC}sjRH&K*yC+6&S>s?v!hl70%h6cd(UE-!SP|(jfy*LXe}b zgJw%Bc-7&d$d8cx{&~A%2f@N=zN7T-3~6$W@L>{uWmbbbpUz3oUp?|_e8c&Ao%H;;5E<>v$HfBET%i-hhew0Xh z#^;oi+ALan`n7Rq5Tl2yS^PKah1^)mYR!B`qlMbtE6jQ+T7@;Q1cFyO3l-(arqrL6 zo0EQ&Y$B#rzTM7rXd7hB3{C9IzA@eep)p3NGo4KIA#Ylj(63(tD?tN-EqU^GZCbkX z6^Q|>?u?FeA{dhl>ozXeI-~iNuGb7zxleu-&yJQo=ZJNAkdj{N5fbH%uGBASyRX?Z z5uwE$(}Dpi=`#59%0{whGnN3A0Xbdx6^9?%>vormvt14SLiE^?=)lz>x)(z0)|I@e zdUu*_h>CPlPCQzmPoJ#eYHaK`9+G+}OVGlBm5X1cTKcO0jj7zqxVBzm+3c*$R9(gc z2Ex%=4tcj;&BbYHi2gi=J;R*q(MK_6vIf0T^De8k*|4x0L-SzA$+_v9@cRn>PoA71 zlG-t)fIXk7(D>Ebvr0OH^TG zbtj)PU&7_#dEohs2cAbv&o$0sAVv^Ep~d z5e$_!s;apWUMNf|fWC#+R(rip|ikzg?#~dT{+S!tV~PXdu<`z*`2cR77}W2zARN ziKobjuwl=FJO|nkhMfpl zqdBX#->F$R<^-8ZS@X?a{%u7! zGH&bKat#lYJP>)*-c>yN;PT<`Z^RbcEEkpR%i04{XDMH-IK+VJj@DKgywLQO;J_@7 zfVB7n=~|v$B4&s`WxWhzYkbeClT*gn*`F<&BPvc+!X5MG-&FmK>ze6TkzCTX(jJ<2 z7JDrH@2fSP!sks&mmHYZf2I4%VL5%#T5!K|&gaK*h)Q{u4mt=V7)JIb;?$${va5ux?m=PS&40Oj-bA(`MsUfJ?@uNTxtnu)4qx= z7F^4LVsTY#@Vxc8+lG*Ma}WC|^C&#QVs+1lq~@KutI(rKdLgByzOCJ#m$GFXUu!VG zrawb?6Q!&^=q(tVCAcx(X_bBxvG+^`_;bvdyUx4J&+HtjkY|js;f>m zgAU1w@jZNFvSsHF-dvFQ)U%y@f1H}Vxs9MSR@**(GiC$3zx5poGi$XMTqM`!+J%~> z-*N60u(-Hm>XB%LK3?s60plAGXl`PA>O^Z@9jFVgC)#6gzT20mDBF&e82xczmgnx4 zREK*$+Hdh~(Yt%q24C|v&&ya`>!a-pFXtX!I)5k>H*4XIbSrlgvd@R!WB1+r&YI78 z=hKCEP{vJT3)@PB+_!=4cBwqekOr-SwgOqFDLqAMJ_ZoG|4wP>4^%1_D&E7%;MFkI zgTq*@c76e{_yZ@r2UMFCoh)C8mHV#75Lr$ZD_V`d3KuDdeCRgVyZB*Y5pF!S@wV?( z^V74cDfq5a+0QsW%#ZZ$A|CSR)PT@-rgudU4C?}kzwnX!iDgPl!J>_xWN?RXq8`c% z+WS%X=qL|{0?#_qtzW7V39=I3zV`z8@sH-o9Rt zO0im43~yBQSrqRI4B_4)E}hSztN1XGWw{nlXsPi4L+cmu9M-(D3XoJ#x%W(q(;_sE zSxI{(#OIm}KJ`;;Kzpdso0_|^94hgHFtl;!XW7Gu1IBz|*!nV4?I8Ed^t76Ivuv)(@ulK1lpJuzQq zfl*Uo2;SOFmgyll$;KK8i=Rf{EigqHtml5dBiqnsU&K|j+R67xGli)(7xqD{Kae^u z?6QM&XE_f2%|o}kEwos+ADhXIpUp){z>uFarnhe(XOQXMu45)LO%Eu|i)jt`cajtJ^{t7JxC#WqIlrnP1qKh_M^?)i1SqcD z6TcR`&XvSoyIWg0#pNksve3YyoINSQ{K~6Umpt2sfMm%eVo)%7iOR~d#VHF6X0Qpm6`QmNK_rQ{XS~>;GCL! zEJH8+Q^EeEYviz!)DG;kJ)naK)tlin_-(wV0WDzJP z63`0~(Ljz4HDPlM!YRK}9mTFXvj2wlkZmhATRgQp!~HyH|9QnrBju=+EeZ;DtelIh zX9m+el^4n#WSLZBsy1lO*W4&$X`FWxTHO&EeLVc&hDwEHF!tNk@x6K4#-|Fpm|VM9 zL=lV8xInhMepW0@uFSNgt3lHc7NcXVs{AIxK_@HA#v*zwwp?1i^QgLH^;ZJ>sA$SU z-oAC@j$JeK*;F!?<9D{J3;P)wAYvXr*tM(eIg@0sHS#%ra*RVSrFQlA-i1W9yTzl-j_bRG+8l7ddhf9MV9-44M~o1Y&`SPtypC}sFUBYKN3{}u9EXja z!_JpoQad(7E(f*6-42v5iC&}g2XbAXbc_t-eI728@R%QD!z__#J8$-5$YZUxY~Q0^ z^7@%fs?x$nYeza^OV&-4lDNrpC<1ci`q9rFz>V>8@*B)txfxhu6k^t^xQ}X|nel*6 zE!W;#eCS`mjha1(e4ejdv~c`~?VWkqZz_oEc9NNTW_%LdSMd=h$SX7RzI_ULf{N+o zGlZ~x3Px{qJ2wtcg{Y9dJpeqw;V45d(*SDVzneX-%>V zVW``se=W|ZEL))G|70@$w?&qP77Ve*-vl`gWw0Qsg_f`0UKjUdN*^PBE##`Y&Q{n8=L0Oz{9RQ#Zi<%i~149lE6SvXe;mnEUd&$nC+Jk@^j+bqR z89*p|Po$7=wi-g?an%ncc5;v6U2yNgH~%C!yM{yHx!W&^df&X-L_JQuGXuL+}2ef%xrK1l1_IG8%@^ZQ`F z_5R6UM#HH@%n|((*0gx3cb{3=#}ay>$Q#5SuSHJuf=wl~&$ANF!;6JU&Y~oaIbU!{)2!kh>fND zl?7sLK#-Pk&Q+0?9dqhCLTo+MS}mYXY<8uQ?_i0}-ZS|dv?on{oQbluN;EW;u=C@Z zWii*QLf^#le-^lO2>(mIc}Bw3!p3IUa^&m$LR>+(Twa;+Z%ghWF-ZBhS(2A`K&%+? zkIovb=bsnXZB9W)&2ad{_WP=lt{MoGer6-A`RB#dUgt%&n`M-}wj|41qTA3~V$$c) ze*H8C0TL#$$PLPHLLWBw!|Ok_?N16DzPB>~GZ_;JC2+8m^>A~LGx;pSEF!A3@Z|&j zh!$AW`E8K;S!FvWn2kk5Wr6|Ur@k;)Ay)ay(HOIs<`wo5P=RXQV6PMuogFTA`jRR1 zK7KUo>nSzq#u2r$+9zbi@wWo)5^YdZ7{hR=tMlxILOqlEUQ2xG8=Lkfp-r?Ev>b8y zgm9qa;av5fU3g1~zjyv%5J#2wydSE&wwl%S==AwpGs^1Pjwb?ZA*s!U{!zbUW{E8< zrE_qR#dK-4W9epoZK}C0Ky0C_{zP!tja(`XYNpF(nJ1fbGXRxPZr1)ahR4fdRMUB5 z`T%5G3(#JX;A207AE{yjsg)ZMgyO*XYa)ci13_^)GxWpe?_hyR6e6=$v<2yZbDF{o znsIkcs?^Kb5^5(@WN_AOGbx9Z-O52;Jw8|dCAjZkhUCsaId5LkKeM&b*6n_p0@Oo+ zf{eM}IJI&=fGbvSSo*!JH#8)pD)_o4xAMb*WIl?7_I)6(uwC!8N;0qgYniYcKYv4zuvSij{KX=85Fg_A>&@Z%SIa$(N0CKejOdYeW*a0=| z$@wEH!nxw;?=tt`Gj8*V)r3U11bJDa+k$cm--7!HYhMV^UneUD>efI4t(Y#J0~rW0 z!2Xi*(x~!#qR97V>m2j7lW}NM1}rYLp4?h$wC7=vjeBcB^bE(e5~B<5qp!$GMYuPJ;Jxp_dMBJzY$ZdRQc;~v{LdGP|ec_VOF~%Z?<^PeIbb8 zNe{vxQH0|<=!T7R@xAqjPM^wvol*N>ffc~4;bngf@F&Kr6S z=TC&xaK$n()gFswe9H^~T$?h#_=G#Esrv?9ZTH#DZEJ!i;W8Qa#(1L(pPZ3Hzu_>} zreO0bWW0>I^gzyjWD^G_snOFuF$pk3{~U5UqIcSl$X5{fVgh>7bzzM2rnu}Dq* zTJ$7XlX3_C@>|8Up#5pWR4VO1htIALn``&SKU|1CVu5Xku7Y89?E$1Lnnk+TTo1^n zX&!pJZI4#+nED3%)g2gojk}BmvnP>mx*E1$ooU=`aRi3h<6I{P!j4~PssRJ*=1{D< zISR}k2*e}59)e|WYu*Kx1qg&c+AiU4*Xk3v&YpCgp3Tv4xyyw(!^u9qgk(3Kujaxo z_VS}Sl2P?UrJF6Vv#!lTzIZBT!~gB0dbM4_z=~zXw?ZI)V~ z$}d4pN`dyG90BqHSR-yf`DSLOLKU33b55ExJ|6#K;$mnPwDzAfM!I={n-xxy-mfYc zj$H#ThvWhR>eM@|AO!Z2YnA0x8IQ&IsP*KlD;C5#T*~=6dchoazz5sC2tihOZXzv; zuKd;WHT?0i%rhe{T!1|O=FscN%#)nsb8(N3=b_R@03G2TkH*{tQ2VPaIR2m)&H5Nw zB`9d3e=-qzufem4#P6MP*n{-?(XDe^S;RC$bdG`3FS=z;@_L4}57+KNbaSt#GrDo6 zp`-Y~EPz<=^h9$Y4!c<<3c=M@GgXQ#Gu1XXvB_6c@z!;@RaH*XcRu-YjnB*)1yLSJ z4gVRJkWG)1CHlYl3J&GxLvSv3a+7$G?-Yvu$*PUH>@T$NIXk%qfVcN&I43Vpr_`{0 zYb?LG9$6Rkl+SvqOj2U~WP2j;{!7F|HX@vaLP`uB4{jv^CB6cmZ$?)2Tk;>*Kfx)C$h>#$h9$R^j5;;yi3E_Waky0vXAEoA z>!uA4BGAKxbo5Jgq$|Bnd8I47 zva-_D;m|%jFI2VJK#~-V$7sQwAEKD#o?1xJ^HGkQr9@L~ zkbyS1+IkE>wtc=q8 z-mka9CFWaj`#;H;&dCVQ4|tr!SAkz3B?`c_u3?LH`pI>t6#0ioAvE|AzvK z?7dh2DNuto?duLJIqt^=uK1T--x&d%Yh40)lf(^bYE7SdsW^PoTLQU3D9biUuogJE za}a0cm3`F&Ma+0>|I~bcutQz?n?jr_JRr{(`!)QNYmDa!$TT#6!4ymY2P@no{ zkDHBA`DeUb6AXohC#l0rQ|Z{w4u8elUEI5Mc{#K8DL{^AB;ANvc3XM#;43=#9WD6x zbY0ye^Mbl!HnK_czq}spSF=`*4&4cx#dMEc01ClF-%k3c@11kU4i=+4D+7D1t=v=K z+1l@CXbS;GMR_N|$wW2yd)arRbd5PiS0z=&;HvFUiO9!R#MrqtX8Dr|zh;23m5lbz z_~Is(POec~ti-_n@YL`O4L=SC8wRo6Bh|lhl*p-cJH8+4!c00q{WLUR=AD$z-ckv; zd`W&64e|yb6?V_9=o_V<$O~=1m-t5Jo4-K(=tkaQ?`MyXi5OI^qDt17%1q$HdzXXh z=392XK`67cSJd+|>+lcx53bnUOdV`ltNsP3;b|Bl`e!YqGv+VnnWp0q2l9^N8(OBe z89;+>+BZEf^)_x_Ta8=et_PC2@p{my!q zCaiVy`Ff^L(_chVvJpuqo*#0T8E4C9Ov$#uN)yhsLs`gY)SFhKr%?Ba%KZM6IUlW5 zfaIfQGD9$HH6Zb0O;UlNKc1)Mj%1ozi1*d94^J01{#N&{$b46=wL_7s8i;(eYH4