LCOV - code coverage report
Current view: top level - libgnucash/engine - gnc-option-impl.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 56.6 % 555 314
Test Date: 2026-11-02 18:19:43 Functions: 38.8 % 139 54
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /********************************************************************\
       2              :  * gnc-option-impl.cpp -- Application options system                     *
       3              :  * Copyright (C) 2019 John Ralls <jralls@ceridwen.us>               *
       4              :  *                                                                  *
       5              :  * This program is free software; you can redistribute it and/or    *
       6              :  * modify it under the terms of the GNU General Public License as   *
       7              :  * published by the Free Software Foundation; either version 2 of   *
       8              :  * the License, or (at your option) any later version.              *
       9              :  *                                                                  *
      10              :  * This program is distributed in the hope that it will be useful,  *
      11              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
      12              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
      13              :  * GNU General Public License for more details.                     *
      14              :  *                                                                  *
      15              :  * You should have received a copy of the GNU General Public License*
      16              :  * along with this program; if not, contact:                        *
      17              :  *                                                                  *
      18              :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      19              :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      20              :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      21              :  *                                                                  *
      22              : \********************************************************************/
      23              : 
      24              : #include "gnc-option-impl.hpp"
      25              : #include "gnc-datetime.hpp"
      26              : #include "gnc-option-uitype.hpp"
      27              : #include "guid.hpp"
      28              : #include <cassert>
      29              : #include <sstream>
      30              : #include <numeric>
      31              : 
      32              : #include "gnc-accounting-period.h"
      33              : #include "gnc-session.h"
      34              : #include "gncOwner.h"
      35              : 
      36              : static const QofLogModule log_module{"gnc.options"};
      37              : 
      38              : const std::string GncOptionMultichoiceValue::c_empty_string{""};
      39              : const std::string GncOptionMultichoiceValue::c_list_string{"multiple values"};
      40              : 
      41              : static inline GncOwnerType
      42            0 : ui_type_to_owner_type(GncOptionUIType ui_type)
      43              : {
      44            0 :     if (ui_type == GncOptionUIType::CUSTOMER)
      45            0 :         return GNC_OWNER_CUSTOMER;
      46            0 :     if (ui_type == GncOptionUIType::VENDOR)
      47            0 :         return GNC_OWNER_VENDOR;
      48            0 :     if (ui_type == GncOptionUIType::EMPLOYEE)
      49            0 :         return GNC_OWNER_EMPLOYEE;
      50            0 :     if (ui_type == GncOptionUIType::JOB)
      51            0 :         return GNC_OWNER_JOB;
      52            0 :     return GNC_OWNER_NONE;
      53              : }
      54              : 
      55              : static GncOwner*
      56           63 : make_owner_ptr(const GncOwner* owner)
      57              : {
      58           63 :     if (!owner)
      59           60 :         return nullptr;
      60            3 :     auto rv{gncOwnerNew()};
      61            3 :     gncOwnerCopy(owner, rv);
      62            3 :     return rv;
      63              : }
      64              : 
      65           15 : GncOptionGncOwnerValue::GncOptionGncOwnerValue(
      66              :     const char* section, const char* name,
      67              :     const char* key, const char* doc_string,
      68           15 :     const GncOwner* value, GncOptionUIType ui_type) :
      69            0 :     OptionClassifier{section, name, key, doc_string},
      70           15 :     m_ui_type(ui_type), m_value{make_owner_ptr(value)},
      71          120 :     m_default_value{make_owner_ptr(value)} {}
      72              : 
      73           15 : GncOptionGncOwnerValue::GncOptionGncOwnerValue(const GncOptionGncOwnerValue& from) :
      74           45 :     OptionClassifier{from.m_section, from.m_name, from.m_sort_tag,
      75           15 :                      from.m_doc_string},
      76           15 :     m_ui_type(from.get_ui_type()), m_value{make_owner_ptr(from.get_value())},
      77           30 :     m_default_value{make_owner_ptr(from.get_default_value())} {}
      78              : 
      79              : void
      80            3 : GncOptionGncOwnerValue::set_value(const GncOwner* new_value)
      81              : {
      82            3 :     m_value.reset(make_owner_ptr(new_value));
      83            3 :     m_dirty = true;
      84            3 : }
      85              : 
      86              : void
      87            0 : GncOptionGncOwnerValue::set_default_value(const GncOwner *new_value)
      88              : {
      89            0 :     m_value.reset(make_owner_ptr(new_value));
      90            0 :     m_default_value.reset(make_owner_ptr(new_value));
      91            0 : }
      92              : 
      93              : const GncOwner*
      94           52 : GncOptionGncOwnerValue::get_value() const
      95              : {
      96           52 :     return m_value.get();
      97              : }
      98              : 
      99              : const GncOwner*
     100           17 : GncOptionGncOwnerValue::get_default_value() const
     101              : {
     102           17 :     return m_default_value.get();
     103              : }
     104              : 
     105              : void
     106            0 : GncOptionGncOwnerValue::reset_default_value()
     107              : {
     108            0 :     gncOwnerCopy(m_default_value.get(), m_value.get());
     109            0 : }
     110              : 
     111              : bool
     112            0 : GncOptionGncOwnerValue::is_changed() const noexcept
     113              : {
     114            0 :     return gncOwnerEqual(m_value.get(), m_default_value.get());
     115              : }
     116              : 
     117              : bool
     118            0 : GncOptionGncOwnerValue::deserialize(const std::string& str) noexcept
     119              : {
     120              :     try {
     121            0 :         auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
     122            0 :         auto inst = qof_instance_from_guid(&guid, m_ui_type);
     123            0 :         if (inst)
     124              :         {
     125            0 :             GncOwner owner{};
     126            0 :             owner.type = ui_type_to_owner_type(m_ui_type);
     127            0 :             owner.owner.undefined = inst;
     128            0 :             set_default_value(&owner);
     129            0 :             return true;
     130              :         }
     131              :     }
     132            0 :     catch (const gnc::guid_syntax_exception& err)
     133              :     {
     134            0 :         PWARN("Failed to convert %s to a GUID", str.c_str());
     135            0 :     }
     136            0 :     return false;
     137              : }
     138              : 
     139              : std::string
     140            0 : GncOptionGncOwnerValue::serialize() const noexcept
     141              : {
     142              : 
     143            0 :     auto owner{m_value.get()};
     144            0 :     gnc::GUID guid{*qof_instance_get_guid(static_cast<QofInstance*>(owner->owner.undefined))};
     145            0 :     std::string retval{guid.to_string()};
     146              : 
     147            0 :     return retval;
     148              : }
     149              : 
     150              : static GncItem
     151          109 : make_gnc_item(const QofInstance* inst)
     152              : {
     153          109 :     if (!inst)
     154           64 :         return std::make_pair<QofIdTypeConst, GncGUID>("", guid_new_return());
     155           45 :     auto type{qof_collection_get_type(qof_instance_get_collection(inst))};
     156           45 :     auto guid{qof_instance_get_guid(inst)};
     157           90 :     return std::make_pair(std::move(type), std::move(*const_cast<GncGUID*>(guid)));
     158              : }
     159              : 
     160              : static inline QofBook*
     161          422 : get_current_book(void)
     162              : {
     163          422 :     return qof_session_get_book(gnc_get_current_session());
     164              : }
     165              : 
     166              : static inline Account*
     167           27 : get_current_root_account(void)
     168              : {
     169           27 :     return gnc_book_get_root_account(get_current_book());
     170              : }
     171              : 
     172              : static const QofInstance*
     173          130 : qof_instance_from_gnc_item(const GncItem& item)
     174              : {
     175          130 :     auto [type, guid] = item;
     176          130 :     auto book{get_current_book()};
     177          130 :     auto coll{qof_book_get_collection(book, type)};
     178          260 :     return static_cast<QofInstance*>(qof_collection_lookup_entity(coll, &guid));
     179              : }
     180              : 
     181           39 : GncOptionQofInstanceValue::GncOptionQofInstanceValue(
     182              :     const char* section, const char* name,
     183              :     const char* key, const char* doc_string,
     184           39 :     const QofInstance* value, GncOptionUIType ui_type) :
     185            0 :     OptionClassifier{section, name, key, doc_string},
     186           39 :     m_ui_type(ui_type), m_value{},
     187          312 :     m_default_value{} {
     188           39 :     m_value = make_gnc_item(value);
     189           39 :     m_default_value = make_gnc_item(value);
     190           39 : }
     191              : 
     192           39 : GncOptionQofInstanceValue::GncOptionQofInstanceValue(const GncOptionQofInstanceValue& from) :
     193          117 :     OptionClassifier{from.m_section, from.m_name, from.m_sort_tag,
     194           39 :                      from.m_doc_string},
     195           39 :     m_ui_type(from.get_ui_type()), m_value{from.get_item()},
     196           78 :     m_default_value{from.get_default_item()}
     197              : {
     198           39 : }
     199              : void
     200           30 : GncOptionQofInstanceValue::set_value(const QofInstance* new_value)
     201              : {
     202           30 :     m_value = make_gnc_item(new_value);
     203           30 :     m_dirty = true;
     204           30 : }
     205              : 
     206              : void
     207            1 : GncOptionQofInstanceValue::set_default_value(const QofInstance *new_value)
     208              : {
     209            1 :     m_value = m_default_value = make_gnc_item(new_value);
     210              : 
     211            1 : }
     212              : 
     213              : const QofInstance*
     214          126 : GncOptionQofInstanceValue::get_value() const
     215              : {
     216          126 :     return qof_instance_from_gnc_item(m_value);
     217              : }
     218              : 
     219              : const QofInstance*
     220            4 : GncOptionQofInstanceValue::get_default_value() const
     221              : {
     222            4 :     return qof_instance_from_gnc_item(m_default_value);
     223              : }
     224              : 
     225              : void
     226            0 : GncOptionQofInstanceValue::reset_default_value()
     227              : {
     228            0 :     m_value = m_default_value;
     229            0 : }
     230              : 
     231              : bool
     232            0 : GncOptionQofInstanceValue::is_changed() const noexcept
     233              : {
     234            0 :     return m_value != m_default_value;
     235              : }
     236              : 
     237              : bool
     238            0 : GncOptionQofInstanceValue::deserialize(const std::string& str) noexcept
     239              : {
     240            0 :     QofInstance* inst{};
     241              :     // Commodities are often serialized as Namespace::Mnemonic or just Mnemonic
     242              :     try {
     243            0 :         auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
     244            0 :         inst = qof_instance_from_guid(&guid, m_ui_type);
     245            0 :         if (inst)
     246              :         {
     247            0 :             m_value = make_gnc_item(inst);
     248            0 :             return true;
     249              :         }
     250              :     }
     251            0 :     catch (const gnc::guid_syntax_exception& err)
     252              :     {
     253            0 :         PWARN("Failed to convert %s to a GUID", str.c_str());
     254            0 :     }
     255            0 :     return false;
     256              : }
     257              : 
     258              : std::string
     259            4 : GncOptionQofInstanceValue::serialize() const noexcept
     260              : {
     261            4 :     auto inst{get_value()};
     262            4 :     std::string retval;
     263            4 :     if (GNC_IS_COMMODITY(inst))
     264              :     {
     265            1 :         auto commodity{GNC_COMMODITY(inst)};
     266            1 :         if (!gnc_commodity_is_currency(commodity))
     267              :         {
     268            1 :             auto name_space{gnc_commodity_get_namespace(GNC_COMMODITY(inst))};
     269            1 :             if (name_space && *name_space != '\0')
     270              :             {
     271            1 :                 retval = name_space;
     272            1 :                 retval += ":";
     273              :             }
     274              :         }
     275            1 :         retval += gnc_commodity_get_mnemonic(GNC_COMMODITY(inst));
     276            1 :         return retval;
     277              :     }
     278              :     else
     279              :     {
     280            3 :         gnc::GUID guid{m_value.second};
     281            3 :         retval = guid.to_string();
     282              :     }
     283            3 :     return retval;
     284              : }
     285              : 
     286              : static gnc_commodity*
     287          178 : gnc_commodity_from_namespace_and_mnemonic(std::string_view name_space,
     288              :                                           std::string_view mnemonic)
     289              : {
     290          178 :     auto book{get_current_book()};
     291          178 :     auto table = gnc_commodity_table_get_table(book);
     292          178 :     return gnc_commodity_table_lookup(table, name_space.data(),
     293          178 :                                       mnemonic.data());
     294              : }
     295              : 
     296              : gnc_commodity*
     297          171 : GncOptionCommodityValue::get_value() const
     298              : {
     299          171 :     return gnc_commodity_from_namespace_and_mnemonic(m_namespace, m_mnemonic);
     300              : }
     301              : 
     302              : gnc_commodity*
     303            4 : GncOptionCommodityValue::get_default_value() const
     304              : {
     305            4 :     return gnc_commodity_from_namespace_and_mnemonic(m_default_namespace,
     306            4 :                                                      m_default_mnemonic);
     307              : }
     308              : 
     309              : void
     310           24 : GncOptionCommodityValue::set_value(gnc_commodity* value)
     311              : {
     312           24 :     if (!validate(value))
     313            1 :         throw std::invalid_argument("Value not a currency when required or not a commodity. Value not set.");
     314           23 :     m_mnemonic = gnc_commodity_get_mnemonic(value);
     315           23 :     m_namespace = gnc_commodity_get_namespace(value);
     316           23 :     m_dirty = true;
     317           23 : }
     318              : 
     319              : void
     320            0 : GncOptionCommodityValue::set_default_value(gnc_commodity* value)
     321              : {
     322            0 :     if (!validate(value))
     323            0 :         throw std::invalid_argument("Value not a currency when required or not a commodity. Value not set.");
     324            0 :     m_mnemonic = m_default_mnemonic = gnc_commodity_get_mnemonic(value);
     325            0 :     m_namespace = m_default_namespace = gnc_commodity_get_namespace(value);
     326            0 : }
     327              : 
     328              : void
     329            0 : GncOptionCommodityValue::reset_default_value()
     330              : {
     331            0 :     m_mnemonic = m_default_mnemonic;
     332            0 :     m_namespace = m_default_namespace;
     333            0 : }
     334              : 
     335              : bool
     336            0 : GncOptionCommodityValue::is_changed() const noexcept
     337              : {
     338            0 :     return m_namespace != m_default_namespace || m_mnemonic != m_default_mnemonic;
     339              : }
     340              : 
     341              : bool
     342          173 : GncOptionCommodityValue::validate(gnc_commodity* comm) const noexcept
     343              : {
     344          173 :     if (!GNC_IS_COMMODITY(comm))
     345            0 :         return false;
     346          173 :     if (m_is_currency && !gnc_commodity_is_currency(comm))
     347            4 :         return false;
     348          169 :     return true;
     349              : }
     350              : 
     351              : std::string
     352            3 : GncOptionCommodityValue::serialize() const noexcept
     353              : {
     354            3 :     if (m_is_currency)
     355            2 :         return m_mnemonic;
     356              :     else
     357            1 :         return m_namespace + ":" + m_mnemonic;
     358              : }
     359              : 
     360              : bool
     361            3 : GncOptionCommodityValue::deserialize(const std::string& str) noexcept
     362              : {
     363            3 :    auto sep{str.find(":")};
     364            3 :     gnc_commodity* comm{};
     365            3 :     std::string mnemonic, name_space;
     366            3 :     if (sep != std::string::npos)
     367              :     {
     368            2 :         name_space = str.substr(0, sep);
     369            2 :         mnemonic = str.substr(sep + 1, -1);
     370              :     }
     371              :     else
     372              :     {
     373            1 :         name_space = "CURRENCY";
     374            1 :         mnemonic = str;
     375              :     }
     376            3 :     comm = gnc_commodity_from_namespace_and_mnemonic(name_space, mnemonic);
     377            3 :     if (!validate(comm))
     378            1 :         return false;
     379            2 :     m_namespace = std::move(name_space);
     380            2 :     m_mnemonic = std::move(mnemonic);
     381            2 :     return true;
     382            3 : }
     383              : 
     384              : bool
     385          164 : GncOptionAccountListValue::validate(const GncOptionAccountList& values) const
     386              : {
     387          164 :     if (values.empty())
     388           24 :         return true;
     389          141 :     if ((get_ui_type() == GncOptionUIType::ACCOUNT_SEL || !m_multiselect) &&
     390            1 :         values.size() != 1)
     391              :     {
     392            1 :         PWARN("GncOptionAccountListValue::validate: Multiple values for a non-multiselect option.");
     393            1 :         return false;
     394              :     }
     395          139 :     if (m_allowed.empty())
     396           70 :         return true;
     397           69 :     auto book{get_current_book()};
     398          244 :     for(auto& guid : values)
     399              :     {
     400          124 :         if (std::find(m_allowed.begin(), m_allowed.end(),
     401          248 :                       xaccAccountGetType(xaccAccountLookup(&guid, book))) == m_allowed.end())
     402              :         {
     403           18 :             PWARN("GncOptionAccountListValue::validate: Account %s is not of an allowed type", gnc::GUID(guid).to_string().c_str());
     404           18 :             return false; }
     405              :     }
     406           51 :     return true;
     407              : }
     408              : 
     409              : GncOptionAccountList
     410          350 : GncOptionAccountListValue::get_value() const
     411              : {
     412          350 :     return !m_value.empty() ? m_value : get_default_value();
     413              : }
     414              : 
     415              : GncOptionAccountList
     416          118 : GncOptionAccountListValue::get_default_value() const
     417              : {
     418          118 :     if (!m_default_value.empty())
     419            4 :         return m_default_value;
     420              : 
     421              :     /* If no default has been set and there's an allowed set then find the first
     422              :      * account that matches one of the allowed account types.
     423              :      */
     424          114 :     GncOptionAccountList retval{};
     425          114 :     if (m_allowed.empty())
     426           95 :         return retval;
     427              : 
     428           19 :     auto root{get_current_root_account()};
     429           19 :     auto account_list{gnc_account_get_descendants_sorted(root)};
     430           19 :     if (!account_list)
     431           16 :         return retval;
     432              : 
     433           45 :     for (auto node = account_list; node; node = g_list_next (node))
     434              :     {
     435           23 :         if (std::find(m_allowed.begin(), m_allowed.end(),
     436           46 :                       xaccAccountGetType(GNC_ACCOUNT(node->data))) != m_allowed.end())
     437              :         {
     438            2 :             retval.push_back(*qof_entity_get_guid(GNC_ACCOUNT(node->data)));
     439            2 :             break;
     440              :         }
     441              :     }
     442            3 :     g_list_free(account_list);
     443            3 :     return retval;
     444          114 : }
     445              : 
     446              : bool
     447            0 : GncOptionAccountListValue::is_changed() const noexcept
     448              : {
     449            0 :     return m_value != m_default_value;
     450              : }
     451              : 
     452              : 
     453              : 
     454              : /**
     455              :  * Create a GList of account types to pass to gnc_account_sel_set_acct_filters.
     456              :  * gnc_account_sel_set_acct_filters copies the list so the intermediary caller
     457              :  * is responsible for freeing the list.
     458              :  *
     459              :  * @return an allocated GList* or nullptr if the list is empty.
     460              :  */
     461              : GList*
     462            0 : GncOptionAccountListValue::account_type_list() const noexcept
     463              : {
     464            0 :     if (m_allowed.empty())
     465            0 :         return nullptr;
     466            0 :     GList* retval{nullptr};
     467            0 :     for (auto type : m_allowed)
     468            0 :         retval = g_list_prepend(retval, GINT_TO_POINTER(type));
     469            0 :     return g_list_reverse(retval);
     470              : }
     471              : 
     472              : bool
     473           15 : GncOptionAccountSelValue::validate(const Account* value) const
     474              : {
     475           15 :     if (m_allowed.empty() || !value)
     476           12 :         return true;
     477            3 :     if (std::find(m_allowed.begin(), m_allowed.end(),
     478            6 :                   xaccAccountGetType(value)) == m_allowed.end())
     479            0 :             return false;
     480            3 :     return true;
     481              : }
     482              : 
     483              : const Account*
     484           13 : GncOptionAccountSelValue::get_value() const
     485              : {
     486           13 :     auto book{get_current_book()};
     487           13 :     return guid_equal(guid_null(), &m_value) ? get_default_value() :
     488           13 :            xaccAccountLookup(&m_value, book);
     489              : }
     490              : 
     491              : const Account*
     492           11 : GncOptionAccountSelValue::get_default_value() const
     493              : {
     494              : 
     495           11 :     if (!guid_equal(guid_null(), &m_default_value))
     496              :     {
     497            0 :         auto book{get_current_book()};
     498            0 :         return xaccAccountLookup(&m_default_value, book);
     499              :     }
     500              : 
     501              :     /* If no default has been set and there's an allowed set then find the first
     502              :      * account that matches one of the allowed account types.
     503              :      */
     504           11 :     if (m_allowed.empty())
     505            3 :         return nullptr;
     506              : 
     507            8 :     const Account* retval{nullptr};
     508            8 :     auto root{get_current_root_account()};
     509            8 :     auto account_list{gnc_account_get_descendants_sorted(root)};
     510            8 :     if (!account_list)
     511            5 :         return nullptr;
     512              : 
     513           41 :     for (auto node = account_list; node; node = g_list_next (node))
     514           22 :         if (std::find(m_allowed.begin(), m_allowed.end(),
     515           44 :                       xaccAccountGetType(GNC_ACCOUNT(node->data))) != m_allowed.end())
     516              :         {
     517            3 :             retval = GNC_ACCOUNT(node->data);
     518            3 :             break;
     519              :         }
     520            3 :     g_list_free(account_list);
     521            3 :     return retval;
     522              : }
     523              : 
     524              : 
     525              : /**
     526              :  * Create a GList of account types to pass to gnc_account_sel_set_acct_filters.
     527              :  * gnc_account_sel_set_acct_filters copies the list so the intermediary caller
     528              :  * is responsible for freeing the list.
     529              :  *
     530              :  * @return an allocated GList* or nullptr if the list is empty.
     531              :  */
     532              : GList*
     533            0 : GncOptionAccountSelValue::account_type_list() const noexcept
     534              : {
     535            0 :     if (m_allowed.empty())
     536            0 :         return nullptr;
     537            0 :     GList* retval{nullptr};
     538            0 :     for (auto type : m_allowed)
     539            0 :         retval = g_list_prepend(retval, GINT_TO_POINTER(type));
     540            0 :     return g_list_reverse(retval);
     541              : }
     542              : 
     543              : bool
     544          174 : GncOptionDateValue::validate(RelativeDatePeriod value) {
     545          174 :     if (m_period_set.empty())
     546           27 :         return true; // No restrictions
     547          147 :     if (std::find(m_period_set.begin(), m_period_set.end(),
     548          294 :                   value) != m_period_set.end())
     549          145 :         return true;
     550            2 :     return false;
     551              : }
     552              : 
     553              : time64
     554          238 : GncOptionDateValue::get_value() const noexcept
     555              : {
     556          238 :     if (m_period == RelativeDatePeriod::ABSOLUTE)
     557          225 :         return m_date;
     558           13 :     return gnc_relative_date_to_time64(m_period);
     559              : }
     560              : 
     561              : time64
     562            0 : GncOptionDateValue::get_default_value() const noexcept
     563              : {
     564            0 :     if (m_default_period == RelativeDatePeriod::ABSOLUTE)
     565            0 :         return m_default_date;
     566            0 :     return gnc_relative_date_to_time64(m_default_period);
     567              : }
     568              : 
     569              : /* Use asserts for pre- and post-conditions to deliberately crash if they're not
     570              :  * met as the program design should prevent that from happening.
     571              :  */
     572              : uint16_t
     573            4 : GncOptionDateValue::get_period_index() const noexcept
     574              : {
     575            4 :     assert (m_period != RelativeDatePeriod::ABSOLUTE);
     576            4 :     assert(!m_period_set.empty());
     577            4 :     auto item{std::find(m_period_set.begin(), m_period_set.end(), m_period)};
     578            8 :     assert(item != m_period_set.end());
     579            8 :     return item - m_period_set.begin();
     580              : }
     581              : 
     582              : uint16_t
     583            0 : GncOptionDateValue::get_default_period_index() const noexcept
     584              : {
     585            0 :     assert(m_period != RelativeDatePeriod::ABSOLUTE);
     586            0 :     assert(!m_period_set.empty());
     587            0 :     auto item{std::find(m_period_set.begin(), m_period_set.end(),
     588            0 :                         m_default_period)};
     589            0 :     assert (item != m_period_set.end());
     590            0 :     return item - m_period_set.begin();
     591              : }
     592              : 
     593              : void
     594            2 : GncOptionDateValue::set_value(uint16_t index) noexcept
     595              : {
     596            2 :     assert(!m_period_set.empty());
     597            2 :     assert(index < m_period_set.size());
     598            2 :     m_date = INT64_MAX;
     599            2 :     m_period = m_period_set[index];
     600            2 :     m_dirty = true;
     601            2 : }
     602              : 
     603              : uint16_t
     604            0 : GncOptionDateValue::permissible_value_index(const char* key) const noexcept
     605              : {
     606            0 :     auto index = std::find_if(m_period_set.begin(), m_period_set.end(),
     607            0 :                               [key](auto period) -> bool {
     608            0 :                                   return strcmp(gnc_relative_date_display_string(period),
     609            0 :                                                 key) == 0;
     610              :                               });
     611            0 :     return index != m_period_set.end() ? index - m_period_set.begin() : 0;
     612              : }
     613              : 
     614              : static const char* date_type_str[] {"absolute", "relative"};
     615              : 
     616              : std::ostream&
     617            0 : GncOptionDateValue::out_stream(std::ostream& oss) const noexcept
     618              : {
     619            0 :     if (m_period == RelativeDatePeriod::ABSOLUTE)
     620            0 :         oss << date_type_str[0] << " . " << m_date;
     621              :     else
     622              :         oss << date_type_str[1] << " . " <<
     623            0 :             gnc_relative_date_storage_string(m_period);
     624            0 :     return oss;
     625              : }
     626              : 
     627              : std::istream&
     628           13 : GncOptionDateValue::in_stream(std::istream& iss)
     629              : {
     630              :     char type_str[10]; //The length of both "absolute" and "relative" plus 1.
     631           13 :     iss.getline(type_str, sizeof(type_str), '.');
     632           13 :     if(!iss)
     633            0 :         throw std::invalid_argument("Date Type separator missing");
     634              :     /* strcmp is safe, istream::getline null terminates the buffer. */
     635           13 :     if (strcmp(type_str, "absolute ") == 0)
     636              :     {
     637              :         time64 time;
     638            1 :         iss >> time;
     639            1 :         set_value(time);
     640            1 :         if (iss.get() != ')')
     641            1 :             iss.unget();
     642              :     }
     643           12 :     else if (strcmp(type_str, "relative ") == 0)
     644              :     {
     645           12 :         std::string period_str;
     646           12 :         iss >> period_str;
     647           12 :         if (period_str.back() == ')')
     648            0 :             period_str.pop_back();
     649           12 :         auto period = gnc_relative_date_from_storage_string(period_str.c_str());
     650           12 :         if (period == RelativeDatePeriod::ABSOLUTE)
     651              :         {
     652            0 :             std::string err{"Unknown period string in date option: '"};
     653            0 :             err += period_str;
     654            0 :             err += "'";
     655            0 :             throw std::invalid_argument(err);
     656            0 :         }
     657              : 
     658           12 :         set_value(period);
     659           12 :     }
     660              :     else
     661              :     {
     662            0 :         std::string err{"Unknown date type string in date option: '"};
     663            0 :         err += type_str;
     664            0 :         err += "'";
     665            0 :         throw std::invalid_argument{err};
     666            0 :     }
     667           13 :     return iss;
     668              : }
     669              : 
     670              : QofInstance*
     671            5 : qof_instance_from_guid(GncGUID* guid, GncOptionUIType type)
     672              : {
     673              :     QofIdType qof_type;
     674            5 :     switch(type)
     675              :     {
     676            1 :         case GncOptionUIType::BUDGET:
     677            1 :             qof_type = "Budget";
     678            1 :             break;
     679            0 :         case GncOptionUIType::JOB:
     680            0 :             qof_type = "gncJob";
     681            0 :             break;
     682            0 :         case GncOptionUIType::CUSTOMER:
     683            0 :             qof_type = "gncCustomer";
     684            0 :             break;
     685            0 :         case GncOptionUIType::VENDOR:
     686            0 :             qof_type = "gncVendor";
     687            0 :             break;
     688            0 :         case GncOptionUIType::EMPLOYEE:
     689            0 :             qof_type = "gncEmployee";
     690            0 :             break;
     691            0 :         case GncOptionUIType::INVOICE:
     692            0 :             qof_type = "gncInvoice";
     693            0 :             break;
     694            0 :         case GncOptionUIType::TAX_TABLE:
     695            0 :             qof_type = "gncTaxTable";
     696            0 :             break;
     697            4 :         case GncOptionUIType::ACCOUNT_LIST:
     698              :         case GncOptionUIType::ACCOUNT_SEL:
     699              :         default:
     700            4 :             qof_type = "Account";
     701            4 :             break;
     702              :     }
     703            5 :     auto book{get_current_book()};
     704            5 :     auto col{qof_book_get_collection(book, qof_type)};
     705            5 :     return QOF_INSTANCE(qof_collection_lookup_entity(col, guid));
     706              : }
     707              : 
     708              : QofInstance*
     709            5 : qof_instance_from_string(const std::string& str, GncOptionUIType type)
     710              : {
     711            5 :     QofInstance* retval{nullptr};
     712              :     try {
     713            5 :         auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
     714            5 :         retval = qof_instance_from_guid(&guid, type);
     715              :     }
     716            0 :     catch (const gnc::guid_syntax_exception& err)
     717              :     {
     718            0 :         PWARN("Failed to convert %s to a GUID", str.c_str());
     719            0 :     }
     720            5 :     return retval;
     721              : }
     722              : 
     723              : std::string
     724            1 : qof_instance_to_string(const QofInstance* inst)
     725              : {
     726            1 :     std::string retval;
     727            1 :     gnc::GUID guid{*qof_instance_get_guid(inst)};
     728            1 :     retval = guid.to_string();
     729            1 :     return retval;
     730            0 : }
     731              : 
     732              : template <typename ValueType> void
     733         1315 : GncOptionValue<ValueType>::set_value(ValueType new_value)
     734              : {
     735         1315 :     m_value = new_value;
     736         1315 :     m_dirty = true;
     737         1315 : }
     738              : 
     739              : template <typename ValueType> void
     740            9 : GncOptionValue<ValueType>::set_default_value(ValueType new_value)
     741              : {
     742            9 :     m_value = m_default_value = new_value;
     743            9 : }
     744              : 
     745              : template <typename ValueType> void
     746            0 : GncOptionValue<ValueType>::reset_default_value()
     747              : {
     748            0 :     m_value = m_default_value;
     749            0 : }
     750              : 
     751              : /* Missing on purpose: QofQuery because for current usage it's serialized with
     752              :  * gnc_query2scm. The future is to replace QofQuery with SQL queries so there's
     753              :  * not much point to spending the time to create a std::string serialization for
     754              :  * them.
     755              :  */
     756              : template <typename ValueType> std::string
     757           12 : GncOptionValue<ValueType>::serialize() const noexcept
     758              : {
     759           20 :     static const std::string no_value{"No Value"};
     760              :     if constexpr(std::is_same_v<ValueType, const QofInstance*>)
     761              :         return m_value ? qof_instance_to_string(m_value) : no_value;
     762              :     if constexpr(std::is_same_v<ValueType, const GncOwner*>)
     763              :     {
     764            0 :         if (!m_value)
     765            0 :             return no_value;
     766            0 :         auto guid{qof_instance_to_string(qofOwnerGetOwner(m_value))};
     767            0 :         auto type{qofOwnerGetType(m_value)};
     768            0 :         std::ostringstream ostr{};
     769            0 :         ostr << type << " " << guid;
     770            0 :         return ostr.str();
     771            0 :     }
     772              :     if constexpr(std::is_same_v<ValueType, GncOptionReportPlacementVec>)
     773              :     {
     774            0 :         std::ostringstream ostr{};
     775            0 :         ostr << "'(";
     776            0 :         std::for_each(m_value.begin(), m_value.end(),
     777            0 :                  [&ostr](auto rp){
     778            0 :                     auto [id, wide, high] = rp;
     779            0 :                     ostr << "(" << id << " " << wide << " " << high << " #f) ";
     780              :                  });
     781            0 :         ostr << ")";
     782            0 :         return ostr.str();
     783            0 :     }
     784              :     else if constexpr(is_same_decayed_v<ValueType, std::string>)
     785            9 :         return m_value;
     786              :     else if constexpr(is_same_decayed_v<ValueType, bool>)
     787            4 :         return m_value ? "True" : "False";
     788              :     else if constexpr(std::is_arithmetic_v<ValueType>)
     789            1 :         return std::to_string(m_value);
     790              :     else
     791            0 :         return "Serialization not implemented";
     792              : }
     793              : 
     794              : template <typename ValueType> bool
     795            0 : GncOptionValue<ValueType>::deserialize(const std::string& str) noexcept
     796              : {
     797              :     if constexpr(std::is_same_v<ValueType, const QofInstance*>)
     798              :         set_value(qof_instance_from_string(str, get_ui_type()));
     799              :     if constexpr(std::is_same_v<ValueType, const GncOwner*>)
     800              :     {
     801            0 :         std::istringstream istr{str};
     802            0 :         std::string type, guid;
     803            0 :         istr >> type >> guid;
     804            0 :         auto inst{qof_instance_from_string(guid, get_ui_type())};
     805            0 :         qofOwnerSetEntity(const_cast<GncOwner*>(m_value), inst);
     806            0 :     }
     807              :     if constexpr(std::is_same_v<ValueType, GncOptionReportPlacementVec>)
     808              :     {
     809            0 :         std::istringstream istr{str};
     810            0 :         GncOptionReportPlacementVec rpv;
     811            0 :         while (istr)
     812              :         {
     813              :             uint32_t id, wide, high;
     814            0 :             istr >> id >> wide >> high;
     815            0 :             rpv.emplace_back(id, wide, high);
     816              :         }
     817            0 :         set_value(rpv);
     818            0 :     }
     819              :     else if constexpr(is_same_decayed_v<ValueType, std::string>)
     820            0 :         set_value(str);
     821              :     else if constexpr(is_same_decayed_v<ValueType, bool>)
     822            0 :         set_value(str == "True");
     823              :     else if constexpr(is_same_decayed_v<ValueType, int>)
     824            0 :         set_value(stoi(str));
     825              :     else if constexpr(is_same_decayed_v<ValueType, int64_t>)
     826            0 :         set_value(stoll(str));
     827              :     else if constexpr(is_same_decayed_v<ValueType, double>)
     828            0 :         set_value(stod(str));
     829              :     else
     830            0 :         return false;
     831            0 :     return true;
     832              : }
     833              : 
     834              : std::string
     835            3 : GncOptionAccountListValue::serialize() const noexcept
     836              : {
     837            7 :     static const std::string no_value{"No Value"};
     838            3 :     std::string retval;
     839            3 :     bool first = true;
     840            3 :     if (m_value.empty())
     841            0 :         return no_value;
     842              :     gchar guidstr[GUID_ENCODING_LENGTH + 1];
     843           11 :     for (auto val : m_value)
     844              :     {
     845            5 :         if (!first)
     846            2 :             retval += " ";
     847            5 :         first = false;
     848            5 :         guid_to_string_buff (&val, guidstr);
     849            5 :         retval += guidstr;
     850              :     }
     851            3 :     return retval;
     852            3 : }
     853              : 
     854              : bool
     855            0 : GncOptionAccountListValue::deserialize(const std::string& str) noexcept
     856              : {
     857            0 :     if (str.empty() || str.size() < GUID_ENCODING_LENGTH)
     858            0 :         return false;
     859            0 :     m_value.clear();
     860            0 :     m_value.reserve(str.size() / GUID_ENCODING_LENGTH);
     861            0 :     bool first = true;
     862            0 :     size_t pos{};
     863            0 :     while (pos + GUID_ENCODING_LENGTH < str.size())
     864              :     {
     865            0 :         if (!first)
     866            0 :             ++pos;
     867            0 :         first = false;
     868            0 :         GncGUID guid{};
     869            0 :         string_to_guid(str.substr(pos, pos + GUID_ENCODING_LENGTH).c_str(), &guid);
     870            0 :         m_value.push_back(guid);
     871            0 :         pos += GUID_ENCODING_LENGTH;
     872              :     }
     873            0 :     return true;
     874              : }
     875              : 
     876              : std::string
     877            2 : GncOptionAccountSelValue::serialize() const noexcept
     878              : {
     879            4 :     static const std::string no_value{"No Value"};
     880            2 :     if (guid_equal(guid_null(), &m_value))
     881            0 :         return no_value;
     882              : 
     883              :     gchar strbuff[GUID_ENCODING_LENGTH + 1];
     884            2 :     guid_to_string_buff (&m_value, strbuff);
     885            4 :     return strbuff;
     886              : }
     887              : 
     888              : bool
     889            0 : GncOptionAccountSelValue::deserialize(const std::string& str) noexcept
     890              : {
     891            0 :     set_value(reinterpret_cast<Account*>(qof_instance_from_string(str, get_ui_type())));
     892            0 :     return true;
     893              : }
     894              : 
     895              : std::string
     896            8 : GncOptionMultichoiceValue::serialize() const noexcept
     897              : {
     898           12 :     static const std::string no_value{""};
     899            8 :     std::string retval;
     900            8 :     bool first = true;
     901            8 :     bool list_context = m_ui_type == GncOptionUIType::LIST;
     902            8 :     if (m_value.empty())
     903            0 :         return no_value;
     904              : 
     905            8 :     if (list_context)
     906            2 :         retval += '(';
     907           25 :     for (auto index : m_value)
     908              :     {
     909            9 :         if (!first)
     910            1 :             retval += " ";
     911            9 :         first = false;
     912            9 :         retval += std::get<0>(m_choices[index]);
     913              :     }
     914            8 :     if (list_context)
     915            2 :         retval += ')';
     916            8 :     return retval;
     917            8 : }
     918              : 
     919              : bool
     920            0 : GncOptionMultichoiceValue::deserialize(const std::string& str) noexcept
     921              : {
     922              :     static const auto uint16_t_max = std::numeric_limits<uint16_t>::max();
     923            0 :     if (str.empty())
     924              : 
     925            0 :         return false;
     926            0 :     uint16_t pos{};
     927            0 :     while (pos < str.size())
     928              :     {
     929            0 :         auto endpos{str.find(' ', pos)};
     930            0 :         if (endpos == std::string::npos)
     931            0 :             endpos = str.size();
     932              :         //need a null-terminated char* to pass to permissible_value_index
     933            0 :         auto index{permissible_value_index(str.substr(pos, endpos).c_str())};
     934            0 :         if (index == uint16_t_max)
     935            0 :             return false;
     936            0 :         m_value.push_back(index);
     937            0 :         pos = endpos + 1;
     938              :     }
     939            0 :     return true;
     940              : }
     941              : 
     942              : template <typename ValueType> std::string
     943            4 : GncOptionRangeValue<ValueType>::serialize() const noexcept
     944              : {
     945              :     if constexpr (std::is_arithmetic_v<ValueType>)
     946              :     {
     947            4 :         std::ostringstream ostr;
     948              :         if constexpr(is_same_decayed_v<ValueType, double>)
     949            2 :             ostr << std::showpoint << std::fixed;
     950            4 :         ostr << m_value;
     951            4 :         return ostr.str();
     952            4 :     }
     953              :     return "";
     954              : }
     955              : 
     956              : template <typename ValueType> bool
     957            0 : GncOptionRangeValue<ValueType>::deserialize(const std::string& str) noexcept
     958              : {
     959              :     if constexpr(is_same_decayed_v<ValueType, int>)
     960            0 :         set_value(stoi(str));
     961              :     else if constexpr(is_same_decayed_v<ValueType, double>)
     962            0 :         set_value(stod(str));
     963            0 :     return true;
     964              : }
     965              : 
     966              : std::string
     967           20 : GncOptionDateValue::serialize() const noexcept
     968              : {
     969           20 :     std::string retval{"("};
     970           20 :     if (m_period == RelativeDatePeriod::ABSOLUTE)
     971              :     {
     972            3 :         retval += date_type_str[0];
     973            3 :         retval += " . ";
     974            3 :         retval += std::to_string(m_date);
     975              :     }
     976              :     else
     977              :     {
     978           17 :         retval += date_type_str[1];
     979           17 :         retval +=  " . ";
     980           17 :         retval += gnc_relative_date_storage_string(m_period);
     981              :     }
     982           20 :     retval += ")";
     983           20 :     return retval;
     984              : }
     985              : 
     986              : bool
     987            0 : GncOptionDateValue::deserialize(const std::string& str) noexcept
     988              : {
     989              :  //The length of both "absolute" and "relative".
     990              :     static constexpr size_t date_type_len{9};
     991              :     // date_type_len plus the length of " . ".
     992              :     static constexpr size_t date_value_pos{12};
     993            0 :     auto type_str{str.substr(0, date_type_len)};
     994            0 :     auto period_str{str.substr(date_value_pos)};
     995            0 :     if (type_str == "absolute")
     996              :     {
     997              :         // Need a cast to disambiguate from time64.
     998            0 :         set_value(static_cast<uint16_t>(std::stoll(period_str)));
     999            0 :         return true;
    1000              :     }
    1001            0 :     else if (type_str == "relative ")
    1002              :     {
    1003            0 :         auto period = gnc_relative_date_from_storage_string(period_str.c_str());
    1004            0 :         if (period == RelativeDatePeriod::ABSOLUTE)
    1005              :         {
    1006            0 :             PWARN("Unknown period string in date option: '%s'",
    1007              :                   period_str.c_str());
    1008            0 :             return false;
    1009              :         }
    1010              : 
    1011            0 :         set_value(period);
    1012            0 :         return true;
    1013              :     }
    1014              :     else
    1015              :     {
    1016            0 :         PWARN("Unknown date type string in date option: '%s'",
    1017              :               type_str.c_str());
    1018            0 :         return false;
    1019              :     }
    1020            0 : }
    1021              : 
    1022              : std::istream&
    1023            3 : operator>> (std::istream& iss, GncOptionCommodityValue& opt)
    1024              : {
    1025            3 :     std::string instr;
    1026            3 :     iss >> instr;
    1027            3 :     if (!opt.deserialize(instr))
    1028            1 :         throw std::invalid_argument("Invalid commodity string in stream.");
    1029            2 :     return iss;
    1030            3 : }
    1031              : 
    1032              : template void GncOptionValue<bool>::set_value(bool);
    1033              : template void GncOptionValue<int>::set_value(int);
    1034              : template void GncOptionValue<int64_t>::set_value(int64_t);
    1035              : template void GncOptionValue<double>::set_value(double);
    1036              : template void GncOptionValue<char*>::set_value(char*);
    1037              : template void GncOptionValue<const char*>::set_value(const char*);
    1038              : template void GncOptionValue<std::string>::set_value(std::string);
    1039              : template void GncOptionValue<const QofQuery*>::set_value(const QofQuery*);
    1040              : template void GncOptionValue<const GncOwner*>::set_value(const GncOwner*);
    1041              : template void GncOptionValue<RelativeDatePeriod>::set_value(RelativeDatePeriod);
    1042              : template void GncOptionValue<uint16_t>::set_value(uint16_t);
    1043              : template void GncOptionValue<GncOptionAccountList>::set_value(GncOptionAccountList);
    1044              : template void GncOptionValue<GncMultichoiceOptionIndexVec>::set_value(GncMultichoiceOptionIndexVec);
    1045              : template void GncOptionValue<GncOptionReportPlacementVec>::set_value(GncOptionReportPlacementVec);
    1046              : template void GncOptionValue<GncOptionDateFormat>::set_value(GncOptionDateFormat);
    1047              : template void GncOptionValue<bool>::set_default_value(bool);
    1048              : template void GncOptionValue<int>::set_default_value(int);
    1049              : template void GncOptionValue<int64_t>::set_default_value(int64_t);
    1050              : template void GncOptionValue<double>::set_default_value(double);
    1051              : template void GncOptionValue<char*>::set_default_value(char*);
    1052              : template void GncOptionValue<const char*>::set_default_value(const char*);
    1053              : template void GncOptionValue<std::string>::set_default_value(std::string);
    1054              : template void GncOptionValue<const QofQuery*>::set_default_value(const QofQuery*);
    1055              : template void GncOptionValue<const GncOwner*>::set_default_value(const GncOwner*);
    1056              : template void GncOptionValue<RelativeDatePeriod>::set_default_value(RelativeDatePeriod);
    1057              : template void GncOptionValue<uint16_t>::set_default_value(uint16_t);
    1058              : template void GncOptionValue<GncOptionAccountList>::set_default_value(GncOptionAccountList);
    1059              : template void GncOptionValue<GncMultichoiceOptionIndexVec>::set_default_value(GncMultichoiceOptionIndexVec);
    1060              : template void GncOptionValue<GncOptionReportPlacementVec>::set_default_value(GncOptionReportPlacementVec);
    1061              : template void GncOptionValue<GncOptionDateFormat>::set_default_value(GncOptionDateFormat);
    1062              : template void GncOptionValue<bool>::reset_default_value();
    1063              : template void GncOptionValue<int>::reset_default_value();
    1064              : template void GncOptionValue<int64_t>::reset_default_value();
    1065              : template void GncOptionValue<double>::reset_default_value();
    1066              : template void GncOptionValue<char*>::reset_default_value();
    1067              : template void GncOptionValue<const char*>::reset_default_value();
    1068              : template void GncOptionValue<std::string>::reset_default_value();
    1069              : template void GncOptionValue<const QofQuery*>::reset_default_value();
    1070              : template void GncOptionValue<const GncOwner*>::reset_default_value();
    1071              : template void GncOptionValue<RelativeDatePeriod>::reset_default_value();
    1072              : template void GncOptionValue<uint16_t>::reset_default_value();
    1073              : template void GncOptionValue<GncOptionAccountList>::reset_default_value();
    1074              : template void GncOptionValue<GncMultichoiceOptionIndexVec>::reset_default_value();
    1075              : template void GncOptionValue<GncOptionReportPlacementVec>::reset_default_value();
    1076              : template void GncOptionValue<GncOptionDateFormat>::reset_default_value();
    1077              : template std::string GncOptionValue<bool>::serialize() const noexcept;
    1078              : template std::string GncOptionValue<int>::serialize() const noexcept;
    1079              : template std::string GncOptionValue<int64_t>::serialize() const noexcept;
    1080              : template std::string GncOptionValue<double>::serialize() const noexcept;
    1081              : template std::string GncOptionValue<char*>::serialize() const noexcept;
    1082              : template std::string GncOptionValue<const char*>::serialize() const noexcept;
    1083              : template std::string GncOptionValue<std::string>::serialize() const noexcept;
    1084              : template std::string GncOptionValue<const QofQuery*>::serialize() const noexcept;
    1085              : template std::string GncOptionValue<const GncOwner*>::serialize() const noexcept;
    1086              : template std::string GncOptionValue<GncOptionReportPlacementVec>::serialize() const noexcept;
    1087              : template std::string GncOptionValue<GncOptionDateFormat>::serialize() const noexcept;
    1088              : template std::string GncOptionRangeValue<int>::serialize() const noexcept;
    1089              : template std::string GncOptionRangeValue<double>::serialize() const noexcept;
    1090              : template bool GncOptionValue<bool>::deserialize(const std::string&) noexcept;
    1091              : template bool GncOptionValue<int>::deserialize(const std::string&) noexcept;
    1092              : template bool GncOptionValue<int64_t>::deserialize(const std::string&) noexcept;
    1093              : template bool GncOptionValue<double>::deserialize(const std::string&) noexcept;
    1094              : template bool GncOptionValue<char*>::deserialize(const std::string&) noexcept;
    1095              : template bool GncOptionValue<const char*>::deserialize(const std::string&) noexcept;
    1096              : template bool GncOptionValue<std::string>::deserialize(const std::string&) noexcept;
    1097              : template bool GncOptionValue<const QofQuery*>::deserialize(const std::string&) noexcept;
    1098              : template bool GncOptionValue<const GncOwner*>::deserialize(const std::string&) noexcept;
    1099              : template bool GncOptionValue<GncOptionReportPlacementVec>::deserialize(const std::string&) noexcept;
    1100              : template bool GncOptionValue<GncOptionDateFormat>::deserialize(const std::string&) noexcept;
    1101              : template bool GncOptionRangeValue<int>::deserialize(const std::string&) noexcept;
    1102              : template bool GncOptionRangeValue<double>::deserialize(const std::string&) noexcept;
        

Generated by: LCOV version 2.0-1