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