LCOV - code coverage report
Current view: top level - libgnucash/engine - kvp-frame.hpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 78.3 % 23 18
Test Date: 2026-11-02 18:19:43 Functions: 68.4 % 19 13
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /********************************************************************\
       2              :  * kvp-frame.hpp -- Implements a key-value frame system             *
       3              :  * Copyright (C) 2014 Aaron Laws                                    *
       4              :  * Copyright 2015 John Ralls                                        *
       5              :  *                                                                  *
       6              :  * This program is free software; you can redistribute it and/or    *
       7              :  * modify it under the terms of the GNU General Public License as   *
       8              :  * published by the Free Software Foundation; either version 2 of   *
       9              :  * the License, or (at your option) any later version.              *
      10              :  *                                                                  *
      11              :  * This program is distributed in the hope that it will be useful,  *
      12              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
      13              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
      14              :  * GNU General Public License for more details.                     *
      15              :  *                                                                  *
      16              :  * You should have received a copy of the GNU General Public License*
      17              :  * along with this program; if not, contact:                        *
      18              :  *                                                                  *
      19              :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      20              :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      21              :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      22              :  *                                                                  *
      23              : \********************************************************************/
      24              : /** @addtogroup KVP
      25              : 
      26              :  * A KvpFrame is a set of associations between character strings
      27              :  * (keys) and KvpValues.  A KvpValue is notionally a union with
      28              :  * possible types enumerated in the KvpValue::Type enum, and includes,
      29              :  * among other things, ints, doubles, strings, guids, lists, time
      30              :  * and numeric values.  KvpValues may also be other frames, so
      31              :  * KVP is inherently hierarchical.
      32              :  *
      33              :  * Values are stored in a 'slot' associated with a key.
      34              :  * Pointers passed as arguments into set_slot and get_slot are the
      35              :  * responsibility of the caller.  Pointers returned by get_slot are
      36              :  * owned by the kvp_frame.  Make copies as needed.
      37              :  *
      38              :  * A 'path' is a sequence of keys that can be followed to a value.  Paths are
      39              :  * passed as either '/'-delimited strings or as std::vectors of keys. Unlike
      40              :  * file system paths, the tokens '.' and '..' have no special meaning.
      41              :  *
      42              :  * KVP is an implementation detail whose direct use should be avoided; create an
      43              :  * abstraction object in libqof to keep KVP encapsulated here and ensure that
      44              :  * KVP modifications are written to the database. Two generic abstractions are
      45              :  * provided:
      46              :  *
      47              :  * * @ref qof_book_set_option and @ref qof_book_get_option provide similar
      48              :  *   access for book options.
      49              :  *
      50              :  * @ref kvpvalues provides a catolog of KVP entries including what objects
      51              :  * they're part of and how they're used.
      52              :  *
      53              :  * ## Purpose
      54              :  * KVP is used to extend the class structure without directly reflecting the
      55              :  * extension in the database or xML schema. The backend will directly load and
      56              :  * store KVP slots without any checking, which allows older versions of GnuCash
      57              :  * to load the database without complaint and without damaging the KVP data that
      58              :  * they don't understand.
      59              :  *
      60              :  * When a feature is entirely implemented in KVP and doesn't affect the meaning
      61              :  * of the books or other features, this isn't a problem, but when it's not true
      62              :  * then it should be registered in @ref UtilFeature so that older versions of
      63              :  * GnuCash will refuse to load the database.
      64              :  *
      65              :  * ## Policy
      66              :  * * Document every KVP slot in src/engine/kvp_doc.txt so that it is presented
      67              :  * in @ref kvpvalues.
      68              :  * * Register a feature in @ref UtilFeature if the use of the KVP in any way
      69              :     affects the books or business computations.
      70              :  * * Key strings should be all lower case with '-', not spaces, separating words
      71              :     and '/' separating frames. Prefer longer and more descriptive names to
      72              :     abbreviations, and define a global const char[] to the key or path string so
      73              :     that the compiler will catch any typos.
      74              :  * * Make good use of the hierarchical nature of KVP by using frames to group
      75              :     related slots.
      76              :  * * Don't use the KVP API directly outside of libqof, and prefer to use the
      77              :      QofInstance and QofBook functions whenever feasible. If those functions
      78              :      aren't feasible write a class in libqof to abstract the use of KVP.
      79              :  * * Avoid re-using key names in different contexts (e.g. Transactions and
      80              :     Splits) unless the slot is used for the same purpose in both.
      81              :  * @{
      82              : */
      83              : 
      84              : #ifndef GNC_KVP_FRAME_TYPE
      85              : #define GNC_KVP_FRAME_TYPE
      86              : 
      87              : #include "kvp-value.hpp"
      88              : #include <map>
      89              : #include <string>
      90              : #include <vector>
      91              : #include <cstring>
      92              : #include <algorithm>
      93              : #include <iostream>
      94              : using Path = std::vector<std::string>;
      95              : using KvpEntry = std::pair <std::vector <std::string>, KvpValue*>;
      96              : 
      97              : /** Implements KvpFrame.
      98              :  *  It's a struct because QofInstance needs to use the typename to declare a
      99              :  *  KvpFrame* member, and QofInstance's API is C until its children are all
     100              :  *  rewritten in C++.
     101              :  *
     102              :  * N.B.**  Writes to KvpFrames must** be wrapped in BeginEdit and Commit
     103              :  * for the containing QofInstance and the QofInstance must be marked dirty. This
     104              :  * is not** done by the KvpFrame API. In general Kvp items should be
     105              :  * accessed using either QofInstance or QofBook methods in order to ensure that
     106              :  * this is done.
     107              :  * @{
     108              :  */
     109              : struct KvpFrameImpl
     110              : {
     111              :     class cstring_comparer
     112              :     {
     113              :     public:
     114              :         /* Returns true if one is less than two. */
     115       562380 :         bool operator()(const char * one, const char * two) const
     116              :             {
     117       562380 :                 auto ret = std::strcmp(one, two) < 0;
     118       562380 :                 return ret;
     119              :             }
     120              :     };
     121              :     using map_type = std::map<const char *, KvpValue*, cstring_comparer>;
     122              : 
     123              :     public:
     124        45185 :     KvpFrameImpl() noexcept {};
     125              : 
     126              :     /**
     127              :      * Performs a deep copy.
     128              :      */
     129              :     KvpFrameImpl(const KvpFrameImpl &) noexcept;
     130              : 
     131              :     /**
     132              :      * Perform a deep delete.
     133              :      */
     134              :     ~KvpFrameImpl() noexcept;
     135              : 
     136              :     /**
     137              :      * Set the value with the key in the immediate frame, replacing and
     138              :      * returning the old value if it exists or nullptr if it doesn't. Takes
     139              :      * ownership of new value and releases ownership of the returned old
     140              :      * value. Values must be allocated on the free store with operator new.
     141              :      * @param key: The key to insert/replace.
     142              :      * @param newvalue: The value to set at key.
     143              :      * @return The old value if there was one or nullptr.
     144              :      */
     145              :     //KvpValue* set(const char * key, KvpValue* newvalue) noexcept;
     146              :     /**
     147              :      * Set the value with the key in a subframe following the keys in path,
     148              :      * replacing and returning the old value if it exists or nullptr if it
     149              :      * doesn't. Takes ownership of new value and releases ownership of the
     150              :      * returned old value. Values must be allocated on the free store with
     151              :      * operator new.
     152              :      * @param key: The key to insert/replace.
     153              :      * @throw invalid_argument if the path doesn't exist.
     154              :      * @param path: The path of subframes leading to the frame in which to
     155              :      * insert/replace.
     156              :      * @param newvalue: The value to set at key.
     157              :      * @return The old value if there was one or nullptr.
     158              :      */
     159              :     KvpValue* set(Path path, KvpValue* newvalue) noexcept;
     160              :      /**
     161              :      * Set the value with the key in a subframe following the keys in path,
     162              :      * replacing and returning the old value if it exists or nullptr if it
     163              :      * doesn't. Creates any missing intermediate frames.Takes
     164              :      * ownership of new value and releases ownership of the returned old
     165              :      * value. Values must be allocated on the free store with operator new.
     166              :      * @param path: The path of subframes as a std::vector leading to the
     167              :      * frame in which to insert/replace.
     168              :      * @param newvalue: The value to set at key.
     169              :      * @return The old value if there was one or nullptr.
     170              :      */
     171              :     KvpValue* set_path(Path path, KvpValue* newvalue) noexcept;
     172              :     /**
     173              :      * Make a string representation of the frame. Mostly useful for debugging.
     174              :      * @return A std::string representing the frame and all its children.
     175              :      */
     176              :     std::string to_string() const noexcept;
     177              :     /**
     178              :      * Make a string representation of the frame with the specified string
     179              :      * prefixed to every item in the frame.
     180              :      * @return A std::string representing all the children of the frame.
     181              :      */
     182              :     std::string to_string(std::string const &) const noexcept;
     183              :     /**
     184              :      * Report the keys in the immediate frame. Be sensible about using this, it
     185              :      * isn't a very efficient way to iterate.
     186              :      * @return std::vector of keys as std::strings.
     187              :      */
     188              :     std::vector<std::string> get_keys() const noexcept;
     189              : 
     190              :     /** Get the value for the tail of the path or nullptr if it doesn't exist.
     191              :      * @param path: Path of keys leading to the desired value.
     192              :      * @return The value at the key or nullptr.
     193              :      */
     194              :     KvpValue* get_slot(Path keys) noexcept;
     195              : 
     196              :     /** The function should be of the form:
     197              :      * <anything> func (char const *, KvpValue *, data_type &);
     198              :      * Do not pass nullptr as the function.
     199              :      */
     200              :     template <typename func_type, typename data_type>
     201              :     void for_each_slot_temp(func_type const &, data_type &) const noexcept;
     202              : 
     203              :     template <typename func_type>
     204              :     void for_each_slot_temp(func_type const &) const noexcept;
     205              : 
     206              :     /**
     207              :      * Like for_each_slot, but doesn't traverse nested values. This will only loop
     208              :      * over root-level values whose keys match the specified prefix.
     209              :      */
     210              :     template <typename func_type, typename data_type>
     211              :     void for_each_slot_prefix(std::string const & prefix, func_type const &, data_type &) const noexcept;
     212              : 
     213              :     template <typename func_type>
     214              :     void for_each_slot_prefix(std::string const & prefix, func_type const &) const noexcept;
     215              : 
     216              :     /**
     217              :      * Returns all keys and values of this frame recursively, flattening
     218              :      * the frame-containing values.
     219              :      */
     220              :     std::vector <KvpEntry>
     221              :     flatten_kvp(void) const noexcept;
     222              : 
     223              :     /** Test for emptiness
     224              :      * @return true if the frame contains nothing.
     225              :      */
     226         2960 :     bool empty() const noexcept { return m_valuemap.empty(); }
     227              :     friend int compare(const KvpFrameImpl&, const KvpFrameImpl&) noexcept;
     228              : 
     229            5 :     map_type::iterator begin() { return m_valuemap.begin(); }
     230            5 :     map_type::iterator end() { return m_valuemap.end(); }
     231              : 
     232              :     private:
     233              :     map_type m_valuemap;
     234              : 
     235              :     KvpFrame * get_child_frame_or_nullptr (Path const &) noexcept;
     236              :     KvpFrame * get_child_frame_or_create (Path const &) noexcept;
     237              :     void flatten_kvp_impl(std::vector <std::string>, std::vector <KvpEntry> &) const noexcept;
     238              :     KvpValue * set_impl (std::string const &, KvpValue *) noexcept;
     239              : };
     240              : 
     241              : template<typename func_type, typename data_type>
     242           13 : void KvpFrame::for_each_slot_prefix(std::string const & prefix,
     243              :         func_type const & func, data_type & data) const noexcept
     244              : {
     245           13 :     std::for_each (m_valuemap.begin(), m_valuemap.end(),
     246          132 :         [&prefix,&func,&data](const KvpFrameImpl::map_type::value_type & a)
     247              :         {
     248              :             /* Testing for prefix matching */
     249           66 :             if (strncmp(a.first, prefix.c_str(), prefix.size()) == 0)
     250           12 :                 func (&a.first[prefix.size()], a.second, data);
     251              :         }
     252              :     );
     253           13 : }
     254              : 
     255              : template <typename func_type>
     256            0 : void KvpFrame::for_each_slot_temp(func_type const & func) const noexcept
     257              : {
     258            0 :     std::for_each (m_valuemap.begin(), m_valuemap.end(),
     259            0 :         [&func](const KvpFrameImpl::map_type::value_type & a)
     260              :         {
     261            0 :             func (a.first, a.second);
     262              :         }
     263              :     );
     264            0 : }
     265              : 
     266              : template <typename func_type, typename data_type>
     267          610 : void KvpFrame::for_each_slot_temp(func_type const & func, data_type & data) const noexcept
     268              : {
     269          610 :     std::for_each (m_valuemap.begin(), m_valuemap.end(),
     270         4118 :         [&func,&data](const KvpFrameImpl::map_type::value_type & a)
     271              :         {
     272         2059 :             func (a.first, a.second, data);
     273              :         }
     274              :     );
     275          610 : }
     276              : 
     277              : int compare (const KvpFrameImpl &, const KvpFrameImpl &) noexcept;
     278              : int compare (const KvpFrameImpl *, const KvpFrameImpl *) noexcept;
     279              : /** @} Doxygen Group */
     280              : 
     281              : #endif
        

Generated by: LCOV version 2.0-1