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: 2025-02-07 16:25:45 Functions: 68.4 % 19 13
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     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                 :      580314 :         bool operator()(const char * one, const char * two) const
     116                 :             :             {
     117                 :      580314 :                 auto ret = std::strcmp(one, two) < 0;
     118                 :      580314 :                 return ret;
     119                 :             :             }
     120                 :             :     };
     121                 :             :     using map_type = std::map<const char *, KvpValue*, cstring_comparer>;
     122                 :             : 
     123                 :             :     public:
     124                 :       65899 :     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                 :        2705 :     bool empty() const noexcept { return m_valuemap.empty(); }
     227                 :             :     friend int compare(const KvpFrameImpl&, const KvpFrameImpl&) noexcept;
     228                 :             : 
     229                 :          17 :     map_type::iterator begin() { return m_valuemap.begin(); }
     230                 :          17 :     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                 :         596 : void KvpFrame::for_each_slot_temp(func_type const & func, data_type & data) const noexcept
     268                 :             : {
     269                 :         596 :     std::for_each (m_valuemap.begin(), m_valuemap.end(),
     270                 :        4098 :         [&func,&data](const KvpFrameImpl::map_type::value_type & a)
     271                 :             :         {
     272                 :        2049 :             func (a.first, a.second, data);
     273                 :             :         }
     274                 :             :     );
     275                 :         596 : }
     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