xlnt/source/detail/number_formatter.hpp
2017-01-02 20:35:18 -05:00

379 lines
8.9 KiB
C++

// Copyright (c) 2014-2017 Thomas Fussell
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE
//
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file
#pragma once
#include <string>
#include <unordered_map>
#include <vector>
#include <xlnt/utils/datetime.hpp>
class test_number_format;
namespace xlnt {
namespace detail {
enum class format_color
{
black,
blue,
cyan,
green,
magenta,
red,
white,
yellow,
color1,
color2,
color3,
color4,
color5,
color6,
color7,
color8,
color9,
color10,
color11,
color12,
color13,
color14,
color15,
color16,
color17,
color18,
color19,
color20,
color21,
color22,
color23,
color24,
color25,
color26,
color27,
color28,
color29,
color30,
color31,
color32,
color33,
color34,
color35,
color36,
color37,
color38,
color39,
color40,
color41,
color42,
color43,
color44,
color45,
color46,
color47,
color48,
color49,
color50,
color51,
color52,
color53,
color54,
color55,
color56
};
enum class format_locale
{
arabic_saudi_arabia = 0x401,
bulgarian = 0x402,
catalan = 0x403,
chinese_taiwan = 0x404,
czech = 0x405,
danish = 0x406,
german_germany = 0x407,
greek = 0x408,
english_united_states = 0x409,
italian_italy = 0x410,
japanese = 0x411,
korean = 0x412,
dutch_netherlands = 0x413,
norwegian_bokml = 0x414,
polish = 0x415,
portuguese_brazil = 0x416,
raeto_romance = 0x417,
romanian_romania = 0x418,
russian = 0x419,
urdu = 0x420,
indonesian = 0x421,
ukrainian = 0x422,
belarusian = 0x423,
slovenian = 0x424,
estonian = 0x425,
latvian = 0x426,
lithuanian = 0x427,
tajik = 0x428,
farsi_persian = 0x429,
sesotho_sutu = 0x430,
tsonga = 0x431,
setsuana = 0x432,
venda = 0x433,
xhosa = 0x434,
zulu = 0x435,
afrikaans = 0x436,
georgian = 0x437,
faroese = 0x438,
hindi = 0x439,
kyrgyz_cyrillic = 0x440,
swahili = 0x441,
turkmen = 0x442,
uzbek_latin = 0x443,
tatar = 0x444,
bengali_india = 0x445,
punjabi = 0x446,
gujarati = 0x447,
oriya = 0x448,
tamil = 0x449,
mongolian = 0x450,
tibetan = 0x451,
welsh = 0x452,
khmer = 0x453,
lao = 0x454,
burmese = 0x455,
galician = 0x456,
konkani = 0x457,
manipuri = 0x458,
sindhi = 0x459,
kashmiri = 0x460,
nepali = 0x461,
frisian_netherlands = 0x462,
filipino = 0x464,
divehi_dhivehi_maldivian = 0x465,
edo = 0x466,
igbo_nigeria = 0x470,
guarani_paraguay = 0x474,
latin = 0x476,
somali = 0x477,
maori = 0x481,
arabic_iraq = 0x801,
chinese_china = 0x804,
german_switzerland = 0x807,
english_great_britain = 0x809,
italian_switzerland = 0x810,
dutch_belgium = 0x813,
norwegian_nynorsk = 0x814,
portuguese_portugal = 0x816,
romanian_moldova = 0x818,
russian_moldova = 0x819,
uzbek_cyrillic = 0x843,
bengali_bangladesh = 0x845,
mongolian2 = 0x850,
arabic_libya = 0x1001,
chinese_singapore = 0x1004,
german_luxembourg = 0x1007,
english_canada = 0x1009,
arabic_algeria = 0x1401,
chinese_macau_sar = 0x1404,
german_liechtenstein = 0x1407,
english_new_zealand = 0x1409,
arabic_morocco = 0x1801,
english_ireland = 0x1809,
arabic_oman = 0x2001,
english_jamaica = 0x2009,
arabic_yemen = 0x2401,
english_caribbean = 0x2409,
arabic_syria = 0x2801,
english_belize = 0x2809,
arabic_lebanon = 0x3001,
english_zimbabwe = 0x3009,
arabic_kuwait = 0x3401,
english_phillippines = 0x3409,
arabic_united_arab_emirates = 0x3801,
arabic_qatar = 0x4001
};
// TODO this really shouldn't be exported...
struct XLNT_API format_condition
{
enum class condition_type
{
less_than,
less_or_equal,
equal,
not_equal,
greater_than,
greater_or_equal
} type = condition_type::not_equal;
long double value = 0;
bool satisfied_by(long double number) const;
};
struct format_placeholders
{
enum class placeholders_type
{
general,
text,
integer_only,
integer_part,
fractional_part,
fraction_integer,
fraction_numerator,
fraction_denominator,
scientific_significand,
scientific_exponent_plus,
scientific_exponent_minus
} type = placeholders_type::general;
bool use_comma_separator = false;
bool percentage = false;
bool scientific = false;
std::size_t num_zeros = 0; // 0
std::size_t num_optionals = 0; // #
std::size_t num_spaces = 0; // ?
std::size_t thousands_scale = 0;
};
struct number_format_token
{
enum class token_type
{
color,
locale,
condition,
text,
fill,
space,
number,
datetime,
end_section,
end
} type = token_type::end;
std::string string;
};
struct template_part
{
enum class template_type
{
text,
fill,
space,
general,
month_number,
month_number_leading_zero,
month_abbreviation,
month_name,
month_letter,
day_number,
day_number_leading_zero,
day_abbreviation,
day_name,
year_short,
year_long,
hour,
hour_leading_zero,
minute,
minute_leading_zero,
second,
second_fractional,
second_leading_zero,
second_leading_zero_fractional,
am_pm,
a_p,
elapsed_hours,
elapsed_minutes,
elapsed_seconds
} type = template_type::general;
std::string string;
format_placeholders placeholders;
};
struct format_code
{
bool has_color = false;
format_color color = format_color::black;
bool has_locale = false;
format_locale locale = format_locale::english_united_states;
bool has_condition = false;
format_condition condition;
bool is_datetime = false;
bool is_timedelta = false;
bool twelve_hour = false;
std::vector<template_part> parts;
};
class number_format_parser
{
public:
number_format_parser(const std::string &format_string);
const std::vector<format_code> &result() const;
void reset(const std::string &format_string);
void parse();
private:
void finalize();
void validate();
number_format_token parse_next_token();
format_placeholders parse_placeholders(const std::string &placeholders_string);
format_color color_from_string(const std::string &color);
std::pair<format_locale, std::string> locale_from_string(const std::string &locale_string);
std::size_t position_ = 0;
std::string format_string_;
std::vector<format_code> codes_;
};
class XLNT_API number_formatter
{
public:
number_formatter(const std::string &format_string, xlnt::calendar calendar);
std::string format_number(long double number);
std::string format_text(const std::string &text);
private:
friend class ::test_number_format;
std::string fill_placeholders(const format_placeholders &p, long double number);
std::string fill_fraction_placeholders(const format_placeholders &numerator,
const format_placeholders &denominator, long double number, bool improper);
std::string fill_scientific_placeholders(const format_placeholders &integer_part,
const format_placeholders &fractional_part, const format_placeholders &exponent_part,
long double number);
std::string format_number(const format_code &format, long double number);
std::string format_text(const format_code &format, const std::string &text);
number_format_parser parser_;
std::vector<format_code> format_;
xlnt::calendar calendar_;
};
} // namespace detail
} // namespace xlnt