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
|