1*11be35a1SLionel Sambuc // Copyright 2010 Google Inc.
2*11be35a1SLionel Sambuc // All rights reserved.
3*11be35a1SLionel Sambuc //
4*11be35a1SLionel Sambuc // Redistribution and use in source and binary forms, with or without
5*11be35a1SLionel Sambuc // modification, are permitted provided that the following conditions are
6*11be35a1SLionel Sambuc // met:
7*11be35a1SLionel Sambuc //
8*11be35a1SLionel Sambuc // * Redistributions of source code must retain the above copyright
9*11be35a1SLionel Sambuc // notice, this list of conditions and the following disclaimer.
10*11be35a1SLionel Sambuc // * Redistributions in binary form must reproduce the above copyright
11*11be35a1SLionel Sambuc // notice, this list of conditions and the following disclaimer in the
12*11be35a1SLionel Sambuc // documentation and/or other materials provided with the distribution.
13*11be35a1SLionel Sambuc // * Neither the name of Google Inc. nor the names of its contributors
14*11be35a1SLionel Sambuc // may be used to endorse or promote products derived from this software
15*11be35a1SLionel Sambuc // without specific prior written permission.
16*11be35a1SLionel Sambuc //
17*11be35a1SLionel Sambuc // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18*11be35a1SLionel Sambuc // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19*11be35a1SLionel Sambuc // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20*11be35a1SLionel Sambuc // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21*11be35a1SLionel Sambuc // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22*11be35a1SLionel Sambuc // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23*11be35a1SLionel Sambuc // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24*11be35a1SLionel Sambuc // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25*11be35a1SLionel Sambuc // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*11be35a1SLionel Sambuc // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*11be35a1SLionel Sambuc // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*11be35a1SLionel Sambuc
29*11be35a1SLionel Sambuc #include "utils/format/formatter.hpp"
30*11be35a1SLionel Sambuc
31*11be35a1SLionel Sambuc #include <memory>
32*11be35a1SLionel Sambuc #include <string>
33*11be35a1SLionel Sambuc #include <utility>
34*11be35a1SLionel Sambuc
35*11be35a1SLionel Sambuc #include "utils/format/exceptions.hpp"
36*11be35a1SLionel Sambuc #include "utils/sanity.hpp"
37*11be35a1SLionel Sambuc #include "utils/text/exceptions.hpp"
38*11be35a1SLionel Sambuc #include "utils/text/operations.ipp"
39*11be35a1SLionel Sambuc
40*11be35a1SLionel Sambuc namespace format = utils::format;
41*11be35a1SLionel Sambuc namespace text = utils::text;
42*11be35a1SLionel Sambuc
43*11be35a1SLionel Sambuc
44*11be35a1SLionel Sambuc namespace {
45*11be35a1SLionel Sambuc
46*11be35a1SLionel Sambuc
47*11be35a1SLionel Sambuc /// Finds the next placeholder in a string.
48*11be35a1SLionel Sambuc ///
49*11be35a1SLionel Sambuc /// \param format The original format string provided by the user; needed for
50*11be35a1SLionel Sambuc /// error reporting purposes only.
51*11be35a1SLionel Sambuc /// \param expansion The string containing the placeholder to look for. Any
52*11be35a1SLionel Sambuc /// '%%' in the string will be skipped, and they must be stripped later by
53*11be35a1SLionel Sambuc /// strip_double_percent().
54*11be35a1SLionel Sambuc /// \param begin The position from which to start looking for the next
55*11be35a1SLionel Sambuc /// placeholder.
56*11be35a1SLionel Sambuc ///
57*11be35a1SLionel Sambuc /// \return The position in the string in which the placeholder is located and
58*11be35a1SLionel Sambuc /// the placeholder itself. If there are no placeholders left, this returns
59*11be35a1SLionel Sambuc /// the length of the string and an empty string.
60*11be35a1SLionel Sambuc ///
61*11be35a1SLionel Sambuc /// \throw bad_format_error If the input string contains a trailing formatting
62*11be35a1SLionel Sambuc /// character. We cannot detect any other kind of invalid formatter because
63*11be35a1SLionel Sambuc /// we do not implement a full parser for them.
64*11be35a1SLionel Sambuc static std::pair< std::string::size_type, std::string >
find_next_placeholder(const std::string & format,const std::string & expansion,std::string::size_type begin)65*11be35a1SLionel Sambuc find_next_placeholder(const std::string& format,
66*11be35a1SLionel Sambuc const std::string& expansion,
67*11be35a1SLionel Sambuc std::string::size_type begin)
68*11be35a1SLionel Sambuc {
69*11be35a1SLionel Sambuc begin = expansion.find('%', begin);
70*11be35a1SLionel Sambuc while (begin != std::string::npos && expansion[begin + 1] == '%')
71*11be35a1SLionel Sambuc begin = expansion.find('%', begin + 2);
72*11be35a1SLionel Sambuc if (begin == std::string::npos)
73*11be35a1SLionel Sambuc return std::make_pair(expansion.length(), "");
74*11be35a1SLionel Sambuc if (begin == expansion.length() - 1)
75*11be35a1SLionel Sambuc throw format::bad_format_error(format, "Trailing %");
76*11be35a1SLionel Sambuc
77*11be35a1SLionel Sambuc std::string::size_type end = begin + 1;
78*11be35a1SLionel Sambuc while (end < expansion.length() && expansion[end] != 's')
79*11be35a1SLionel Sambuc end++;
80*11be35a1SLionel Sambuc const std::string placeholder = expansion.substr(begin, end - begin + 1);
81*11be35a1SLionel Sambuc if (end == expansion.length() ||
82*11be35a1SLionel Sambuc placeholder.find('%', 1) != std::string::npos)
83*11be35a1SLionel Sambuc throw format::bad_format_error(format, "Unterminated placeholder '" +
84*11be35a1SLionel Sambuc placeholder + "'");
85*11be35a1SLionel Sambuc return std::make_pair(begin, placeholder);
86*11be35a1SLionel Sambuc }
87*11be35a1SLionel Sambuc
88*11be35a1SLionel Sambuc
89*11be35a1SLionel Sambuc /// Converts a string to an integer.
90*11be35a1SLionel Sambuc ///
91*11be35a1SLionel Sambuc /// \param format The format string; for error reporting purposes only.
92*11be35a1SLionel Sambuc /// \param str The string to conver.
93*11be35a1SLionel Sambuc /// \param what The name of the field this integer belongs to; for error
94*11be35a1SLionel Sambuc /// reporting purposes only.
95*11be35a1SLionel Sambuc ///
96*11be35a1SLionel Sambuc /// \return An integer representing the input string.
97*11be35a1SLionel Sambuc inline int
to_int(const std::string & format,const std::string & str,const char * what)98*11be35a1SLionel Sambuc to_int(const std::string& format, const std::string& str, const char* what)
99*11be35a1SLionel Sambuc {
100*11be35a1SLionel Sambuc try {
101*11be35a1SLionel Sambuc return text::to_type< int >(str);
102*11be35a1SLionel Sambuc } catch (const text::value_error& e) {
103*11be35a1SLionel Sambuc throw format::bad_format_error(format, "Invalid " + std::string(what) +
104*11be35a1SLionel Sambuc "specifier");
105*11be35a1SLionel Sambuc }
106*11be35a1SLionel Sambuc }
107*11be35a1SLionel Sambuc
108*11be35a1SLionel Sambuc
109*11be35a1SLionel Sambuc /// Constructs an std::ostringstream based on a formatting placeholder.
110*11be35a1SLionel Sambuc ///
111*11be35a1SLionel Sambuc /// \param format The format placeholder; may be empty.
112*11be35a1SLionel Sambuc ///
113*11be35a1SLionel Sambuc /// \return A new std::ostringstream that is prepared to format a single
114*11be35a1SLionel Sambuc /// object in the manner specified by the format placeholder.
115*11be35a1SLionel Sambuc ///
116*11be35a1SLionel Sambuc /// \throw bad_format_error If the format string is bad. We do minimal
117*11be35a1SLionel Sambuc /// validation on this string though.
118*11be35a1SLionel Sambuc static std::ostringstream*
new_ostringstream(const std::string & format)119*11be35a1SLionel Sambuc new_ostringstream(const std::string& format)
120*11be35a1SLionel Sambuc {
121*11be35a1SLionel Sambuc std::auto_ptr< std::ostringstream > output(new std::ostringstream());
122*11be35a1SLionel Sambuc
123*11be35a1SLionel Sambuc if (format.length() <= 2) {
124*11be35a1SLionel Sambuc // If the format is empty, we create a new stream so that we don't have
125*11be35a1SLionel Sambuc // to check for NULLs later on. We rarely should hit this condition
126*11be35a1SLionel Sambuc // (and when we do it's a bug in the caller), so this is not a big deal.
127*11be35a1SLionel Sambuc //
128*11be35a1SLionel Sambuc // Otherwise, if the format is a regular '%s', then we don't have to do
129*11be35a1SLionel Sambuc // any processing for additional formatters. So this is just a "fast
130*11be35a1SLionel Sambuc // path".
131*11be35a1SLionel Sambuc } else {
132*11be35a1SLionel Sambuc std::string partial = format.substr(1, format.length() - 2);
133*11be35a1SLionel Sambuc if (partial[0] == '0') {
134*11be35a1SLionel Sambuc output->fill('0');
135*11be35a1SLionel Sambuc partial.erase(0, 1);
136*11be35a1SLionel Sambuc }
137*11be35a1SLionel Sambuc if (!partial.empty()) {
138*11be35a1SLionel Sambuc const std::string::size_type dot = partial.find('.');
139*11be35a1SLionel Sambuc if (dot != 0)
140*11be35a1SLionel Sambuc output->width(to_int(format, partial.substr(0, dot), "width"));
141*11be35a1SLionel Sambuc if (dot != std::string::npos) {
142*11be35a1SLionel Sambuc output->setf(std::ios::fixed, std::ios::floatfield);
143*11be35a1SLionel Sambuc output->precision(to_int(format, partial.substr(dot + 1),
144*11be35a1SLionel Sambuc "precision"));
145*11be35a1SLionel Sambuc }
146*11be35a1SLionel Sambuc }
147*11be35a1SLionel Sambuc }
148*11be35a1SLionel Sambuc
149*11be35a1SLionel Sambuc return output.release();
150*11be35a1SLionel Sambuc }
151*11be35a1SLionel Sambuc
152*11be35a1SLionel Sambuc
153*11be35a1SLionel Sambuc /// Replaces '%%' by '%' in a given string range.
154*11be35a1SLionel Sambuc ///
155*11be35a1SLionel Sambuc /// \param in The input string to be rewritten.
156*11be35a1SLionel Sambuc /// \param begin The position at which to start the replacement.
157*11be35a1SLionel Sambuc /// \param end The position at which to end the replacement.
158*11be35a1SLionel Sambuc ///
159*11be35a1SLionel Sambuc /// \return The modified string and the amount of characters removed.
160*11be35a1SLionel Sambuc static std::pair< std::string, int >
strip_double_percent(const std::string & in,const std::string::size_type begin,std::string::size_type end)161*11be35a1SLionel Sambuc strip_double_percent(const std::string& in, const std::string::size_type begin,
162*11be35a1SLionel Sambuc std::string::size_type end)
163*11be35a1SLionel Sambuc {
164*11be35a1SLionel Sambuc std::string part = in.substr(begin, end - begin);
165*11be35a1SLionel Sambuc
166*11be35a1SLionel Sambuc int removed = 0;
167*11be35a1SLionel Sambuc std::string::size_type pos = part.find("%%");
168*11be35a1SLionel Sambuc while (pos != std::string::npos) {
169*11be35a1SLionel Sambuc part.erase(pos, 1);
170*11be35a1SLionel Sambuc ++removed;
171*11be35a1SLionel Sambuc pos = part.find("%%", pos + 1);
172*11be35a1SLionel Sambuc }
173*11be35a1SLionel Sambuc
174*11be35a1SLionel Sambuc return std::make_pair(in.substr(0, begin) + part + in.substr(end), removed);
175*11be35a1SLionel Sambuc }
176*11be35a1SLionel Sambuc
177*11be35a1SLionel Sambuc
178*11be35a1SLionel Sambuc } // anonymous namespace
179*11be35a1SLionel Sambuc
180*11be35a1SLionel Sambuc
181*11be35a1SLionel Sambuc /// Performs internal initialization of the formatter.
182*11be35a1SLionel Sambuc ///
183*11be35a1SLionel Sambuc /// This is separate from the constructor just because it is shared by different
184*11be35a1SLionel Sambuc /// overloaded constructors.
185*11be35a1SLionel Sambuc void
init(void)186*11be35a1SLionel Sambuc format::formatter::init(void)
187*11be35a1SLionel Sambuc {
188*11be35a1SLionel Sambuc const std::pair< std::string::size_type, std::string > placeholder =
189*11be35a1SLionel Sambuc find_next_placeholder(_format, _expansion, _last_pos);
190*11be35a1SLionel Sambuc const std::pair< std::string, int > no_percents =
191*11be35a1SLionel Sambuc strip_double_percent(_expansion, _last_pos, placeholder.first);
192*11be35a1SLionel Sambuc
193*11be35a1SLionel Sambuc _oss = new_ostringstream(placeholder.second);
194*11be35a1SLionel Sambuc
195*11be35a1SLionel Sambuc _expansion = no_percents.first;
196*11be35a1SLionel Sambuc _placeholder_pos = placeholder.first - no_percents.second;
197*11be35a1SLionel Sambuc _placeholder = placeholder.second;
198*11be35a1SLionel Sambuc }
199*11be35a1SLionel Sambuc
200*11be35a1SLionel Sambuc
201*11be35a1SLionel Sambuc /// Constructs a new formatter object (internal).
202*11be35a1SLionel Sambuc ///
203*11be35a1SLionel Sambuc /// \param format The format string.
204*11be35a1SLionel Sambuc /// \param expansion The format string with any replacements performed so far.
205*11be35a1SLionel Sambuc /// \param last_pos The position from which to start looking for formatting
206*11be35a1SLionel Sambuc /// placeholders. This must be maintained in case one of the replacements
207*11be35a1SLionel Sambuc /// introduced a new placeholder, which must be ignored. Think, for
208*11be35a1SLionel Sambuc /// example, replacing a "%s" string with "foo %s".
formatter(const std::string & format,const std::string & expansion,const std::string::size_type last_pos)209*11be35a1SLionel Sambuc format::formatter::formatter(const std::string& format,
210*11be35a1SLionel Sambuc const std::string& expansion,
211*11be35a1SLionel Sambuc const std::string::size_type last_pos) :
212*11be35a1SLionel Sambuc _format(format),
213*11be35a1SLionel Sambuc _expansion(expansion),
214*11be35a1SLionel Sambuc _last_pos(last_pos),
215*11be35a1SLionel Sambuc _oss(NULL)
216*11be35a1SLionel Sambuc {
217*11be35a1SLionel Sambuc init();
218*11be35a1SLionel Sambuc }
219*11be35a1SLionel Sambuc
220*11be35a1SLionel Sambuc
221*11be35a1SLionel Sambuc /// Constructs a new formatter object.
222*11be35a1SLionel Sambuc ///
223*11be35a1SLionel Sambuc /// \param format The format string. The formatters in the string are not
224*11be35a1SLionel Sambuc /// validated during construction, but will cause errors when used later if
225*11be35a1SLionel Sambuc /// they are invalid.
formatter(const std::string & format)226*11be35a1SLionel Sambuc format::formatter::formatter(const std::string& format) :
227*11be35a1SLionel Sambuc _format(format),
228*11be35a1SLionel Sambuc _expansion(format),
229*11be35a1SLionel Sambuc _last_pos(0),
230*11be35a1SLionel Sambuc _oss(NULL)
231*11be35a1SLionel Sambuc {
232*11be35a1SLionel Sambuc init();
233*11be35a1SLionel Sambuc }
234*11be35a1SLionel Sambuc
235*11be35a1SLionel Sambuc
~formatter(void)236*11be35a1SLionel Sambuc format::formatter::~formatter(void)
237*11be35a1SLionel Sambuc {
238*11be35a1SLionel Sambuc delete _oss;
239*11be35a1SLionel Sambuc }
240*11be35a1SLionel Sambuc
241*11be35a1SLionel Sambuc
242*11be35a1SLionel Sambuc /// Returns the formatted string.
243*11be35a1SLionel Sambuc const std::string&
str(void) const244*11be35a1SLionel Sambuc format::formatter::str(void) const
245*11be35a1SLionel Sambuc {
246*11be35a1SLionel Sambuc return _expansion;
247*11be35a1SLionel Sambuc }
248*11be35a1SLionel Sambuc
249*11be35a1SLionel Sambuc
250*11be35a1SLionel Sambuc /// Automatic conversion of formatter objects to strings.
251*11be35a1SLionel Sambuc ///
252*11be35a1SLionel Sambuc /// This is provided to allow painless injection of formatter objects into
253*11be35a1SLionel Sambuc /// streams, without having to manually call the str() method.
operator const std::string&(void) const254*11be35a1SLionel Sambuc format::formatter::operator const std::string&(void) const
255*11be35a1SLionel Sambuc {
256*11be35a1SLionel Sambuc return _expansion;
257*11be35a1SLionel Sambuc }
258*11be35a1SLionel Sambuc
259*11be35a1SLionel Sambuc
260*11be35a1SLionel Sambuc /// Specialization of operator% for booleans.
261*11be35a1SLionel Sambuc ///
262*11be35a1SLionel Sambuc /// \param value The boolean to inject into the format string.
263*11be35a1SLionel Sambuc ///
264*11be35a1SLionel Sambuc /// \return A new formatter that has one less format placeholder.
265*11be35a1SLionel Sambuc format::formatter
operator %(const bool & value) const266*11be35a1SLionel Sambuc format::formatter::operator%(const bool& value) const
267*11be35a1SLionel Sambuc {
268*11be35a1SLionel Sambuc (*_oss) << (value ? "true" : "false");
269*11be35a1SLionel Sambuc return replace(_oss->str());
270*11be35a1SLionel Sambuc }
271*11be35a1SLionel Sambuc
272*11be35a1SLionel Sambuc
273*11be35a1SLionel Sambuc /// Replaces the first formatting placeholder with a value.
274*11be35a1SLionel Sambuc ///
275*11be35a1SLionel Sambuc /// \param arg The replacement string.
276*11be35a1SLionel Sambuc ///
277*11be35a1SLionel Sambuc /// \return A new formatter in which the first formatting placeholder has been
278*11be35a1SLionel Sambuc /// replaced by arg and is ready to replace the next item.
279*11be35a1SLionel Sambuc ///
280*11be35a1SLionel Sambuc /// \throw utils::format::extra_args_error If there are no more formatting
281*11be35a1SLionel Sambuc /// placeholders in the input string, or if the placeholder is invalid.
282*11be35a1SLionel Sambuc format::formatter
replace(const std::string & arg) const283*11be35a1SLionel Sambuc format::formatter::replace(const std::string& arg) const
284*11be35a1SLionel Sambuc {
285*11be35a1SLionel Sambuc if (_placeholder_pos == _expansion.length())
286*11be35a1SLionel Sambuc throw format::extra_args_error(_format, arg);
287*11be35a1SLionel Sambuc
288*11be35a1SLionel Sambuc const std::string expansion = _expansion.substr(0, _placeholder_pos)
289*11be35a1SLionel Sambuc + arg + _expansion.substr(_placeholder_pos + _placeholder.length());
290*11be35a1SLionel Sambuc return formatter(_format, expansion, _placeholder_pos + arg.length());
291*11be35a1SLionel Sambuc }
292