test number_formatter and improve exceptions

This commit is contained in:
Thomas Fussell 2016-07-30 11:58:36 -04:00
parent 5bd1a79536
commit cd3c0c5f0b
5 changed files with 85 additions and 86 deletions

View File

@ -63,15 +63,6 @@ public:
invalid_sheet_title(const std::string &title); invalid_sheet_title(const std::string &title);
}; };
/// <summary>
/// Exception for incorrectly formatted named ranges.
/// </summary>
class XLNT_CLASS invalid_named_range : public exception
{
public:
invalid_named_range();
};
/// <summary> /// <summary>
/// Exception when a referenced number format is not in the stylesheet. /// Exception when a referenced number format is not in the stylesheet.
/// </summary> /// </summary>

View File

@ -160,8 +160,6 @@ bool format_condition::satisfied_by(long double number) const
return number < value; return number < value;
case condition_type::not_equal: case condition_type::not_equal:
return number != value; return number != value;
default:
return false;
} }
} }
@ -199,24 +197,26 @@ void number_format_parser::parse()
break; break;
case number_format_token::token_type::color: case number_format_token::token_type::color:
if (section.color != format_color::none if (section.has_color
|| section.condition.type != format_condition::condition_type::none || section.has_condition
|| section.locale != format_locale::none || section.has_locale
|| !section.parts.empty()) || !section.parts.empty())
{ {
throw std::runtime_error("color should be the first part of a format"); throw std::runtime_error("color should be the first part of a format");
} }
section.has_color = true;
section.color = color_from_string(token.string); section.color = color_from_string(token.string);
break; break;
case number_format_token::token_type::locale: case number_format_token::token_type::locale:
{ {
if (section.locale != format_locale::none) if (section.has_locale)
{ {
throw std::runtime_error("multiple locales"); throw std::runtime_error("multiple locales");
} }
section.has_locale = true;
auto parsed_locale = locale_from_string(token.string); auto parsed_locale = locale_from_string(token.string);
section.locale = parsed_locale.first; section.locale = parsed_locale.first;
@ -233,11 +233,12 @@ void number_format_parser::parse()
case number_format_token::token_type::condition: case number_format_token::token_type::condition:
{ {
if (section.condition.type != format_condition::condition_type::none) if (section.has_condition)
{ {
throw std::runtime_error("multiple conditions"); throw std::runtime_error("multiple conditions");
} }
section.has_condition = true;
std::string value; std::string value;
if (token.string.front() == '<') if (token.string.front() == '<')
@ -439,9 +440,6 @@ void number_format_parser::parse()
finalize(); finalize();
return; return;
default:
break;
} }
token = parse_next_token(); token = parse_next_token();
@ -524,6 +522,7 @@ void number_format_parser::finalize()
fractional_seconds = true; fractional_seconds = true;
} }
//TODO this block needs improvement
if (part.type == template_part::template_type::month_number if (part.type == template_part::template_type::month_number
|| part.type == template_part::template_type::month_number_leading_zero) || part.type == template_part::template_type::month_number_leading_zero)
{ {
@ -552,8 +551,8 @@ void number_format_parser::finalize()
if (previous.type == template_part::template_type::text if (previous.type == template_part::template_type::text
&& previous.string == ":" && previous.string == ":"
&& (before_previous.type == template_part::template_type::hour || && (before_previous.type == template_part::template_type::hour_leading_zero
before_previous.type == template_part::template_type::hour_leading_zero)) || before_previous.type == template_part::template_type::hour))
{ {
fix = true; fix = true;
leading_zero = part.type == template_part::template_type::month_number_leading_zero; leading_zero = part.type == template_part::template_type::month_number_leading_zero;
@ -848,9 +847,9 @@ void number_format_parser::validate()
if (codes_.size() > 2) if (codes_.size() > 2)
{ {
if (codes_[0].condition.type != format_condition::condition_type::none && if (codes_[0].has_condition
codes_[1].condition.type != format_condition::condition_type::none && && codes_[1].has_condition
codes_[2].condition.type != format_condition::condition_type::none) && codes_[2].has_condition)
{ {
throw std::runtime_error("format should have a maximum of two codes with conditions"); throw std::runtime_error("format should have a maximum of two codes with conditions");
} }
@ -1045,7 +1044,7 @@ number_formatter::number_formatter(const std::string &format_string, xlnt::calen
std::string number_formatter::format_number(long double number) std::string number_formatter::format_number(long double number)
{ {
if (format_[0].condition.type != format_condition::condition_type::none) if (format_[0].has_condition)
{ {
if (format_[0].condition.satisfied_by(number)) if (format_[0].condition.satisfied_by(number))
{ {
@ -1057,7 +1056,7 @@ std::string number_formatter::format_number(long double number)
return std::string(11, '#'); return std::string(11, '#');
} }
if (format_[1].condition.type == format_condition::condition_type::none if (!format_[1].has_condition
|| format_[1].condition.satisfied_by(number)) || format_[1].condition.satisfied_by(number))
{ {
return format_number(format_[1], number); return format_number(format_[1], number);
@ -1125,10 +1124,12 @@ std::string number_formatter::format_text(const std::string &text)
std::string number_formatter::fill_placeholders(const format_placeholders &p, long double number) std::string number_formatter::fill_placeholders(const format_placeholders &p, long double number)
{ {
std::string result;
if (p.type == format_placeholders::placeholders_type::general if (p.type == format_placeholders::placeholders_type::general
|| p.type == format_placeholders::placeholders_type::text) || p.type == format_placeholders::placeholders_type::text)
{ {
auto result = std::to_string(number); result = std::to_string(number);
while (result.back() == '0') while (result.back() == '0')
{ {
@ -1155,13 +1156,11 @@ std::string number_formatter::fill_placeholders(const format_placeholders &p, lo
auto integer_part = static_cast<int>(number); auto integer_part = static_cast<int>(number);
switch (p.type) if (p.type == format_placeholders::placeholders_type::integer_only
|| p.type == format_placeholders::placeholders_type::integer_part
|| p.type == format_placeholders::placeholders_type::fraction_integer)
{ {
case format_placeholders::placeholders_type::integer_only: result = std::to_string(integer_part);
case format_placeholders::placeholders_type::integer_part:
case format_placeholders::placeholders_type::fraction_integer:
{
auto result = std::to_string(integer_part);
while (result.size() < p.num_zeros) while (result.size() < p.num_zeros)
{ {
@ -1195,16 +1194,13 @@ std::string number_formatter::fill_placeholders(const format_placeholders &p, lo
{ {
result.push_back('%'); result.push_back('%');
} }
return result;
} }
else if (p.type == format_placeholders::placeholders_type::fractional_part)
case format_placeholders::placeholders_type::fractional_part:
{ {
auto fractional_part = number - integer_part; auto fractional_part = number - integer_part;
auto result = fractional_part == 0 ? std::string(".") : std::to_string(fractional_part).substr(1); result = fractional_part == 0 ? std::string(".") : std::to_string(fractional_part).substr(1);
while (result.back() == '0' || result.size() > (p.num_zeros + p.num_optionals + 1)) while (result.back() == '0' || result.size() > (p.num_zeros + p.num_optionals + p.num_spaces + 1))
{ {
result.pop_back(); result.pop_back();
} }
@ -1214,7 +1210,7 @@ std::string number_formatter::fill_placeholders(const format_placeholders &p, lo
result.push_back('0'); result.push_back('0');
} }
while (result.size() < p.num_zeros + p.num_spaces + 1) while (result.size() < p.num_zeros + p.num_optionals + p.num_spaces + 1)
{ {
result.push_back(' '); result.push_back(' ');
} }
@ -1223,13 +1219,9 @@ std::string number_formatter::fill_placeholders(const format_placeholders &p, lo
{ {
result.push_back('%'); result.push_back('%');
} }
return result;
} }
default: return result;
return "";
}
} }
std::string number_formatter::fill_scientific_placeholders(const format_placeholders &integer_part, std::string number_formatter::fill_scientific_placeholders(const format_placeholders &integer_part,
@ -1591,9 +1583,6 @@ std::string number_formatter::format_number(const format_code &format, long doub
case template_part::template_type::day_name: case template_part::template_type::day_name:
result.append(day_names->at(dt.weekday() - 1)); result.append(day_names->at(dt.weekday() - 1));
break; break;
case template_part::template_type::bad:
throw std::runtime_error("bad format");
} }
} }

View File

@ -36,7 +36,6 @@ namespace detail {
enum class format_color enum class format_color
{ {
none,
black, black,
blue, blue,
cyan, cyan,
@ -105,7 +104,6 @@ enum class format_color
enum class format_locale enum class format_locale
{ {
none = 0,
arabic_saudi_arabia = 0x401, arabic_saudi_arabia = 0x401,
bulgarian = 0x402, bulgarian = 0x402,
catalan = 0x403, catalan = 0x403,
@ -218,14 +216,13 @@ struct XLNT_CLASS format_condition
{ {
enum class condition_type enum class condition_type
{ {
none,
less_than, less_than,
less_or_equal, less_or_equal,
equal, equal,
not_equal, not_equal,
greater_than, greater_than,
greater_or_equal greater_or_equal
} type = condition_type::none; } type = condition_type::not_equal;
long double value = 0; long double value = 0;
@ -236,7 +233,6 @@ struct format_placeholders
{ {
enum class placeholders_type enum class placeholders_type
{ {
bad,
general, general,
text, text,
integer_only, integer_only,
@ -248,7 +244,7 @@ struct format_placeholders
scientific_significand, scientific_significand,
scientific_exponent_plus, scientific_exponent_plus,
scientific_exponent_minus scientific_exponent_minus
} type = placeholders_type::bad; } type = placeholders_type::general;
bool use_comma_separator = false; bool use_comma_separator = false;
bool percentage = false; bool percentage = false;
@ -264,7 +260,6 @@ struct number_format_token
{ {
enum class token_type enum class token_type
{ {
bad,
color, color,
locale, locale,
condition, condition,
@ -275,7 +270,7 @@ struct number_format_token
datetime, datetime,
end_section, end_section,
end end
} type = token_type::bad; } type = token_type::end;
std::string string; std::string string;
}; };
@ -284,7 +279,6 @@ struct template_part
{ {
enum class template_type enum class template_type
{ {
bad,
text, text,
fill, fill,
space, space,
@ -313,7 +307,7 @@ struct template_part
elapsed_hours, elapsed_hours,
elapsed_minutes, elapsed_minutes,
elapsed_seconds elapsed_seconds
} type = template_type::bad; } type = template_type::general;
std::string string; std::string string;
format_placeholders placeholders; format_placeholders placeholders;
@ -321,8 +315,11 @@ struct template_part
struct format_code struct format_code
{ {
format_color color = format_color::none; bool has_color = false;
format_locale locale = format_locale::none; 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; format_condition condition;
bool is_datetime = false; bool is_datetime = false;
bool is_timedelta = false; bool is_timedelta = false;

View File

@ -1,11 +1,10 @@
#pragma once #pragma once
#include <iostream> #include <iostream>
#include <pugixml.hpp>
#include <cxxtest/TestSuite.h> #include <cxxtest/TestSuite.h>
#include "pugixml.hpp"
#include <xlnt/xlnt.hpp> #include <xlnt/xlnt.hpp>
#include <detail/number_formatter.hpp>
class test_number_format : public CxxTest::TestSuite class test_number_format : public CxxTest::TestSuite
{ {
@ -375,16 +374,6 @@ public:
TS_ASSERT_EQUALS(formatted, "text"); TS_ASSERT_EQUALS(formatted, "text");
} }
void test_bad_part()
{
xlnt::detail::template_part bad_part;
xlnt::detail::format_code bad_code;
bad_code.parts.push_back(bad_part);
xlnt::detail::number_formatter formatter("", xlnt::calendar::windows_1900);
formatter.format_ = { bad_code };
TS_ASSERT_THROWS(formatter.format_number(1), std::runtime_error);
}
void test_conditional_format() void test_conditional_format()
{ {
xlnt::number_format nf; xlnt::number_format nf;
@ -487,7 +476,53 @@ public:
formatted = nf.format(6, xlnt::calendar::windows_1900); formatted = nf.format(6, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "a---------b"); TS_ASSERT_EQUALS(formatted, "a---------b");
} }
void test_placeholders_zero()
{
xlnt::number_format nf;
nf.set_format_string("00");
auto formatted = nf.format(6, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "06");
nf.set_format_string("00");
formatted = nf.format(63, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "63");
}
void test_placeholders_space()
{
xlnt::number_format nf;
nf.set_format_string("?0");
auto formatted = nf.format(6, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, " 6");
nf.set_format_string("?0");
formatted = nf.format(63, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "63");
nf.set_format_string("?0");
formatted = nf.format(637, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "637");
nf.set_format_string("0.?");
formatted = nf.format(6, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "6. ");
nf.set_format_string("0.0?");
formatted = nf.format(6.3, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "6.3 ");
formatted = nf.format(6.34, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "6.34");
}
void test_scientific()
{
xlnt::number_format nf;
nf.set_format_string("0E-0");
auto formatted = nf.format(6.1, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "6.1E0");
}
void test_locale_currency() void test_locale_currency()
{ {
xlnt::number_format nf; xlnt::number_format nf;
@ -627,14 +662,6 @@ public:
TS_ASSERT_THROWS(nf.format(1.2, xlnt::calendar::windows_1900), std::runtime_error); TS_ASSERT_THROWS(nf.format(1.2, xlnt::calendar::windows_1900), std::runtime_error);
} }
void test_none_condition()
{
xlnt::detail::format_condition f;
f.type = xlnt::detail::format_condition::condition_type::none;
f.value = 3;
TS_ASSERT(!f.satisfied_by(3));
}
void format_and_test(const xlnt::number_format &nf, const std::array<std::string, 4> &expect) void format_and_test(const xlnt::number_format &nf, const std::array<std::string, 4> &expect)
{ {
long double positive = 42503.1234; long double positive = 42503.1234;

View File

@ -52,11 +52,6 @@ invalid_data_type::invalid_data_type()
{ {
} }
invalid_named_range::invalid_named_range()
: exception("named range not found or not owned by this worksheet")
{
}
invalid_file::invalid_file(const std::string &filename) invalid_file::invalid_file(const std::string &filename)
: exception(std::string("couldn't open file: (") + filename + ")") : exception(std::string("couldn't open file: (") + filename + ")")
{ {