ALib C++ Library
Library Version: 2402 R1
Documentation generated by doxygen
Loading...
Searching...
No Matches
propertyformatter.hpp
Go to the documentation of this file.
1/** ************************************************************************************************
2 * \file
3 * This header file is part of sub-namespace #alib::lang::format of module \alib_basecamp of
4 * the \aliblong.
5 *
6 * \emoji :copyright: 2013-2024 A-Worx GmbH, Germany.
7 * Published under \ref mainpage_license "Boost Software License".
8 **************************************************************************************************/
9#ifndef HPP_ALIB_LANG_FORMAT_PROPERTY_FORMATTER
10#define HPP_ALIB_LANG_FORMAT_PROPERTY_FORMATTER
11
12#if !defined (HPP_ALIB_LANG_FORMAT_FORMATTER_STD)
14#endif
15
16#if !defined (_GLIBCXX_VECTOR) && !defined(_VECTOR_)
17 #include <vector>
18#endif
19
20namespace alib { namespace lang::format {
21
22
23/** ************************************************************************************************
24 * # Introduction # {#alib_ns_strings_propertyformatter_overview}
25 *
26 * This class can be used to offer customized format strings to end users. For example, when
27 * it should be allowed to end-users to store output format strings of complex type.
28 *
29 * For this, the format string definition of \alib \alib{lang::format;Formatter}
30 * types is extended to support identifiers,
31 * which act as placeholders for object values. The identifiers placed in the string have to match
32 * to the normal formatting placeholders in respect to their total number and value type.
33 *
34 * \note
35 * In other words: This class allows not only to have custom format strings which consume a
36 * list of data objects that is hard-coded, but also to have the replacement data objects defined
37 * by the end user.
38 *
39 * In the constructor of this class, a custom format string which whose syntax is based on
40 * \ref alib_basecamp_format_intro "standard ALib format strings" is passed.
41 * However, before using the format string, it is processed as follows:
42 * - It is searched for <em>"custom identifiers"</em> within the format string.
43 * - For each identifier found, a reference to an associated callback function is stored.
44 * - The <em>"custom identifier"</em> found is removed from the format string.
45 *
46 * It could be said, that the format string given "is compiled" with the constructor of this class.
47 *
48 * The only interface method #Format then accepts a
49 * \ref alib::boxing "boxed object" of custom type that is used to collect the format string's
50 * placeholder data, by using the stored callback functions.
51 *
52 * The identifiers are denoted by prefixing them with (customizable) character <c>'@'</c>.
53 *
54 * A vector of objects of inner type \b IdentifierEntry needs to be defined and passed to the
55 * constructor of this class.
56 * Field #IdentifierEntry::Name contains the \e 'identifier' string which is removed from the
57 * format string if found. Field #IdentifierEntry::Callback has to point to a callback function
58 * used to retrieve the data when the identifier is found in the format string.
59 *
60 * The whole approach is useful in cases where an application wants to allow a customizable output
61 * format of data objects.
62 *
63 * # Sample # {#alib_ns_strings_propertyformatter_sample}
64 * Let us assume, an application uses an enumeration and a simple struct:
65 * \snippet "DOX_ALIB_STRINGS.cpp" DOX_ALIB_STRINGS_PROPERTY_FORMATTER_1
66 *
67 * In the application's configuration file, a custom output format for objects of type \b Person
68 * should be user defined. We document to the user that she can use Python-style or
69 * Java-style output formats - extended by the ability to place the following three identifiers in
70 * the string:
71 * - \@name
72 * - \@age
73 * - \@hobby.
74 *
75 * A possible format string then would be:
76 *
77 * "{@name} is aged {@age} and his/her hobby is {@hobby}"
78 *
79 * \note
80 * The custom identifiers might appear anywhere in the format string. But as sampled above,
81 * placing them in directly in the formatter replacement fields, makes the string very well
82 * readable. Also, it is important to have exactly one identifier for each replacement field,
83 * which this notation of-course supports well.
84 *
85 * To tell this class how to retrieve the replacement values, we need to define three callback
86 * functions:
87 * \snippet "DOX_ALIB_STRINGS.cpp" DOX_ALIB_STRINGS_PROPERTY_FORMATTER_2
88 * It is a good idea to place the callback functions in an anonymous (sub-) namespace as they
89 * are only referred to once (in the next step).<br>
90 * As this sample is very trivial, the second parameter <c>AString&</c> is not used and named.
91 * Details on the purpose and use of this parameter is addressed
92 * \ref alib_ns_strings_propertyformatter_callbacks "below".
93 *
94 * As a next step, these functions need to be collected together in a "translation table".
95 * The table primarily holds a string denoting the replacement identifier and a pointer to the
96 * corresponding callback function. For convenience, the table type is provided with <c>using</c>
97 * definition #TCallbackTable.
98 *
99 * In our sample, the definition of the table looks like this:
100 * \snippet "DOX_ALIB_STRINGS.cpp" DOX_ALIB_STRINGS_PROPERTY_FORMATTER_3
101 *
102 * This is all we need! Of-course, an external declaration of our table \b PersonCallbacks
103 * should be placed somewhere in the project's header file. With this, a code like this may now use
104 * the custom formatter strings:
105 * \snippet "DOX_ALIB_STRINGS.cpp" DOX_ALIB_STRINGS_PROPERTY_FORMATTER_4
106 *
107 * Running the code above produces the following output:
108 * \verbinclude "DOX_ALIB_STRINGS_PROPERTY_FORMATTER.txt"
109 *
110 * Serializing objects in a custom format (e.g. a user could define its own \b JSon object output)
111 * should be the most obvious and frequent use case. And this is what the classes name suggests.
112 * Nevertheless, the callback methods might be more complex than just returning "properties" of
113 * objects. The next sections gives more details on the custom callbacks.
114 *
115 * \I{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
116 * # Callback Functions # {#alib_ns_strings_propertyformatter_callbacks}
117 * The call back function's signature is defined with <c>using</c>-statement #TCallback which
118 * evaluates to
119 *
120 * Box (*)(const Box&, AString&)
121 *
122 * The return type is \ref alib::boxing::Box "Box", which allows the callback function to
123 * return objects of arbitrary type, respectively all types that are are equipped to be used
124 * with \alib formatters.
125 *
126 * The first input parameter provides the data object passed to method #Format.
127 * If the callback functions are dedicated to a property formatter that receives a certain
128 * object type (what they usually are), the custom type can be straightly unboxed.
129 *
130 * \note
131 * The name of the class, as well as the sample above, indicate that the data objects are
132 * simple objects with rather simple "properties" to read. While this is the usual use case,
133 * a user of this class might pass more complex objects, or even the root object of the
134 * application to the formatter. Then, the callback function may retrieve (or calculate)
135 * any data that the application provides.
136 *
137 * The second parameter is an \b AString object which optionally can be used to assemble
138 * string objects in more complex callback methods. Note, that if this string is not
139 * empty after the callback invocation, the result is copied to a string buffer allocated
140 * in the heap memory. In this case, the box object returned by the callback is ignored
141 * and instead the copy of the string is passed to the formatter. If C++ string constants
142 * (e.g <c>"true"</c> are to be returned by the callbacks, those constants do not need
143 * to be copied to the buffer, as their memory is statically allocated. In other words,
144 * the \b %AString buffer provided in the second parameter is needed to be used
145 * only in cases that a string gets assembled in the callback function!
146 *
147 * \I{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
148 * # Identifiers And The Escape Character '@' # {#alib_ns_strings_propertyformatter_escape}
149 * The escape character used to find custom identifiers in the format string is defined
150 * with parameter \p{ESCCharacter} of the constructor and defaults to \b '\@'.
151 * This escape character is searched in the format string. If found, the identifier is
152 * read by consuming alphabetic characters. Optionally, the end of a custom identifier can be
153 * marked with an additional escape character.
154 * Hence, the two format strings
155 *
156 * "{@name}"
157 * "{@name@}"
158 * are both valid and equal.<br>
159 *
160 * In case of
161 * \alib{lang::format;FormatterPythonStyle;FormatterPythonStyle} format strings,
162 * it is a good idea to place the identifier right inside the brackets. It just looks very intuitive.
163 * However, these versions:
164 *
165 * "@name{}"
166 * "{}@name"
167 * are also valid custom format strings.
168 *
169 * In case of
170 * \alib{lang::format;FormatterJavaStyle;FormatterJavaStyle}, which uses \b '\%'
171 * as its escape character, we consider the best option to put the custom identifier in front
172 * of each \b '\%' symbol. The string of the example given above would then look like this:
173 *
174 * "@name%s is aged @age%d and his/her hobby is @hobby%s"
175 * Doing it this way, the \b '\%' symbol acts as a very natural delimiter for the custom identifier.
176 *
177 * Furthermore, field #IdentifierEntry::MinimumRecognitionLength allows abbreviations of identifier
178 * names. It denotes the minimum number of characters to be* matched. As in the above sample a
179 * value of \c 1 is provided, each identifier of the custom format string can be abbreviated down
180 * to one character. Consequently the following format string samples are all equal and allowed:
181 *
182 * "{@name}"
183 * "{@nam}"
184 * "{@na}"
185 * "{@n}"
186 *
187 * Finally, to insert the escape character itself into the format string, it has to be doubly
188 * inserted (<c>"@@"</c>).
189 *
190 * \I{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
191 * # Alternative # {#alib_ns_strings_propertyformatter_alternative}
192 * If \alib_expressions is compiled with the library, a utility class similar to this one
193 * is available with \alib{expressions::util;ExpressionFormatter}.
194 * With this, complex expressions and calculations might be used instead of only having simple
195 * identifiers as replacements.
196 *
197 * \I{#############################################################################################}
198 * # Reference Documentation #
199 * @throws #alib::lang::format::FMTExceptions::UnknownPropertyInFormatString
200 **************************************************************************************************/
202{
203 public:
204 /**
205 * The signature of the callback functions.
206 * See \ref alib_ns_strings_propertyformatter_callbacks for more information.
207 */
208 using TCallback= Box (*)(const Box&, AString&);
209
210 /**
211 * The entry type of translation table.
212 */
214 {
215 /** The identifier string to search in the original format string. */
217
218 /** The minimum characters of the identifier to read in the format string.
219 * If less or equal to zero, abbreviations are not allowed. */
221
222 /** The callback function for this identifier.
223 * See \ref alib_ns_strings_propertyformatter_callbacks. */
225 };
226
227 /** Type definition of the callback table. */
228 using TCallbackTable= std::vector<IdentifierEntry>;
229
230
231 protected:
232 /**
233 * Internal type definition for the list of callback table entries collected in the
234 * constructor by parsing the extended format string.
235 */
236 using TCallbackResultTable= std::vector<const IdentifierEntry*>;
237
238 /** The formatter (as given in the constructor). */
240
241 /** The original format string. Used only for exception information. */
243
244 /** The resulting format string passed to \alib formatters. */
246
247 /** The callback functions to receive the format data. */
249
250 public:
251
252 /** ****************************************************************************************
253 * Constructor. Processes the given format string and builds internal structures
254 * which are then used with invocations of method #Format.
255 *
256 * @param customFormatString The format string as described in the class documentation.
257 * @param propertyTable Table with property identifier names and callback functions to
258 * retrieve the property values.
259 * @param formatter The formatter to use. Defaults to \c nullptr which selects
260 * \alib{lang::format;Formatter::GetDefault;default formatter}.
261 * @param ESCCharacter The prefix used to search identifiers in \p{customFormatString}.
262 * Defaults to <c>'@'</c>.
263 *
264 * @throws #alib::lang::format::FMTExceptions::UnknownPropertyInFormatString
265 ******************************************************************************************/
267 PropertyFormatter( const String customFormatString,
268 const TCallbackTable& propertyTable,
269 SPFormatter formatter = nullptr,
270 character ESCCharacter = '@' );
271
272 /** ****************************************************************************************
273 * Writes the formatted output of the properties of the given \p{TFormattable} object
274 * to a given \p{target} string.
275 *
276 * @param target The target string to write into.
277 * @param src The custom object which is passed to the callback methods to collect
278 * the formatter arguments.
279 ******************************************************************************************/
281 void Format( AString& target, const Box& src );
282}; // class PropertyFormatter
283
284} // namespace alib[:: lang::format]
285
286/// Type alias in namespace \b alib.
288
289} // namespace [alib]
290
291
292#endif // HPP_ALIB_LANG_FORMAT_PROPERTY_FORMATTER
std::vector< IdentifierEntry > TCallbackTable
Box(*)(const Box &, AString &) TCallback
std::vector< const IdentifierEntry * > TCallbackResultTable
#define ALIB_API
Definition alib.hpp:538
Definition alib.cpp:57
lang::format::PropertyFormatter PropertyFormatter
Type alias in namespace alib.
strings::TFormat< character > Format
Type alias in namespace alib.
std::shared_ptr< lang::format::Formatter > SPFormatter
characters::character character
Type alias in namespace alib.
boxing::Box Box
Type alias in namespace alib.