From 4623cff38d4619374d749cca51096eb0e3aa55fb Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 3 Jul 2015 16:14:00 -0700 Subject: [PATCH] Document new KVP API in Doxygen, including relevant bits from src/docs/design. --- src/engine/kvp_doc.txt | 40 +++++++++--------- src/libqof/qof/kvp-value.hpp | 15 +++++-- src/libqof/qof/kvp_frame.hpp | 75 ++++++++++++++++++++++++++-------- src/libqof/qof/qofbook.h | 5 ++- src/libqof/qof/qofinstance-p.h | 28 +++++++++++-- src/libqof/qof/qofinstance.h | 6 --- 6 files changed, 119 insertions(+), 50 deletions(-) diff --git a/src/engine/kvp_doc.txt b/src/engine/kvp_doc.txt index f39f53f0b2..e9f6717c71 100644 --- a/src/engine/kvp_doc.txt +++ b/src/engine/kvp_doc.txt @@ -6,7 +6,7 @@ API: \ref KVP This file documents the use of keys in the key-value pair system used by the GnuCash Application (both the engine, and non-engine, GUI -pieces). Before assigning keys for use, please read the Key-Value +pieces). Before assigning keys for use, please read the Key-Value Policy in the GnuCash Design Document located under src/doc/design. The format of the data below is: @@ -16,7 +16,7 @@ Name: The name of the key, including key names of parent frames filename. Use the '/' character to separate keys. Type: The type of value stored in the key. The types are listed in - 'kvp_frame.h'. + 'kvp-value.hpp'. Entities: Which engine entities (Accounts, Transactions, Splits) can use this key. Use 'All' if every entity could have @@ -24,7 +24,7 @@ Entities: Which engine entities (Accounts, Transactions, Splits) Use: The use to which the key will be put. Include any requirements for using the key here. Also include any API calls which use - the key. If more than one entity can use the key, + the key. If more than one entity can use the key, Example: @@ -44,7 +44,7 @@ Please put the keys in alphabetical order. [ \ref kvpA ] [ \ref kvpB ] [ \ref kvpC ] [ \ref kvpD ] [ \ref kvpE ] [ \ref kvpF ] [ \ref kvpG ] [ \ref kvpH ] [ \ref kvpJ ] [ \ref kvpK ] [ \ref kvpL ]\n -[ \ref kvpM ] [ \ref kvpN ] [ \ref kvpO ] [ \ref kvpP ] [ \ref kvpQ ] +[ \ref kvpM ] [ \ref kvpN ] [ \ref kvpO ] [ \ref kvpP ] [ \ref kvpQ ] [ \ref kvpR ] [ \ref kvpS ] [ \ref kvpT ] [ \ref kvpU ] [ \ref kvpV ] [ \ref kvpW ] \subsection kvpA A @@ -62,7 +62,7 @@ Use: kvp subdirectory holding info relating to accounting periods, including \verbatim Name: /book/accounting-period -Type: string, enum {none, week, month, quarter, trimester, year} +Type: string, enum {none, week, month, quarter, trimester, year} XXX not used, should be UIFreqSpec stuff .. Entities: Book Use: An enumerated identifier indicating how often books are supposed @@ -74,7 +74,7 @@ Use: An enumerated identifier indicating how often books are supposed Name: /book/close-date Type: Timespec Entities: Book -Use: The posted closing date of this book. This book only contains +Use: The posted closing date of this book. This book only contains transactions whose posted date is earlier than this closing date. \endverbatim @@ -82,7 +82,7 @@ Use: The posted closing date of this book. This book only contains Name: /book/closed-acct Type: GUID Entities: Transaction -Use: The GUID of the account for which this transaction represents the +Use: The GUID of the account for which this transaction represents the opening balance. This value will occur *only* in transactions that are opening balances. \endverbatim @@ -91,7 +91,7 @@ Use: The GUID of the account for which this transaction represents the Name: /book/closed-book Type: GUID Entities: Transaction -Use: The GUID of the book for which this transaction represents the +Use: The GUID of the book for which this transaction represents the opening balance. This value will occur *only* in transactions that are opening balances. \endverbatim @@ -173,7 +173,7 @@ Entities: Book Use: Holders for a bunch of counters for various types. Used specifically in the business objects for ID counters. The counter name is the path that follows /counters/, e.g. "/counters/GncCustomer" -\endverbatim +\endverbatim \verbatim Name: /counter_formats/... @@ -224,7 +224,7 @@ Use: GUID of the split that records the capital gains for this split. Name: /gemini/ Type: kvp_glist Entities: Account, Book -Use: kvp bag holding frames that identify accounts or books +Use: kvp bag holding frames that identify accounts or books that are copies of this account. \endverbatim @@ -247,7 +247,7 @@ Use: guid of another account that is a copy of this one. Name: /gemini//book_guid Type: guid Entities: Account, Book -Use: When this appears in an account, then it contains the guid of +Use: When this appears in an account, then it contains the guid of the book that the other account belongs to. When this appears in a book, then this is the guid of the other book. \endverbatim @@ -304,7 +304,7 @@ Name: /hbci/config-filename Type: string Entitied: Book Use: OpenHBCI configuration file name, where the real HBCI -configuration for the OpenHBCI library can be found +configuration for the OpenHBCI library can be found \endverbatim \subsection kvpJ J @@ -328,15 +328,15 @@ Type: kvp_frame Entities: Account Use: Frame holding info regarding how lots should be managed in this account, including what accounting policy to use, where realized - gains should be reported, and etc. + gains should be reported, and etc. \endverbatim \verbatim Name: /lot-mgmt/gains-acct/ Type: frame Entities: Account -Use: When a lot in this account is double-balanced, this frame - holds per-currency accounts to which realized gains are to +Use: When a lot in this account is double-balanced, this frame + holds per-currency accounts to which realized gains are to be posted. \endverbatim @@ -361,7 +361,7 @@ Name: /lot-split/ Type: kvp_glist Entities: Split Use: A bag of kvp frames holding identification of splits - that were split off of this split. Same style as the + that were split off of this split. Same style as the /gemini/, look there for additional doco's. \endverbatim @@ -380,7 +380,7 @@ Use: The GUID of the peer split which was split from this split. Name: /notes Type: string Entities: Account, Lot, Transaction -Use: A user-suplied 'Notes' text field. The user can set this to +Use: A user-suplied 'Notes' text field. The user can set this to any value and change it at any time for any reason. Accessors: xaccAccountGetNotes(), xaccAccountSetNotes(), xaccTransGetNotes(), xaccTransSetNotes() @@ -533,7 +533,7 @@ Use: Store the formula for the credit side of the split Name: /sched-xaction/debit-formula Type: string Entities: Split associated with a SchedXaction -Use: Formula for the debit. Either the credit or the +Use: Formula for the debit. Either the credit or the debit formula must be empty. \endverbatim @@ -571,7 +571,7 @@ Use: A boolean flag indicated whether the Account is tax-related. Name: /title Type: string Entities: Lot -Use: A user-supplied title for the lot. The user can set this to +Use: A user-supplied title for the lot. The user can set this to any value and change it at any time for any reason. \endverbatim @@ -606,7 +606,7 @@ Use: This frame is used to store keys which are editable directly by Name: void-reason Type: string Entities: Transaction -Use: This string is used to store the reason why a transaction has been +Use: This string is used to store the reason why a transaction has been voided. Note that it should only exist if the transaction has been voided. \endverbatim diff --git a/src/libqof/qof/kvp-value.hpp b/src/libqof/qof/kvp-value.hpp index 350e9ab5f7..e97fb367f8 100644 --- a/src/libqof/qof/kvp-value.hpp +++ b/src/libqof/qof/kvp-value.hpp @@ -37,6 +37,12 @@ extern "C" //Must be a struct because it's exposed to C so that it can in turn be //translated to/from Scheme. +/** @addtogroup KVP + * @{ + */ + +/** Implements KvpValue using boost::variant. + */ struct KvpValueImpl { public: @@ -134,7 +140,7 @@ struct KvpValueImpl int compare(const KvpValueImpl *, const KvpValue *) noexcept; - +/** @} Close Doxygen AddToGroup */ template KvpValueImpl::KvpValueImpl(T newvalue) noexcept: datastore(newvalue) @@ -153,7 +159,9 @@ KvpValueImpl::set(T val) noexcept { this->datastore = val; } - +/** @ingroup KVP + @{ */ +/** @internal @{ */ /** Convert a kvp_value into a GValue. Frames aren't converted. * @param kval: A KvpValue. * @return GValue*. Must be freed with g_free(). @@ -172,7 +180,8 @@ KvpValue* kvp_value_from_gvalue (const GValue *gval); * \param value: A GValue* created by kvp_frame_get_gvalue */ void gnc_gvalue_free (GValue *value); - +/** @} Close Doxygen Internal */ +/** @} Close Doxygen Group */ extern "C" GType gnc_value_list_get_type (void); #define GNC_TYPE_VALUE_LIST (gnc_value_list_get_type ()) diff --git a/src/libqof/qof/kvp_frame.hpp b/src/libqof/qof/kvp_frame.hpp index 5aae496b53..f765a6dc87 100644 --- a/src/libqof/qof/kvp_frame.hpp +++ b/src/libqof/qof/kvp_frame.hpp @@ -39,6 +39,35 @@ * passed as either '/'-delimited strings or as std::vectors of keys. Unlike * file system paths, the tokens '.' and '..' have no special meaning. * + * KVP is an implementation detail whose direct use should be avoided; create an + * abstraction object in libqof to keep KVP encapsulated here and ensure that + * KVP modifications are written to the database. Two generic abstractions are + * provided: + * + * * @ref qof_instance_set_kvp and @ref qof_instance_get_kvp provide single-item + access via GValues to support object properties. + + * * @ref qof_book_set_option and @ref qof_book_get_option provide similar + access for book options. + + * + * @ref kvpvalues provides a catolog of KVP entries including what objects + * they're part of and how they're used. + * + * ## Purpose + * KVP is used to extend the class structure without directly reflecting the extension in the database or xML schema. The backend will directly load and store KVP slots without any checking, which allows older versions of GnuCash to load the database without complaint and without damaging the KVP data that they don't understand. + * + * When a feature is entirely implemented in KVP and doesn't affect the meaning of the books or other features, this isn't a problem, but when it's not true then it should be registered in @ref UtilFeature so that older versions of GnuCash will refuse to load the database. + * + * ## Policy + * * Document every KVP slot in src/engine/kvp_doc.txt so that it is presented + * in @ref kvpvalues. + * * Register a feature in @ref UtilFeature if the use of the KVP in any way affects the books or business computations. + * * Key strings should be all lower case with '-', not spaces, separating words and '/' separating frames. Prefer longer and more descriptive names to abbreviations, and define a global const char[] to the key or path string so that the compiler will catch any typos. + * * Make good use of the hierarchical nature of KVP by using frames to group related slots. + * * Don't use the KVP API directly outside of libqof, and prefer to use the QofInstance and QofBook functions whenever feasible. If those functions aren't feasible write a class in libqof to abstract the use of KVP. + * * Avoid re-using key names in different contexts (e.g. Transactions and Splits) unless the slot is used for the same purpose in both. + * @{ */ #ifndef GNC_KVP_FRAME_TYPE @@ -49,23 +78,33 @@ #include #include #include - -class cstring_comparer -{ - public: - /* Returns true if one is less than two. */ - bool operator()(const char * one, const char * two) const - { - auto ret = std::strcmp(one, two) < 0; - return ret; - } -}; - using Path = std::vector; +/** Implements KvpFrame. + * It's a struct because QofInstance needs to use the typename to declare a + * KvpFrame* member, and QofInstance's API is C until its children are all + * rewritten in C++. + * + * N.B.** Writes to KvpFrames must** be wrapped in BeginEdit and Commit + * for the containing QofInstance and the QofInstance must be marked dirty. This + * is not** done by the KvpFrame API. In general Kvp items should be + * accessed using either QofInstance or QofBook methods in order to ensure that + * this is done. + * @{ + */ struct KvpFrameImpl { - typedef std::map map_type; + class cstring_comparer + { + public: + /* Returns true if one is less than two. */ + bool operator()(const char * one, const char * two) const + { + auto ret = std::strcmp(one, two) < 0; + return ret; + } + }; + using map_type = std::map; public: KvpFrameImpl() noexcept {}; @@ -98,8 +137,8 @@ struct KvpFrameImpl * Set the value with the key in a subframe following the keys in path, * replacing and returning the old value if it exists or nullptr if it * doesn't. Creates any missing intermediate frames. - * @param path: The path of subframes as a '/'-delimited string leading to the frame in which to - * insert/replace. + * @param path: The path of subframes as a '/'-delimited string leading to + * the frame in which to insert/replace. * @param newvalue: The value to set at key. * @return The old value if there was one or nullptr. */ @@ -112,8 +151,8 @@ struct KvpFrameImpl * Set the value with the key in a subframe following the keys in path, * replacing and returning the old value if it exists or nullptr if it * doesn't. Creates any missing intermediate frames. - * @param path: The path of subframes as a std::vector leading to the frame in which to - * insert/replace. + * @param path: The path of subframes as a std::vector leading to the + * frame in which to insert/replace. * @param newvalue: The value to set at key. * @return The old value if there was one or nullptr. */ @@ -158,4 +197,6 @@ struct KvpFrameImpl int compare (const KvpFrameImpl &, const KvpFrameImpl &) noexcept; int compare (const KvpFrameImpl *, const KvpFrameImpl *) noexcept; +/** @} Doxygen Group */ + #endif diff --git a/src/libqof/qof/qofbook.h b/src/libqof/qof/qofbook.h index 04448f76ab..50750ce907 100644 --- a/src/libqof/qof/qofbook.h +++ b/src/libqof/qof/qofbook.h @@ -354,6 +354,9 @@ void qof_book_begin_edit(QofBook *book); void qof_book_commit_edit(QofBook *book); /* Access functions for options. */ +/** @ingroup KVP + @{ + */ /** Load a GNCOptionsDB from KVP data. * @param book: The book. * @param load_cb: A callback function that does the loading. @@ -394,7 +397,7 @@ KvpValue* qof_book_get_option (QofBook *book, GSList *path); * @param list: A GList of keys which from a path under KVP_OPTION_PATH. */ void qof_book_options_delete (QofBook *book); - +/** @} End of Doxygen Include */ /** deprecated */ #define qof_book_get_guid(X) qof_entity_get_guid (QOF_INSTANCE(X)) diff --git a/src/libqof/qof/qofinstance-p.h b/src/libqof/qof/qofinstance-p.h index 16eeee0cce..0c0d4fd1bb 100644 --- a/src/libqof/qof/qofinstance-p.h +++ b/src/libqof/qof/qofinstance-p.h @@ -106,11 +106,33 @@ void qof_instance_set_version_check (gpointer inst, guint32 value); void qof_instance_copy_version_check (gpointer to, gconstpointer from); void qof_instance_set_idata(gpointer inst, guint32 idata); /* Convenience functions to save some typing in property handlers */ +/** @ingroup KVP + * @{ */ +/** Report whether a QofInstance has anything stored in KVP + * @param inst The QofInstance + * @return TRUE if Kvp isn't empty. + */ gboolean qof_instance_has_kvp (QofInstance *inst); +/** Sets a KVP slot to a value from a GValue. The key can be a '/'-delimited + * path, and intermediate container frames will be created if necessary. + * Commits the change to the QofInstance. + * @param inst: The QofInstance on which to set the value. + * @param key: The key for the slot or '/'-delimited path + * @param value: A GValue containing an item of a type which KvpValue knows + * how to store. + */ void qof_instance_set_kvp (QofInstance *inst, const gchar *key, const GValue *value); -void qof_instance_get_kvp (const QofInstance *inst, const gchar *key, GValue *value); -/* Functions to isolate the KVP mechanism inside QOF for cases where GValue - * operations won't work. +/** Retrieves the contents of a KVP slot into a provided GValue. + * @param inst: The QofInstance + * @param key: The key of or '/'-delimited path to the slot. + * @param value: A GValue into which to store the value of the slot. It will be + * set to the correct type. + */ +void qof_instance_get_kvp (const QofInstance *inst, const gchar *key, GValue +*value); +/** @} Close out the DOxygen ingroup */ +/* Functions to isolate the KVP mechanism inside QOF for cases where +GValue * operations won't work. */ void qof_instance_copy_kvp (QofInstance *to, const QofInstance *from); void qof_instance_swap_kvp (QofInstance *a, QofInstance *b); diff --git a/src/libqof/qof/qofinstance.h b/src/libqof/qof/qofinstance.h index 98b854f0c3..df4f38292e 100644 --- a/src/libqof/qof/qofinstance.h +++ b/src/libqof/qof/qofinstance.h @@ -66,13 +66,7 @@ typedef struct KvpFrameImpl KvpFrame; struct QofInstance_s { GObject object; - QofIdType e_type; /**< Entity type */ - - /* kvp_data is a key-value pair database for storing arbirtary - * information associated with this instance. - * See src/engine/kvp_doc.txt for a list and description of the - * important keys. */ KvpFrame *kvp_data; };