1 // Copyright (c) 2014-2020 Thomas Fussell
2 // Copyright (c) 2010-2015 openpyxl
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE
21 //
22 // @license: http://www.opensource.org/licenses/mit-license.php
23 // @author: see AUTHORS file
24 
25 #include <algorithm>
26 #include <cctype>
27 #include <unordered_map>
28 #include <vector>
29 
30 #include <xlnt/styles/number_format.hpp>
31 #include <xlnt/utils/datetime.hpp>
32 #include <xlnt/utils/exceptions.hpp>
33 #include <detail/number_format/number_formatter.hpp>
34 
35 namespace {
36 
builtin_formats()37 const std::unordered_map<std::size_t, xlnt::number_format> &builtin_formats()
38 {
39     static std::unordered_map<std::size_t, xlnt::number_format> formats;
40 
41     if (formats.size() == 0)
42     {
43         const std::unordered_map<std::size_t, std::string> format_strings{
44             {0, "General"},
45             {1, "0"},
46             {2, "0.00"},
47             {3, "#,##0"},
48             {4, "#,##0.00"},
49             {9, "0%"},
50             {10, "0.00%"},
51             {11, "0.00E+00"},
52             {12, "# ?/?"},
53             {13, "# \?\?/??"}, // escape trigraph
54             {14, "mm-dd-yy"},
55             {15, "d-mmm-yy"},
56             {16, "d-mmm"},
57             {17, "mmm-yy"},
58             {18, "h:mm AM/PM"},
59             {19, "h:mm:ss AM/PM"},
60             {20, "h:mm"},
61             {21, "h:mm:ss"},
62             {22, "m/d/yy h:mm"},
63             {37, "#,##0 ;(#,##0)"},
64             {38, "#,##0 ;[Red](#,##0)"},
65             {39, "#,##0.00;(#,##0.00)"},
66             {40, "#,##0.00;[Red](#,##0.00)"},
67 
68             // 41-44 aren't in the ECMA 376 v4 standard, but Libre Office uses them
69             {41, "_(* #,##0_);_(* \\(#,##0\\);_(* \"-\"_);_(@_)"},
70             {42, "_(\"$\"* #,##0_);_(\"$\"* \\(#,##0\\);_(\"$\"* \"-\"_);_(@_)"},
71             {43, "_(* #,##0.00_);_(* \\(#,##0.00\\);_(* \"-\"??_);_(@_)"},
72             {44, "_(\"$\"* #,##0.00_)_(\"$\"* \\(#,##0.00\\)_(\"$\"* \"-\"??_)_(@_)"},
73 
74             {45, "mm:ss"},
75             {46, "[h]:mm:ss"},
76             {47, "mmss.0"},
77             {48, "##0.0E+0"},
78             {49, "@"}};
79 
80         for (auto format_string_pair : format_strings)
81         {
82             formats[format_string_pair.first] =
83                 xlnt::number_format(format_string_pair.second, format_string_pair.first);
84         }
85     }
86 
87     return formats;
88 }
89 
90 } // namespace
91 
92 namespace xlnt {
93 
general()94 const number_format number_format::general()
95 {
96     return builtin_formats().at(0);
97 }
98 
text()99 const number_format number_format::text()
100 {
101     return builtin_formats().at(49);
102 }
103 
number()104 const number_format number_format::number()
105 {
106     return builtin_formats().at(1);
107 }
108 
number_00()109 const number_format number_format::number_00()
110 {
111     return builtin_formats().at(2);
112 }
113 
number_comma_separated1()114 const number_format number_format::number_comma_separated1()
115 {
116     return builtin_formats().at(4);
117 }
118 
percentage()119 const number_format number_format::percentage()
120 {
121     return builtin_formats().at(9);
122 }
123 
percentage_00()124 const number_format number_format::percentage_00()
125 {
126     return builtin_formats().at(10);
127 }
128 
date_yyyymmdd2()129 const number_format number_format::date_yyyymmdd2()
130 {
131     static const number_format format = number_format("yyyy-mm-dd");
132     return format;
133 }
134 
date_yymmdd()135 const number_format number_format::date_yymmdd()
136 {
137     static const number_format format = number_format("yy-mm-dd");
138     return format;
139 }
140 
date_ddmmyyyy()141 const number_format number_format::date_ddmmyyyy()
142 {
143     static const number_format format = number_format("dd/mm/yy");
144     return format;
145 }
146 
date_dmyslash()147 const number_format number_format::date_dmyslash()
148 {
149     static const number_format format = number_format("d/m/yy");
150     return format;
151 }
152 
date_dmyminus()153 const number_format number_format::date_dmyminus()
154 {
155     static const number_format format = number_format("d-m-yy");
156     return format;
157 }
158 
date_dmminus()159 const number_format number_format::date_dmminus()
160 {
161     static const number_format format = number_format("d-m");
162     return format;
163 }
164 
date_myminus()165 const number_format number_format::date_myminus()
166 {
167     static const number_format format = number_format("m-yy");
168     return format;
169 }
170 
date_xlsx14()171 const number_format number_format::date_xlsx14()
172 {
173     return builtin_formats().at(14);
174 }
175 
date_xlsx15()176 const number_format number_format::date_xlsx15()
177 {
178     return builtin_formats().at(15);
179 }
180 
date_xlsx16()181 const number_format number_format::date_xlsx16()
182 {
183     return builtin_formats().at(16);
184 }
185 
date_xlsx17()186 const number_format number_format::date_xlsx17()
187 {
188     return builtin_formats().at(17);
189 }
190 
date_xlsx22()191 const number_format number_format::date_xlsx22()
192 {
193     return builtin_formats().at(22);
194 }
195 
date_datetime()196 const number_format number_format::date_datetime()
197 {
198     static const number_format format = number_format("yyyy-mm-dd h:mm:ss");
199     return format;
200 }
201 
date_time1()202 const number_format number_format::date_time1()
203 {
204     return builtin_formats().at(18);
205 }
206 
date_time2()207 const number_format number_format::date_time2()
208 {
209     return builtin_formats().at(19);
210 }
211 
date_time3()212 const number_format number_format::date_time3()
213 {
214     return builtin_formats().at(20);
215 }
216 
date_time4()217 const number_format number_format::date_time4()
218 {
219     return builtin_formats().at(21);
220 }
221 
date_time5()222 const number_format number_format::date_time5()
223 {
224     return builtin_formats().at(45);
225 }
226 
date_time6()227 const number_format number_format::date_time6()
228 {
229     return builtin_formats().at(21);
230 }
231 
number_format()232 number_format::number_format()
233     : number_format("General", 0)
234 {
235 }
236 
number_format(std::size_t id)237 number_format::number_format(std::size_t id)
238     : number_format(from_builtin_id(id))
239 {
240 }
241 
number_format(const std::string & format_string)242 number_format::number_format(const std::string &format_string)
243     : format_string_(format_string)
244 {
245 }
246 
number_format(const std::string & format,std::size_t id)247 number_format::number_format(const std::string &format, std::size_t id)
248 {
249     format_string(format, id);
250 }
251 
is_builtin_format(std::size_t builtin_id)252 bool number_format::is_builtin_format(std::size_t builtin_id)
253 {
254     return builtin_formats().find(builtin_id) != builtin_formats().end();
255 }
256 
from_builtin_id(std::size_t builtin_id)257 const number_format &number_format::from_builtin_id(std::size_t builtin_id)
258 {
259     if (!is_builtin_format(builtin_id))
260     {
261         throw invalid_parameter();
262     }
263 
264     return builtin_formats().at(builtin_id);
265 }
266 
format_string() const267 std::string number_format::format_string() const
268 {
269     return format_string_;
270 }
271 
format_string(const std::string & format_string)272 void number_format::format_string(const std::string &format_string)
273 {
274     format_string_ = format_string;
275     id_ = 0;
276 
277     for (const auto &pair : builtin_formats())
278     {
279         if (pair.second.format_string() == format_string)
280         {
281             id_ = pair.first;
282             break;
283         }
284     }
285 }
286 
format_string(const std::string & format_string,std::size_t id)287 void number_format::format_string(const std::string &format_string, std::size_t id)
288 {
289     format_string_ = format_string;
290     id_ = id;
291 }
292 
has_id() const293 bool number_format::has_id() const
294 {
295     return id_.is_set();
296 }
297 
id(std::size_t id)298 void number_format::id(std::size_t id)
299 {
300     id_ = id;
301 }
302 
id() const303 std::size_t number_format::id() const
304 {
305     if (!has_id())
306     {
307         throw invalid_attribute();
308     }
309 
310     return id_.get();
311 }
312 
is_date_format() const313 bool number_format::is_date_format() const
314 {
315     detail::number_format_parser p(format_string_);
316     p.parse();
317     auto parsed = p.result();
318 
319     bool any_datetime = false;
320     bool any_timedelta = false;
321 
322     for (const auto &section : parsed)
323     {
324         if (section.is_datetime)
325         {
326             any_datetime = true;
327         }
328 
329         if (section.is_timedelta)
330         {
331             any_timedelta = true;
332         }
333     }
334 
335     return any_datetime && !any_timedelta;
336 }
337 
format(const std::string & text) const338 std::string number_format::format(const std::string &text) const
339 {
340     return detail::number_formatter(format_string_, calendar::windows_1900).format_text(text);
341 }
342 
format(double number,calendar base_date) const343 std::string number_format::format(double number, calendar base_date) const
344 {
345     return detail::number_formatter(format_string_, base_date).format_number(number);
346 }
347 
operator ==(const number_format & other) const348 bool number_format::operator==(const number_format &other) const
349 {
350     return format_string_ == other.format_string_;
351 }
352 
operator !=(const number_format & other) const353 bool number_format::operator!=(const number_format &other) const
354 {
355     return !(*this == other);
356 }
357 
358 } // namespace xlnt
359